下面是一個案例需求:
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