本系列教程將結(jié)合TI推出的CC254x SoC 系列,講解從環(huán)境的搭建到藍牙4.0協(xié)議棧的開發(fā)來深入學習藍牙4.0的開發(fā)過程。教程共分為六部分,本文為第二部分:
??????
第二部分知識點:
第六節(jié) 獨立按鍵之查詢方式
第七節(jié) 獨立按鍵之中斷方式
第八節(jié) CC254x內(nèi)部溫度傳感器溫度采集
第九節(jié) 五向按鍵
第十節(jié) 蜂鳴器
?
有關TI 的CC254x芯片介紹,可點擊下面鏈接查看:
由淺入深,藍牙4.0/BLE協(xié)議棧開發(fā)攻略大全(1)
?
有關本文的工具下載,大家可以到以下這個地址:
朱兆祺ForARM第六節(jié) 獨立按鍵之查詢方式
在MT254xboard上有一個獨立按鍵KEY1,如圖 ,獨立按鍵和復位鍵在整個班子的左上角。按鍵通過P0.0口和CPU連接,在沒有按鍵時為高電平,按下后為低電平。下面我們通過LCD來顯示獨立按鍵的狀態(tài)。
其對應的原理圖如下:
我們先用查詢的方式讀取按鍵的狀態(tài)。因為按鍵接入在P0.0口,所以我們讀取P0.0口的電平即可知道按鍵的狀態(tài)。
uint8 KeyValue(void) // 讀取按鍵狀態(tài)
{
if((P0&0X01) == 0X00 ) // 按下為低電平
{
return KEY_DOWN;
}
else
{
return KEY_UP;
}
}
這里我們在while循環(huán)中不斷的讀取按鍵狀態(tài),并且判斷是否改變,如果改變則改變LCD的顯示。
int main(void)
{
uint8 OldKeyValue = 0;
uint8 NewKeyValue = 0;
SysStartXOSC();
LCD12864_Init();
LCD12864_DisStr(1, “ Key Test”);
// 按鍵初始化
P0SEL &= ~0X01; // 設置為 IO功能
P0DIR &= ~0X01; // 設置為輸入功能
while(1)
{
NewKeyValue = KeyValue(); // 讀取按鍵狀態(tài)
if(OldKeyValue != NewKeyValue) // 按鍵狀態(tài)改變
{
OldKeyValue = NewKeyValue; // 保存當前按鍵狀態(tài)
if(OldKeyValue == KEY_DOWN)
{
LCD12864_DisStr(3, “ Key Down ”);
}
else
{
LCD12864_DisStr(3, “ Key Up ”);
}
}
}
return 0;
}
運行程序,效果如圖所示:
第七節(jié) 獨立按鍵之中斷方式
復制Key工程,重命名為KeyInterrupt。剛剛我們用查詢的方式讀取按鍵的狀態(tài)。但是這種方式在實際的工程中沒有實際的應用價值,下面我們采用外部中斷的方式來讀取按鍵的狀態(tài),每當按鍵按下時就會觸發(fā)一次外部中斷。為了P0.0口能夠觸發(fā)中斷,我們需要進行如下配置:
P0IEN |= 0X01; // P00 設置為中斷方式
PICTL &=~ 0X01; // 下降沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標志位
EA = 1; // 開總中斷
然后就需要編寫中斷服務函數(shù)了。這里注意一點,在IAR中的中斷函數(shù)有點特殊,格式為:
#pragma vector = 中斷向量
__interrupt 函數(shù)
所以我們的中斷函數(shù)為:
#pragma vector = P0INT_VECTOR
__interrupt void P0_ISR(void)
{
if(0x01&P0IFG)
{
NewKeyValue = KEY_DOWN; // 記錄按鍵按下
}
P0IFG = 0; //清中斷標志
P0IF = 0; //清中斷標志
}
在中斷中我們記錄按鍵按下,等待應用程序處理。而在主函數(shù)中我們需要處理按鍵按下事件,主函數(shù)中我們對按鍵計數(shù)并且通過LCD顯示。
int main(void)
{
char LCDBuf[21]={0}; // 顯存
int KeyCnt = 0;
SysStartXOSC();
LCD12864_Init();
LCD12864_DisStr(1, “ Key Test”);
P0SEL &= ~0X01; // 設置為IO功能
P0DIR &= ~0X01; // 設置為輸入功能
P0IEN |= 0X01; // P0.0 設置為中斷方式
PICTL |= 0X01; // 下降沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標志位
EA = 1; // 開總中斷
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計數(shù)
LCD12864_DisStr(3, LCDBuf);
while(1)
{
if(KEY_DOWN == NewKeyValue) // 按鍵按下
{
SoftWaitUs(25000); // 延時防抖
if((P0&0X01) == 0X00) // 再次確認按鍵是否按下
{
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計數(shù)
LCD12864_DisStr(3, LCDBuf);
}
else
{
NewKeyValue = KEY_UP; // 按鍵松開
}
}
}
return 0;
}
每按一次按鍵計數(shù)加1,效果如圖所示:
?
第八節(jié) CC254x內(nèi)部溫度傳感器溫度采集
CC254x內(nèi)部有一個溫度傳感器,我們這節(jié)使用這個傳感器來采集芯片的溫度,此傳感器精度不高。不適合用于實際的工程中,這里只為演示AD采樣。要使用內(nèi)部的溫度采集我們需要使用AD采樣,所以我們需要先來了解CC254x的AD功能。在后續(xù)課程有對ADC的詳細說明。
ADC結(jié)構(gòu)圖如下所示:
ADC控制寄存器1如下圖所示:
我們使用手動觸發(fā)的方式進行AD采樣,所以STSEL = 11B,最低兩位始終為1,最終ADCCON1=0x33。
ADC控制寄存器3如圖所示:
ADC參考電壓使用內(nèi)部電壓,采用12位精度采集。采集溫度通道。所以ADCCON3= 0x3E。這里注意一點,ADCCON2和ADCCON3的配置是一樣的,我們這里用ADCCON3來配置。
uint16 ADC_Read (uint8 channel)
{
int16 reading = 0;
uint8 adcChannel = 0x01《《channel;
int16 Result = 0;
if (channel 《= 7) // 通道0-7需要通過P0.0-P0.7輸入
{
ADCCFG |= adcChannel;
}
uint8 i=0;
do{
ADCCON3 = channel | 0x20; // 12位精度,啟動轉(zhuǎn)換
while (!(ADCCON1 & 0x80)); // 等待轉(zhuǎn)換完成
// 讀取采樣結(jié)果
reading = (int16)(ADCL);
reading |= (int16)(ADCH 《《 8);
reading 》》= 4; // 丟棄低位
Result += reading; // 累加
}while(i++ 《 10); // 連續(xù)采樣10次
if (channel 《= 7)
{
ADCCFG &= (adcChannel ^ 0xFF);
}
return (Result/10);
}
在讀取溫度值前,我們還需要使能溫度傳感器。
int main(void)
{
float temp=0;
char LCDBuf[21] = {0};
SysStartXOSC(); // 啟動外部晶振
LCD12864_Init(); // LCD初始化
// 打開溫度傳感器
TR0 = 0x01;
ATEST = 0x01;
while(1)
{
temp = (ADC_Read(TEMP_ADC_CHANNEL) - 1340) /10.0;
sprintf(LCDBuf, “ temp : %0.1f”, temp); //
LCD12864_DisStr(3, LCDBuf);
SoftWaitUs(100000);
}
return 0;
}
采集的溫度顯示在LCD上,可以看到溫度在跳動,這是由于AD的誤差太大導致的,這里只做一個簡單的實驗,如果需要工程應用,建議外接溫度傳感器。把手放在芯片上可以看到溫度在上升。溫度采集結(jié)果如下圖所示:
第九節(jié) 五向按鍵
五向按鍵,也就是我們平常所見的搖桿內(nèi)部構(gòu)造,五向按鍵有上下左右和中間五個按鍵值,MT254xboard上的五向按鍵檢測電路由饅頭科技自主設計,而不是Ti的設計,采用一個外部中斷和一個AD檢測口來完成按鍵的檢測。
由原理圖可知當我們按下不同的鍵值時在JOY_CHK將會產(chǎn)生一個上升沿,并且在JOY_AD口有不同的電壓。我們只需要在JOY_CHK的外部中斷中讀取JOY_AD的電壓即可識別不同的按鍵。
外部中斷和AD采用在前面已經(jīng)講過了,這里只需要拿來用就可以了。JOY_CHK連接在P0.7腳,JOY_AD連接在P0.6腳。我們將按鍵值顯示在LCD上。
int main(void)
{
uint8 KeyValue = 0;
SysStartXOSC();
LCD12864_Init();
LCD12864_DisStr(1, “ JoyStick Test”);
P0INP |= 0X40; // P0.6 三態(tài)
P0SEL &= ~0X80; // 設置為IO功能
P0DIR &= ~0X80; // 設置為輸入功能
P0IEN |= 0X80; // P0.7 設置為中斷方式
PICTL &= ~0X80; // 上升沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標志位
EA = 1; // 開總中斷
while(1)
{
if(KeyStat) // 按鍵按下
{
KeyValue = GetKeyValue();
switch ( KeyValue )
{
case KEY_UP :
sprintf(LCDBuf, “ UP”);
break;
case KEY_DOWN :
sprintf(LCDBuf, “ Down”);
break;
case KEY_LEFT :
sprintf(LCDBuf, “ Left”);
break;
case KEY_CENTER :
sprintf(LCDBuf, “ Center”);
break;
case KEY_RIGHT :
sprintf(LCDBuf, “ Right”);
break;
default:
break;
}
KeyStat =0;
LCD12864_DisStr(3, LCDBuf);
}
}
return 0;
}
按鍵的檢測通過電壓來區(qū)分。
uint8 GetKeyValue(void)
{
uint16 adc;
uint8 ksave0 = 0;
adc = ADC_Read (JOY_AD_CHANNEL);
if ((adc 》= 800) && (adc 《= 1100))
{
ksave0 = KEY_RIGHT;
}
else if ((adc 》= 1200) && (adc 《= 2000))
{
ksave0 = KEY_CENTER;
}
else if ((adc 》= 2050) && (adc 《= 2150))
{
ksave0 = KEY_UP;
}
else if ((adc 》= 2200) && (adc 《= 2230))
{
ksave0 = KEY_LEFT;
}
else if ((adc 》= 2240) && (adc 《= 2500))
{
ksave0 = KEY_DOWN;
}
return ksave0;
}
使用五向按鍵效果如下所示:
第十節(jié) 蜂鳴器
蜂鳴器是一種常用的報警設備,常用的蜂鳴器有無源和有源兩種類型,無源蜂鳴器需要用一定頻率的方波驅(qū)動,從而發(fā)出不同頻率的聲音。而有源蜂鳴器只需要通電就會發(fā)出固定頻率的聲音,MT254xboard開發(fā)板上的蜂鳴器用的是無源蜂鳴器,因此我們需要用一定頻率的方波來驅(qū)動。
硬件驅(qū)動方面,我們這里使用了PNP三極管來驅(qū)動蜂鳴器,BUZZ引腳為芯片的P2.0。對照IO復用表可知,此IO可以作為定時器4的匹配通道1輸出。所以我們需要把定時器配置為PWM匹配輸出模式:
PERCFG |= (0x01《《4); // 選擇定時器4匹配功能中的第2種IO口
P2DIR |= 0x01; // p2.0 輸出
P2SEL |= 0x01; // p2.0 復用功能
T4CTL &= ~0x10; // Stop timer 3 (if it was running)
T4CTL |= 0x04; // Clear timer 3
T4CTL &= ~0x08; // Disable Timer 3 overflow interrupts
T4CTL |= 0x03; // Timer 3 mode = 3 - Up/Down
T4CCTL0 &= ~0x40; // Disable channel 0 interrupts
T4CCTL0 |= 0x04; // Ch0 mode = compare
T4CCTL0 |= 0x10; // Ch0 output compare mode = toggle on compare
這里僅僅是配置為匹配輸出,具體輸出什么樣的波形還需要我們再通過計算得出。
void Buzzer_Start(uint16 frequency)
{
P2SEL |= 0x01; // p2.0 復用功能
uint8 prescaler = 0;
// Get current Timer tick divisor setting
uint8 tickSpdDiv = (CLKCONSTA & 0x38)》》3;
// Check if frequency too low
if (frequency 《 (244 》》 tickSpdDiv)){ // 244 Hz = 32MHz / 256 (8bit counter) / 4 (up/down counter and toggle on compare) / 128 (max timer prescaler)
Buzzer_Stop(); // A lower tick speed will lower this number accordingly.
}
// Calculate nr of ticks required to achieve target frequency
uint32 ticks = (8000000/frequency) 》》 tickSpdDiv; // 8000000 = 32M / 4;
// Fit this into an 8bit counter using the timer prescaler
while ((ticks & 0xFFFFFF00) != 0)
{
ticks 》》= 1;
prescaler += 32;
}
// Update registers
T4CTL &= ~0xE0;
T4CTL |= prescaler;
T4CC0 = (uint8)ticks;
// Start timer
T4CTL |= 0x10;
}
這個函數(shù)是通過傳入參數(shù)的形式,使P2.0口發(fā)出指定頻率的方波。
void Buzzer_Stop(void)
{
T4CTL &= ~0x10; // Stop timer 3
P2SEL &= ~0x01;
P2_0 = 1;
}
這個函數(shù)是使蜂鳴器停止,主要有三個動作,停止定時器,將P2.0配置為IO功能并且輸出高電平,因為我們使用的是PNP三極管。
我們在按鍵的程序上加上蜂鳴器的控制,當按下按鍵時,蜂鳴器響。松開后停止響。
int main(void)
{
char LCDBuf[21]={0}; // 顯存
int KeyCnt = 0;
SysStartXOSC();
LCD12864_Init();
LCD12864_DisStr(1, “ Buzzer Test”);
Buzzer_Init();
P0SEL &= ~0X01; // 設置為IO功能
P0DIR &= ~0X01; // 設置為輸入功能
P0IEN |= 0X01; // P0.0 設置為中斷方式
PICTL |= 0X01; // 下降沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標志位
EA = 1; // 開總中斷
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計數(shù)
LCD12864_DisStr(3, LCDBuf);
while(1)
{
if(KEY_DOWN == NewKeyValue) // 按鍵按下
{
SoftWaitUs(25000); // 延時防抖
if((P0&0X01) == 0X00) // 再次確認按鍵是否按下
{
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計數(shù)
LCD12864_DisStr(3, “ Buzzer Start”);
Buzzer_Start(2000);
}
else
{
NewKeyValue = KEY_UP; // 按鍵松開
Buzzer_Stop();
LCD12864_DisStr(3, “ Buzzer Stop”);
}
}
}
return 0;
}
按下按鍵后可以看到LCD顯示Buzzer Start,聽到蜂鳴器響,如果你有示波器,還能測到P2.0口有一個2KHz的方波。
評論
查看更多