下面是一个案例需求:
1、编写一段程序,该程序的主要功能是监控电路板上的电压值,若电压值超过当前的电压限制则通过蜂鸣器报警,通过按键解除报警;
2、其具体要求如下;
a) 程序下载20s后,进入电压采集状态(使用RTC ALARM功能完成), 要求1s采集1次电路板电压值;(采用RTC TIME TICK完成)
b) 每次电压采集完成后,通过COM2将采集到的电压值发送到PC,在PC端可通过串口调试助手查看当前的电压值;
c) 每次电压采集完成后,比较当前的电压值是否正常正常的电压值为(1V~2V),若当前采集的电压值异常,则通过蜂鸣器发出报警信号;
d) 报警信号的解除通过电路板上的KEY2控制( 通过按下KEY2使蜂鸣器停止鸣叫);
下面是具体实现:
1、头文件定义
0) -- exynos_4412.h
内容过多,这里不予以展示了,在前面的文章中均可找到相关寄存器定义
1) -- adc.h
- #ifndef __ADC_H_
- #define __ADC_H_
- void adc_init(int temp);
- void adc_collect(void);
- #endif
2) -- key.h
- #ifndef _KEY_H_
- #define _KEY_H_
- void key2_init(void);
- #endif
3) -- pwm.h
- #ifndef __PWM_H__
- #define __PWM_H__
- void pwm_init(void);
- void beep_on(void);
- void beep_off(void);
- #define SYS_SET_FREQUENCE 25000
- void beep_set_frequence( unsigned int fre );
- #endif
4) -- rtc.h
- #ifndef _RTC_H_
- #define _RTC_H_
- void rtc_init(void);
- void rtc_tic(void);
- void rtc_alarm(void);
- #endif
5) -- uart.h
- #ifndef _UART_H
- #define _UART_H
- void putc(const char data);
- void puts(const char *pstr);
- void uart_init(void);
- extern void putc(const char data);
- extern void puts(const char *pstr);
- extern void uart_init(void);
- #endif
2、具体函数实现:
1) -- adc.c
- /*
- * adc.c
- *
- * Created on: 2016-2-29
- * Author: Administrator
- */
- #include "exynos_4412.h"
- //ADC初始化函数
- adc_init(int temp)
- {
- // 初始化A/D控制寄存器
- // ADCCON [16]位置1,12bit输出;[14]位置1,允许预分频; [13:6] = 99,预分频值为99;
- // [1]位置1,采用读启动方式启动ADC;
- // A/D转换时间计算: PCLK = 100 MHZ,PRESCALER = 99,则 12 位转换时间为 100MHz/(99+1) = 1 MHZ
- ADCCON = (1 << 16 | 1 << 14 | 99 <<6 | 1 << 1);
- ADCMUX = 3; //电压输入通道选择,查看原理图,ADC连接 XadcAIN3,这里将ADCMUX = 3;
- temp = ADCDAT & 0xfff; // temp用于存放转换的数据值,由于是读启动方式启动ADC,第一次读是读不到正确值的,
- // 所以这里先读取依次进行初始化。
- }
- //ADC 采集函数
- adc_collect()
- {
- unsigned int temp;
- adc_init(temp);
- while(!(ADCCON & (1 << 15))); //读取ADCCON [15]位,当其为1时,A/D转换结束
- temp = ADCDAT & 0xfff; // 读取ADCDAT低12位,获取电压值
- temp = 18 * 100 * temp/0xfff; // 电压转换公式,电压最大值为1.8V,temp 范围为 0 ~ 4096
- // 由于没有浮点型头文件,这里不识别浮点型,这里将其转换成mv,
- // 其实应该是 1.8*1000*temp/0xfff,但1.8不被识别,这里用18*100 。
- printf("电压值 = %d mV\n",temp);
- if((temp > 2000)||(temp < 1000)) // 这里设正常值为 1000mv ~ 2000mV。
- {
- printf("电压异常!\n");
- beep_on(); //电压值异常时,蜂鸣器报警
- }
- else
- {
- beep_off(); // 如果调整到正常值,关闭蜂鸣器。
- }
- }
2) -- key.c
- /*
- * key.c
- *
- * Created on: 2016-2-29
- * Author: Administrator
- */
- #include "exynos_4412.h"
- //按键中断 初始化函数
- key2_init()
- {
- //1、 外设级寄存器设置
- GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引脚功能为外部中断,这里key2所连引脚为CPX1_1
- GPX1.PUD = GPX1.PUD & (~(0x3 << 2)); //关闭上下拉电阻
- EXT_INT41_CON = EXT_INT41_CON &(~(0xf << 4))|(0x2 << 4); //外部中断触发方式
- EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1)); //使能中断
- // 2、GIC级寄存器设置
- // 使能分配器
- ICDDCR = 1;
- // 使能相应中断到分配器,ICDISER每1bit控制一中断源;
- // EINT[9]中断号为57,在ICDISER1 第[25]位置1;
- ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25);
- // ICDIPTR每8位表示一中断源对应的CPU接口,所以一个ICDIPTR控制4个中断源
- // 这里中断号57在ICDPTR14 第[15:8]设置
- ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口
- //3、CPU0级寄存器设置
- CPU0.ICCPMR = 255; //中断屏蔽优先级
- CPU0.ICCICR = 1; //使能中断到CPU
- }
3) -- pwm.c
- #include "exynos_4412.h"
- #include "pwm.h"
- // 蜂鸣器函数配置,这里蜂鸣器是无源的,由PWM定时器控制,管脚为GPD0_0
- void pwm_init(void)
- {
- GPD0.CON = GPD0.CON & (~(0xf))| (0x2 << 0); //GPD0_0 由 GPD0.CON [3:0]控制,置2为TOUT_0
- GPD0.PUD = GPD0.PUD & (~(0xf)) ; //禁用上拉/下拉电阻
- //定时器配置,这里使用定时器0
- // 1、设置预分频值,范围为0~255,这里设为249
- PWM.TCFG0 = PWM.TCFG0 & (~(0xff))|0xf9;
- // 2、设置分频器分频值,有1、1/2、1/4、1/8、1/16 五种因子选择
- // TCFG1 [3:0]用于配置Time0,这里置2,选择分频值为1/4
- PWM.TCFG1 = PWM.TCFG1 & (~(0xf)) | 0x2; // 分频后, f = 100MHz/(250)/4 = 100kHz
- //3、TCMPB0 TCNTB0 配合进行占空比设置,
- // 定时器计数缓冲寄存器(TCNTBn)把计数器初始值下载到递减计数器中。
- // 定时器比较缓冲寄存器(TCMPBn)把其初始值下载到比较寄存器中,
- // 并将该值与递减计数器的值进行比较。当递减计数器和比较寄存器值相同时,输出电平翻转。
- // 递减计数器减至0后,输出电平再次翻转,完成一个输出周期。
- // 这里将 TCMPB0设置为50,TCNTB0设为100,占空比为50%。
- PWM.TCMPB0 = 50;
- PWM.TCNTB0 = 100;
- // 4、启动Time0,且第一次要手动更新,将TCMPB0和TCNTB0的值加载进递减计数器
- PWM.TCON = PWM.TCON & (~(0xff)) | (1 << 0) | (1 << 1) ;
- }
- // 开启蜂鸣器
- void beep_on(void)
- {
- PWM.TCON = PWM.TCON & (~(0xff)) | (1 << 0) | (1 << 3) ; //自动加载TCMPB0和TCNTB0的值
- }
- //关闭蜂鸣器
- void beep_off(void)
- {
- PWM.TCON = PWM.TCON & (~(1 << 0)) ; //[0]位置0,关闭定时器0
- }
- //#define SYS_SET_FREQUENCE 25000
- void beep_set_frequence( unsigned int fre )
- {
- //若蜂鸣器的发声频率为0则返回
- if( 0==fre )
- return ;
- PWM.TCMPB0 = SYS_SET_FREQUENCE/(fre+fre); //根据设定频率重新设定计数器比较的值
- PWM.TCNTB0 = SYS_SET_FREQUENCE/fre; //根据频率重新调整计数值
- }
4) -- rtc.c
- /*
- * rtc.c
- *
- * Created on: 2016-2-29
- * Author: Administrator
- */
- #include "exynos_4412.h"
- //RTC初始化函数
- void rtc_init(void)
- {
- RTCCON = 1; // RTC控制使能
- // 通过设置 BCD系列寄存器的值,对年月日时分秒进行配置
- RTC.BCDYEAR = 0x16;
- RTC.BCDMON = 0x2;
- RTC.BCDDAY = 0x29;
- RTC.BCDHOUR = 0x18;
- RTC.BCDMIN = 0x24;
- RTC.BCDSEC = 0x00;
- RTCCON = 0; //RTC控制禁止
- }
- // 滴答计时器配置
- void rtc_tic(void)
- {
- // RTCCON [7:4]用于设置滴答计时器子时钟源选择,这里设为 0000,即32768Hz
- // RTCCON [8] 置1,滴答计时器使能
- RTCCON = RTCCON & (~(0xf << 4)) | (1 << 8);
- // 配置TICCNT寄存器,这里设置为32768,时钟源为32768Hz, 1s发生一次中断。
- TICCNT = 32768;
- ICDDCR = 1; //使能分配器
- ICDISER.ICDISER2 = ICDISER.ICDISER2 | (0x1 << 13); //使能相应中断到分配器
- ICDIPTR.ICDIPTR19 = ICDIPTR.ICDIPTR19 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口
- CPU0.ICCPMR = 255; //中断屏蔽优先级
- CPU0.ICCICR = 1; //使能中断到CPU
- }
- // RTC 闹钟设置
- void rtc_alarm(void)
- {
- int i = 20;
- // 配置RTCALM.ALM寄存器,第[6]位置1,闹钟使能;第[0]位置1,秒时钟使能
- RTCALM.ALM = (1 << 6)|(1 << 0);
- RTCALM.SEC = 0x20; // SEC设为20,每到20秒时,闹钟到时,发生一次中断
- printf("请等待20s....\n");
- ICDDCR = 1; //使能分配器
- ICDISER.ICDISER2 = ICDISER.ICDISER2 | (0x1 << 12); //使能相应中断到分配器
- ICDIPTR.ICDIPTR19 = ICDIPTR.ICDIPTR19 & (~(0xff << 0))|(0x1 << 0); //选择CPU接口
- CPU0.ICCPMR = 255; //中断屏蔽优先级
- CPU0.ICCICR = 1; //使能中断到CPU
- while(i != 1)
- {
- printf("还剩 %-2d s\r", --i);
- mydelay_ms(1000);
- }
- printf("\n");
- }
5) -- uart.c
- #include "exynos_4412.h"
- // UART初始化函数
- void uart_init()
- {
- // COM2口 Rx Tx分别连接GPA1_0 GPA1_1
- // GPA1 [3:0]位 置2,设为UART_2_RXD,[7:4]位置2,设为UART_2_TXD;
- GPA1.CON = (GPA1.CON & ~0xFF ) | (0x22); //GPA1_0:RX;GPA1_1:TX
- // 设置传输格式:ULCONn寄存器[1:0]用于设置数据位bit数,这里设为8
- UART2.ULCON2 = 0x3;
- // 设置UART工作模式:UCONn寄存器 [3:2] [1:0] 均置为1 ,Tx Rx 均设为中断或轮询模式
- UART2.UCON2 = 0x5;
- /*
- * 波特率设置
- 根据给定的波特率、所选择时钟源频率,
- 可以通过以下公式计算 UBRDIVn 寄存器 (n 为 0~4,对应 5个 UART 通道 )的值。
- UBRDIVn = (int)( UART clock / ( buad rate x 16) ) – 1
- 上式计算出来的 UBRDIVn 寄存器值不一定是整数,
- UBRDIVn 寄存器取其整数部分,小部分由 UFRACVALn 寄存器设置,
- 例如,当UART clock为100MHz时,要求波特率为115200 bps,则:
- 100000000/(115200 x 16) – 1 = 54.25 – 1 = 53.25
- UBRDIVn = 整数部分 = 53
- UFRACVALn/16 = 小数部分 = 0.25
- UFRACVALn = 4
- */
- UART2.UBRDIV2 = 0x35;
- UART2.UFRACVAL2 = 0x4;
- }
- void putc(const char data)
- {
- while(!(UART2.UTRSTAT2 & 0X2));
- UART2.UTXH2 = data;
- if (data == '\n')
- putc('\r');
- }
- char getc(void)
- {
- char data;
- while(!(UART2.UTRSTAT2 & 0x1));
- data = UART2.URXH2;
- if ((data == '\n')||(data == '\r'))
- {
- putc('\n');
- putc('\r');
- }else
- putc(data);
- return data;
- }
- void puts(const char *pstr)
- {
- while(*pstr != '\0')
- putc(*pstr++);
- }
- void gets(char *p)
- {
- char data;
- while((data = getc())!= '\r')
- {
- if(data == '\b')
- {
- p--;
- }
- *p++ = data;
- }
- if(data == '\r')
- *p++ = '\n';
- *p = '\0';
- }
3、main函数
- #include "exynos_4412.h"
- #include "adc.h"
- #include "key.h"
- #include "pwm.h"
- #include "rtc.h"
- #include "uart.h"
- void mydelay_ms(int time)
- {
- int i, j;
- while(time--)
- {
- for (i = 0; i < 5; i++)
- for (j = 0; j < 514; j++);
- }
- }
- void do_irq(void)
- {
- static int a = 1;
- int irq_num;
- irq_num = CPU0.ICCIAR&0x3ff; //获取中断号
- switch(irq_num)
- {
- case 57: //按键中断
- beep_off();
- printf("请将电压调到正常值!!\n");
- mydelay_ms(1000); //延时1s,等待电压调整
- EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1)); //清GPIO中断标志位
- ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25); //清GIC中断标志位
- break;
- case 76: // RTC 闹钟中断
- printf("20s已到,开始采集电压值:\n");
- rtc_tic(); // 20s 到后,调用滴答计时器
- RTCALM.ALM = RTCALM.ALM & (~(1 << 6));//关掉闹钟,防止下一个20s中断再次发生
- RTCINTP = RTCINTP | (1 << 1); //清RTC中断标志位
- ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 12); //清GIC中断标志位
- break;
- case 77: //滴答计时器中断
- adc_collect(); //调用ad采样函数
- RTCINTP = RTCINTP | (1 << 0); //清RTC中断标志位
- ICDICPR.ICDICPR2 = ICDICPR.ICDICPR2 | (0x1 << 13); //清GIC中断标志位
- break;
- }
- CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num; //清cpu中断标志位
- }
- /*
- * 裸机代码,不同于LINUX 应用层, 一定加循环控制
- */
- int main (void)
- {
- printf("\n");
- printf("------------------practice--------------------\n");
- uart_init();
- pwm_init();
- rtc_init();
- key2_init();
- rtc_alarm(); //调用alarm函数,开始定时
- while(1) //什么都不做,等待中断发生
- {
- }
- return 0;
- }
将程序下载到开发板中,执行结果如下:
- ------------------practice--------------------
- 请等待20s....
- 20s已到,开始采集电压值:
- 电压值 = 1219 mV
- 电压值 = 1218 mV
- 电压值 = 1219 mV
- 电压值 = 1219 mV
- 电压值 = 1222 mV
- 电压值 = 1333 mV
- 电压值 = 1391 mV
- 电压值 = 1390 mV
- 电压值 = 1403 mV
- 电压值 = 1496 mV
- 电压值 = 1800 mV
- 电压值 = 1560 mV
- 电压值 = 873 mV
- 电压异常!
- 电压值 = 825 mV
- 电压异常!
- 电压值 = 825 mV
- 电压异常!
- 电压值 = 826 mV
- 电压异常!
- 电压值 = 826 mV
- 电压异常!
- 请将电压调到正常值!!
- 电压值 = 825 mV
- 电压值 = 825 mV
- 电压值 = 826 mV
- 电压异常!
- 请将电压调到正常值!!
- 电压值 = 827 mV
- 电压异常!
- 电压值 = 825 mV
- 电压异常!
- 电压值 = 825 mV
- 电压异常!
- 电压值 = 684 mV
- 电压异常!
- 电压值 = 143 mV
- 电压异常!
- 电压值 = 364 mV
- 电压异常!
- 电压值 = 1114 mV
- 电压值 = 1121 mV
- 电压值 = 1120 mV
- 电压值 = 1120 mV
- 电压值 = 1121 mV
- 电压值 = 1121 mV
- 电压值 = 1120 mV
- 电压值 = 1121 mV