×
家电数码 > 手机 > 详情

由浅入深,蓝牙4.0/BLE协议栈开发攻略大全(2)

发布时间:2020-05-25 发布时间:
|

  

  本系列教程将结合TI推出的CC254x SoC 系列,讲解从环境的搭建到蓝牙4.0协议栈的开发来深入学习蓝牙4.0的开发过程。教程共分为六部分,本文为第二部分:
      

  第二部分知识点:

  第六节 独立按键之查询方式

  第七节 独立按键之中断方式

  第八节 CC254x内部温度传感器温度采集

  第九节 五向按键

  第十节 蜂鸣器
 

  有关TI 的CC254x芯片介绍,可点击下面链接查看:

  主流蓝牙BLE控制芯片详解(1):TI CC2540
 

  同系列资料推荐:
 

  由浅入深,蓝牙4.0/BLE协议栈开发攻略大全(1)
 

  有关本文的工具下载,大家可以到以下这个地址:

  朱兆祺ForARM

  第六节 独立按键之查询方式

  在MT254xboard上有一个独立按键KEY1,如图 ,独立按键和复位键在整个班子的左上角。按键通过P0.0口和CPU连接,在没有按键时为高电平,按下后为低电平。下面我们通过LCD来显示独立按键的状态。


  其对应的原理图如下:

  我们先用查询的方式读取按键的状态。因为按键接入在P0.0口,所以我们读取P0.0口的电平即可知道按键的状态。

  uint8 KeyValue(void) // 读取按键状态

  {

  if((P0&0X01) == 0X00 ) // 按下为低电平

  {

  return KEY_DOWN;

  }

  else

  {

  return KEY_UP;

  }

  }

  这里我们在while循环中不断的读取按键状态,并且判断是否改变,如果改变则改变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(); // 读取按键状态

  if(OldKeyValue != NewKeyValue) // 按键状态改变

  {

  OldKeyValue = NewKeyValue; // 保存当前按键状态

  if(OldKeyValue == KEY_DOWN)

  {

  LCD12864_DisStr(3, “ Key Down ”);

  }

  else

  {

  LCD12864_DisStr(3, “ Key Up ”);

  }

  }

  }

  return 0;

  }

  运行程序,效果如图所示:

  第七节 独立按键之中断方式

  复制Key工程,重命名为KeyInterrupt。刚刚我们用查询的方式读取按键的状态。但是这种方式在实际的工程中没有实际的应用价值,下面我们采用外部中断的方式来读取按键的状态,每当按键按下时就会触发一次外部中断。为了P0.0口能够触发中断,我们需要进行如下配置:

  P0IEN |= 0X01; // P00 设置为中断方式

  PICTL &=~ 0X01; // 下降沿触发

  IEN1 |= 0X20; // 允许P0口中断

  P0IFG = 0x00; // 清除中断标志位

  EA = 1; // 开总中断

  然后就需要编写中断服务函数了。这里注意一点,在IAR中的中断函数有点特殊,格式为:

  #pragma vector = 中断向量

  __interrupt 函数

  所以我们的中断函数为:

  #pragma vector = P0INT_VECTOR

  __interrupt void P0_ISR(void)

  {

  if(0x01&P0IFG)

  {

  NewKeyValue = KEY_DOWN; // 记录按键按下

  }

  P0IFG = 0; //清中断标志

  P0IF = 0; //清中断标志

  }

  在中断中我们记录按键按下,等待应用程序处理。而在主函数中我们需要处理按键按下事件,主函数中我们对按键计数并且通过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; // 下降沿触发

  IEN1 |= 0X20; // 允许P0口中断

  P0IFG = 0x00; // 清除中断标志位

  EA = 1; // 开总中断

  sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按键计数

  LCD12864_DisStr(3, LCDBuf);

  while(1)

  {

  if(KEY_DOWN == NewKeyValue) // 按键按下

  {

  SoftWaitUs(25000); // 延时防抖

  if((P0&0X01) == 0X00) // 再次确认按键是否按下

  {

  sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按键计数

  LCD12864_DisStr(3, LCDBuf);

  }

  else

  {

  NewKeyValue = KEY_UP; // 按键松开

  }

  }

  }

  return 0;

  }

  每按一次按键计数加1,效果如图所示:


 

  第八节 CC254x内部温度传感器温度采集

  CC254x内部有一个温度传感器,我们这节使用这个传感器来采集芯片的温度,此传感器精度不高。不适合用于实际的工程中,这里只为演示AD采样。要使用内部的温度采集我们需要使用AD采样,所以我们需要先来了解CC254x的AD功能。在后续课程有对ADC的详细说明。

  ADC结构图如下所示:

  

  ADC控制寄存器1如下图所示:

  

  我们使用手动触发的方式进行AD采样,所以STSEL = 11B,最低两位始终为1,最终ADCCON1=0x33。

  ADC控制寄存器3如图所示:

  

  ADC参考电压使用内部电压,采用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位精度,启动转换

  while (!(ADCCON1 & 0x80)); // 等待转换完成

  // 读取采样结果

  reading = (int16)(ADCL);

  reading |= (int16)(ADCH 《《 8);

  reading 》》= 4; // 丢弃低位

  Result += reading; // 累加

  }while(i++ 《 10); // 连续采样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的误差太大导致的,这里只做一个简单的实验,如果需要工程应用,建议外接温度传感器。把手放在芯片上可以看到温度在上升。温度采集结果如下图所示:

  第九节 五向按键

  五向按键,也就是我们平常所见的摇杆内部构造,五向按键有上下左右和中间五个按键值,MT254xboard上的五向按键检测电路由馒头科技自主设计,而不是Ti的设计,采用一个外部中断和一个AD检测口来完成按键的检测。

  

  由原理图可知当我们按下不同的键值时在JOY_CHK将会产生一个上升沿,并且在JOY_AD口有不同的电压。我们只需要在JOY_CHK的外部中断中读取JOY_AD的电压即可识别不同的按键。

  外部中断和AD采用在前面已经讲过了,这里只需要拿来用就可以了。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 三态

  P0SEL &= ~0X80; // 设置为IO功能

  P0DIR &= ~0X80; // 设置为输入功能

  P0IEN |= 0X80; // P0.7 设置为中断方式

  PICTL &= ~0X80; // 上升沿触发

  IEN1 |= 0X20; // 允许P0口中断

  P0IFG = 0x00; // 清除中断标志位

  EA = 1; // 开总中断

  while(1)

  {

  if(KeyStat) // 按键按下

  {

  KeyValue = GetKeyValue();

  switch ( KeyValue )

  {

  case KEY_UP :

  sprintf(LCDBuf, “\tUP”);

  break;

  case KEY_DOWN :

  sprintf(LCDBuf, “\tDown”);

  break;

  case KEY_LEFT :

  sprintf(LCDBuf, “\tLeft”);

  break;

  case KEY_CENTER :

  sprintf(LCDBuf, “\tCenter”);

  break;

  case KEY_RIGHT :

  sprintf(LCDBuf, “\tRight”);

  break;

  default:

  break;

  }

  KeyStat =0;

  LCD12864_DisStr(3, LCDBuf);

  }

  }

  return 0;

  }

  

  按键的检测通过电压来区分。

  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;

  }

  使用五向按键效果如下所示:

  第十节 蜂鸣器

  蜂鸣器是一种常用的报警设备,常用的蜂鸣器有无源和有源两种类型,无源蜂鸣器需要用一定频率的方波驱动,从而发出不同频率的声音。而有源蜂鸣器只需要通电就会发出固定频率的声音,MT254xboard开发板上的蜂鸣器用的是无源蜂鸣器,因此我们需要用一定频率的方波来驱动。

  

  硬件驱动方面,我们这里使用了PNP三极管来驱动蜂鸣器,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;

  }

  这个函数是通过传入参数的形式,使P2.0口发出指定频率的方波。

  void Buzzer_Stop(void)

  {

  T4CTL &= ~0x10; // Stop timer 3

  P2SEL &= ~0x01;

  P2_0 = 1;

  }

  这个函数是使蜂鸣器停止,主要有三个动作,停止定时器,将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; // 下降沿触发

  IEN1 |= 0X20; // 允许P0口中断

  P0IFG = 0x00; // 清除中断标志位

  EA = 1; // 开总中断

  sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按键计数

  LCD12864_DisStr(3, LCDBuf);

  while(1)

  {

  if(KEY_DOWN == NewKeyValue) // 按键按下

  {

  SoftWaitUs(25000); // 延时防抖

  if((P0&0X01) == 0X00) // 再次确认按键是否按下

  {

  sprintf(LCDBuf, “ Key Count : %d”, KeyCnt++); // 按键计数

  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的方波。


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
基于RFID技术的游客游迹跟踪与追溯系统