本系列教程將結(jié)合TI推出的CC254x SoC 系列,講解從環(huán)境的搭建到藍(lán)牙4.0協(xié)議棧的開(kāi)發(fā)來(lái)深入學(xué)習(xí)藍(lán)牙4.0的開(kāi)發(fā)過(guò)程。教程共分為六部分,本文為第二部分:
??????
第二部分知識(shí)點(diǎn):
第六節(jié) 獨(dú)立按鍵之查詢(xún)方式
第七節(jié) 獨(dú)立按鍵之中斷方式
第八節(jié) CC254x內(nèi)部溫度傳感器溫度采集
第九節(jié) 五向按鍵
第十節(jié) 蜂鳴器
?
有關(guān)TI 的CC254x芯片介紹,可點(diǎn)擊下面鏈接查看:
主流藍(lán)牙BLE控制芯片詳解(1):TI CC2540
?
由淺入深,藍(lán)牙4.0/BLE協(xié)議棧開(kāi)發(fā)攻略大全(1)
?
有關(guān)本文的工具下載,大家可以到以下這個(gè)地址:
朱兆祺ForARM第六節(jié) 獨(dú)立按鍵之查詢(xún)方式
在MT254xboard上有一個(gè)獨(dú)立按鍵KEY1,如圖 ,獨(dú)立按鍵和復(fù)位鍵在整個(gè)班子的左上角。按鍵通過(guò)P0.0口和CPU連接,在沒(méi)有按鍵時(shí)為高電平,按下后為低電平。下面我們通過(guò)LCD來(lái)顯示獨(dú)立按鍵的狀態(tài)。
其對(duì)應(yīng)的原理圖如下:
我們先用查詢(xún)的方式讀取按鍵的狀態(tài)。因?yàn)榘存I接入在P0.0口,所以我們讀取P0.0口的電平即可知道按鍵的狀態(tài)。
uint8 KeyValue(void) // 讀取按鍵狀態(tài)
{
if((P0&0X01) == 0X00 ) // 按下為低電平
{
return KEY_DOWN;
}
else
{
return KEY_UP;
}
}
這里我們?cè)趙hile循環(huán)中不斷的讀取按鍵狀態(tài),并且判斷是否改變,如果改變則改變LCD的顯示。
int main(void)
{
uint8 OldKeyValue = 0;
uint8 NewKeyValue = 0;
SysStartXOSC();
LCD12864_Init();
LCD12864_DisStr(1, “ Key Test”);
// 按鍵初始化
P0SEL &= ~0X01; // 設(shè)置為 IO功能
P0DIR &= ~0X01; // 設(shè)置為輸入功能
while(1)
{
NewKeyValue = KeyValue(); // 讀取按鍵狀態(tài)
if(OldKeyValue != NewKeyValue) // 按鍵狀態(tài)改變
{
OldKeyValue = NewKeyValue; // 保存當(dāng)前按鍵狀態(tài)
if(OldKeyValue == KEY_DOWN)
{
LCD12864_DisStr(3, “ Key Down ”);
}
else
{
LCD12864_DisStr(3, “ Key Up ”);
}
}
}
return 0;
}
運(yùn)行程序,效果如圖所示:
第七節(jié) 獨(dú)立按鍵之中斷方式
復(fù)制Key工程,重命名為KeyInterrupt。剛剛我們用查詢(xún)的方式讀取按鍵的狀態(tài)。但是這種方式在實(shí)際的工程中沒(méi)有實(shí)際的應(yīng)用價(jià)值,下面我們采用外部中斷的方式來(lái)讀取按鍵的狀態(tài),每當(dāng)按鍵按下時(shí)就會(huì)觸發(fā)一次外部中斷。為了P0.0口能夠觸發(fā)中斷,我們需要進(jìn)行如下配置:
P0IEN |= 0X01; // P00 設(shè)置為中斷方式
PICTL &=~ 0X01; // 下降沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標(biāo)志位
EA = 1; // 開(kāi)總中斷
然后就需要編寫(xiě)中斷服務(wù)函數(shù)了。這里注意一點(diǎn),在IAR中的中斷函數(shù)有點(diǎn)特殊,格式為:
#pragma vector = 中斷向量
__interrupt 函數(shù)
所以我們的中斷函數(shù)為:
#pragma vector = P0INT_VECTOR
__interrupt void P0_ISR(void)
{
if(0x01&P0IFG)
{
NewKeyValue = KEY_DOWN; // 記錄按鍵按下
}
P0IFG = 0; //清中斷標(biāo)志
P0IF = 0; //清中斷標(biāo)志
}
在中斷中我們記錄按鍵按下,等待應(yīng)用程序處理。而在主函數(shù)中我們需要處理按鍵按下事件,主函數(shù)中我們對(duì)按鍵計(jì)數(shù)并且通過(guò)LCD顯示。
int main(void)
{
char LCDBuf[21]={0}; // 顯存
int KeyCnt = 0;
SysStartXOSC();
LCD12864_Init();
LCD12864_DisStr(1, “ Key Test”);
P0SEL &= ~0X01; // 設(shè)置為IO功能
P0DIR &= ~0X01; // 設(shè)置為輸入功能
P0IEN |= 0X01; // P0.0 設(shè)置為中斷方式
PICTL |= 0X01; // 下降沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標(biāo)志位
EA = 1; // 開(kāi)總中斷
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計(jì)數(shù)
LCD12864_DisStr(3, LCDBuf);
while(1)
{
if(KEY_DOWN == NewKeyValue) // 按鍵按下
{
SoftWaitUs(25000); // 延時(shí)防抖
if((P0&0X01) == 0X00) // 再次確認(rèn)按鍵是否按下
{
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計(jì)數(shù)
LCD12864_DisStr(3, LCDBuf);
}
else
{
NewKeyValue = KEY_UP; // 按鍵松開(kāi)
}
}
}
return 0;
}
每按一次按鍵計(jì)數(shù)加1,效果如圖所示:
?
第八節(jié) CC254x內(nèi)部溫度傳感器溫度采集
CC254x內(nèi)部有一個(gè)溫度傳感器,我們這節(jié)使用這個(gè)傳感器來(lái)采集芯片的溫度,此傳感器精度不高。不適合用于實(shí)際的工程中,這里只為演示AD采樣。要使用內(nèi)部的溫度采集我們需要使用AD采樣,所以我們需要先來(lái)了解CC254x的AD功能。在后續(xù)課程有對(duì)ADC的詳細(xì)說(shuō)明。
ADC結(jié)構(gòu)圖如下所示:
ADC控制寄存器1如下圖所示:
我們使用手動(dòng)觸發(fā)的方式進(jìn)行AD采樣,所以STSEL = 11B,最低兩位始終為1,最終ADCCON1=0x33。
ADC控制寄存器3如圖所示:
ADC參考電壓使用內(nèi)部電壓,采用12位精度采集。采集溫度通道。所以ADCCON3= 0x3E。這里注意一點(diǎn),ADCCON2和ADCCON3的配置是一樣的,我們這里用ADCCON3來(lái)配置。
uint16 ADC_Read (uint8 channel)
{
int16 reading = 0;
uint8 adcChannel = 0x01《《channel;
int16 Result = 0;
if (channel 《= 7) // 通道0-7需要通過(guò)P0.0-P0.7輸入
{
ADCCFG |= adcChannel;
}
uint8 i=0;
do{
ADCCON3 = channel | 0x20; // 12位精度,啟動(dòng)轉(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(); // 啟動(dòng)外部晶振
LCD12864_Init(); // LCD初始化
// 打開(kāi)溫度傳感器
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上,可以看到溫度在跳動(dòng),這是由于AD的誤差太大導(dǎo)致的,這里只做一個(gè)簡(jiǎn)單的實(shí)驗(yàn),如果需要工程應(yīng)用,建議外接溫度傳感器。把手放在芯片上可以看到溫度在上升。溫度采集結(jié)果如下圖所示:
第九節(jié) 五向按鍵
五向按鍵,也就是我們平常所見(jiàn)的搖桿內(nèi)部構(gòu)造,五向按鍵有上下左右和中間五個(gè)按鍵值,MT254xboard上的五向按鍵檢測(cè)電路由饅頭科技自主設(shè)計(jì),而不是Ti的設(shè)計(jì),采用一個(gè)外部中斷和一個(gè)AD檢測(cè)口來(lái)完成按鍵的檢測(cè)。
由原理圖可知當(dāng)我們按下不同的鍵值時(shí)在JOY_CHK將會(huì)產(chǎn)生一個(gè)上升沿,并且在JOY_AD口有不同的電壓。我們只需要在JOY_CHK的外部中斷中讀取JOY_AD的電壓即可識(shí)別不同的按鍵。
外部中斷和AD采用在前面已經(jīng)講過(guò)了,這里只需要拿來(lái)用就可以了。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; // 設(shè)置為IO功能
P0DIR &= ~0X80; // 設(shè)置為輸入功能
P0IEN |= 0X80; // P0.7 設(shè)置為中斷方式
PICTL &= ~0X80; // 上升沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標(biāo)志位
EA = 1; // 開(kāi)總中斷
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;
}
按鍵的檢測(cè)通過(guò)電壓來(lái)區(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é) 蜂鳴器
蜂鳴器是一種常用的報(bào)警設(shè)備,常用的蜂鳴器有無(wú)源和有源兩種類(lèi)型,無(wú)源蜂鳴器需要用一定頻率的方波驅(qū)動(dòng),從而發(fā)出不同頻率的聲音。而有源蜂鳴器只需要通電就會(huì)發(fā)出固定頻率的聲音,MT254xboard開(kāi)發(fā)板上的蜂鳴器用的是無(wú)源蜂鳴器,因此我們需要用一定頻率的方波來(lái)驅(qū)動(dòng)。
硬件驅(qū)動(dòng)方面,我們這里使用了PNP三極管來(lái)驅(qū)動(dòng)蜂鳴器,BUZZ引腳為芯片的P2.0。對(duì)照IO復(fù)用表可知,此IO可以作為定時(shí)器4的匹配通道1輸出。所以我們需要把定時(shí)器配置為PWM匹配輸出模式:
PERCFG |= (0x01《《4); // 選擇定時(shí)器4匹配功能中的第2種IO口
P2DIR |= 0x01; // p2.0 輸出
P2SEL |= 0x01; // p2.0 復(fù)用功能
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
這里僅僅是配置為匹配輸出,具體輸出什么樣的波形還需要我們?cè)偻ㄟ^(guò)計(jì)算得出。
void Buzzer_Start(uint16 frequency)
{
P2SEL |= 0x01; // p2.0 復(fù)用功能
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;
}
這個(gè)函數(shù)是通過(guò)傳入參數(shù)的形式,使P2.0口發(fā)出指定頻率的方波。
void Buzzer_Stop(void)
{
T4CTL &= ~0x10; // Stop timer 3
P2SEL &= ~0x01;
P2_0 = 1;
}
這個(gè)函數(shù)是使蜂鳴器停止,主要有三個(gè)動(dòng)作,停止定時(shí)器,將P2.0配置為IO功能并且輸出高電平,因?yàn)槲覀兪褂玫氖荘NP三極管。
我們?cè)诎存I的程序上加上蜂鳴器的控制,當(dāng)按下按鍵時(shí),蜂鳴器響。松開(kāi)后停止響。
int main(void)
{
char LCDBuf[21]={0}; // 顯存
int KeyCnt = 0;
SysStartXOSC();
LCD12864_Init();
LCD12864_DisStr(1, “ Buzzer Test”);
Buzzer_Init();
P0SEL &= ~0X01; // 設(shè)置為IO功能
P0DIR &= ~0X01; // 設(shè)置為輸入功能
P0IEN |= 0X01; // P0.0 設(shè)置為中斷方式
PICTL |= 0X01; // 下降沿觸發(fā)
IEN1 |= 0X20; // 允許P0口中斷
P0IFG = 0x00; // 清除中斷標(biāo)志位
EA = 1; // 開(kāi)總中斷
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計(jì)數(shù)
LCD12864_DisStr(3, LCDBuf);
while(1)
{
if(KEY_DOWN == NewKeyValue) // 按鍵按下
{
SoftWaitUs(25000); // 延時(shí)防抖
if((P0&0X01) == 0X00) // 再次確認(rèn)按鍵是否按下
{
sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按鍵計(jì)數(shù)
LCD12864_DisStr(3, “ Buzzer Start”);
Buzzer_Start(2000);
}
else
{
NewKeyValue = KEY_UP; // 按鍵松開(kāi)
Buzzer_Stop();
LCD12864_DisStr(3, “ Buzzer Stop”);
}
}
}
return 0;
}
按下按鍵后可以看到LCD顯示Buzzer Start,聽(tīng)到蜂鳴器響,如果你有示波器,還能測(cè)到P2.0口有一個(gè)2KHz的方波。
評(píng)論
查看更多