CC2540是一款完全兼容8051内核,并集成了2.4GHz射频收发器的无线射频单片机。通过采用BLE(蓝牙低功耗协议)构成一款高性价比、低功耗的SOC(片上系统)解决方案,主要运用于蓝牙低功耗应用,例如可穿戴设备、医疗监测设备等。
CC2540拥有3个不同的存储器访问总线:
只有一个芯片是不能够实现各种复杂的项目的,还需要更多的外围电路和外部资源配合,在本次BLE协议栈实验学习过程中,使用的是江苏学蠡信息科技提供的LPTD006液晶传感器板与核心板组合的方式呈现,如下图所示:
整个开发板采用直插方式,将核心板、传感器模块与液晶底板进行连接,可以实现一板多用、灵活插拔、快速组合的效果,其中左侧为CC2540核心板,核心板中包括一个陶瓷巴伦、陶瓷天线、一个32MHz晶振以及一个32.768KHz晶振,本核心板可以实现CC2540的休眠以及唤醒操作,因此在本核心板上需要一个32.768KHz晶振。
下方就是大家比较熟悉的液晶传感器底板了,比较在之前的NB-IOT以及LoRa相关实验中都有介绍到,这里就不再多说。
CC2540虽然主要运用于低功耗蓝牙需求场景中,但在这个的前提是,CC2540是一款完全兼容8051内核的单片机,因此想要使用CC2540进行蓝牙方面的操作之前,需要先熟悉CC2540仅仅作为单片机的操作,例如CC2540的UART、ADC、定时器等基础资源。在本实验环节中,学蠡信息科技提供的实验源码都是在无BLE协议栈的状态下进行编程,其目的就是让大家能够把 CC2540 当做一颗普通的单片机使用。
在本实验环节,所使用的IDE为 IAR for 8051 8.10版本即IAR Embedded Workbench,各位可以自行寻找资源下载并安装。
液晶传感器底板中的OLED显示屏使用IIC方式与BLE核心板进行连接,对应的MCU引脚为P1_2和P1_3,OLED的电路原理图如下所示:
之前在对CC2540介绍的时候说过,CC2540不支持IIC,需要使用IIC的时候只能使用IO口进行模拟,因此这里的P1_2为IIC的SCL,P1_3为IIC的SDA。
具体实现步骤:
定义OLED显示所需要的宏
在这里进行的预定义的优先级要高于在代码中使用“#define”的定义,如果需要使用显示屏,则先保证HAL_LCD可用同时支持LCD,这里为什么是LCD而不是OLED呢,因为在TI提供的官方SmartRF05EB 开发板中使用的是LCD显示屏,而学蠡信息科技的开发板能够兼容SmartRF05EB 开发板,其中只是将LCD更换为OLED可以以更小的面积实现更好的效果,因此需要OLED显示的时候在进行宏预定义的时候需要打开“LCD”。
不进行这一步,将无法实现效果(显示屏压根不鸟你的)
初始化
请看下方电路原理图,因为显示屏的电路设计,给显示屏增加了一个三极管开关,如果需要显示屏工作则需要P2_0引脚输出一个高电平来打开,因此初始化工作除了发送对应的启动时序外还需要一个打开显示屏的“开关”的操作,并且该操作需要在第一位。
代码:
static void OledPortInt( void )
{
//VCC_33 电源开关
IO_DIR_PORT_PIN1(2, 0, IO_OUT);
//打开VCC_33 电源
P2_0 = 1;
HalLcd_HW_WaitUs(30000);
OLED_SDA_SEL &= ~OLED_SDA_BIT; //General-purpose I/O
OLED_SDA_DIR |= OLED_SDA_BIT; //OutPut
OLED_SCL_SEL &= ~OLED_SCL_BIT; //General-purpose I/O
OLED_SCL_DIR |= OLED_SCL_BIT; //OutPut
OLED_SDA_HIGH();
OLED_SCL_HIGH();
}
准备字模,用于显示
/*全体ASCII 列表:5x7点阵库*/
const uint8 ascii_table_5x7[95][5]={
0x00,0x00,0x00,0x00,0x00,//space
0x00,0x00,0x4f,0x00,0x00,//!
0x00,0x07,0x00,0x07,0x00,//"
0x14,0x7f,0x14,0x7f,0x14,//#
0x24,0x2a,0x7f,0x2a,0x12,//$
0x23,0x13,0x08,0x64,0x62,//%
0x36,0x49,0x55,0x22,0x50,//&
0x00,0x05,0x07,0x00,0x00,//]
0x00,0x1c,0x22,0x41,0x00,//(
0x00,0x41,0x22,0x1c,0x00,//)
0x14,0x08,0x3e,0x08,0x14,//*
0x08,0x08,0x3e,0x08,0x08,//+
0x00,0x50,0x30,0x00,0x00,//,
0x08,0x08,0x08,0x08,0x08,//-
0x00,0x60,0x60,0x00,0x00,//.
0x20,0x10,0x08,0x04,0x02,///
0x3e,0x51,0x49,0x45,0x3e,//0
0x00,0x42,0x7f,0x40,0x00,//1
0x42,0x61,0x51,0x49,0x46,//2
0x21,0x41,0x45,0x4b,0x31,//3
0x18,0x14,0x12,0x7f,0x10,//4
0x27,0x45,0x45,0x45,0x39,//5
0x3c,0x4a,0x49,0x49,0x30,//6
0x01,0x71,0x09,0x05,0x03,//7
0x36,0x49,0x49,0x49,0x36,//8
0x06,0x49,0x49,0x29,0x1e,//9
0x00,0x36,0x36,0x00,0x00,//:
0x00,0x56,0x36,0x00,0x00,//;
0x08,0x14,0x22,0x41,0x00,//<
0x14,0x14,0x14,0x14,0x14,//=
0x00,0x41,0x22,0x14,0x08,//>
0x02,0x01,0x51,0x09,0x06,//?
0x32,0x49,0x79,0x41,0x3e,//@
0x7e,0x11,0x11,0x11,0x7e,//A
0x7f,0x49,0x49,0x49,0x36,//B
0x3e,0x41,0x41,0x41,0x22,//C
0x7f,0x41,0x41,0x22,0x1c,//D
0x7f,0x49,0x49,0x49,0x41,//E
0x7f,0x09,0x09,0x09,0x01,//F
0x3e,0x41,0x49,0x49,0x7a,//G
0x7f,0x08,0x08,0x08,0x7f,//H
0x00,0x41,0x7f,0x41,0x00,//I
0x20,0x40,0x41,0x3f,0x01,//J
0x7f,0x08,0x14,0x22,0x41,//K
0x7f,0x40,0x40,0x40,0x40,//L
0x7f,0x02,0x0c,0x02,0x7f,//M
0x7f,0x04,0x08,0x10,0x7f,//N
0x3e,0x41,0x41,0x41,0x3e,//O
0x7f,0x09,0x09,0x09,0x06,//P
0x3e,0x41,0x51,0x21,0x5e,//Q
0x7f,0x09,0x19,0x29,0x46,//R
0x46,0x49,0x49,0x49,0x31,//S
0x01,0x01,0x7f,0x01,0x01,//T
0x3f,0x40,0x40,0x40,0x3f,//U
0x1f,0x20,0x40,0x20,0x1f,//V
0x3f,0x40,0x38,0x40,0x3f,//W
0x63,0x14,0x08,0x14,0x63,//X
0x07,0x08,0x70,0x08,0x07,//Y
0x61,0x51,0x49,0x45,0x43,//Z
0x00,0x7f,0x41,0x41,0x00,//[
0x02,0x04,0x08,0x10,0x20,// 斜杠
0x00,0x41,0x41,0x7f,0x00,//]
0x04,0x02,0x01,0x02,0x04,//^
0x40,0x40,0x40,0x40,0x40,//_
0x01,0x02,0x04,0x00,0x00,//`
0x20,0x54,0x54,0x54,0x78,//a
0x7f,0x48,0x48,0x48,0x30,//b
0x38,0x44,0x44,0x44,0x44,//c
0x30,0x48,0x48,0x48,0x7f,//d
0x38,0x54,0x54,0x54,0x58,//e
0x00,0x08,0x7e,0x09,0x02,//f
0x48,0x54,0x54,0x54,0x3c,//g
0x7f,0x08,0x08,0x08,0x70,//h
0x00,0x00,0x7a,0x00,0x00,//i
0x20,0x40,0x40,0x3d,0x00,//j
0x7f,0x20,0x28,0x44,0x00,//k
0x00,0x41,0x7f,0x40,0x00,//l
0x7c,0x04,0x38,0x04,0x7c,//m
0x7c,0x08,0x04,0x04,0x78,//n
0x38,0x44,0x44,0x44,0x38,//o
0x7c,0x14,0x14,0x14,0x08,//p
0x08,0x14,0x14,0x14,0x7c,//q
0x7c,0x08,0x04,0x04,0x08,//r
0x48,0x54,0x54,0x54,0x24,//s
0x04,0x04,0x3f,0x44,0x24,//t
0x3c,0x40,0x40,0x40,0x3c,//u
0x1c,0x20,0x40,0x20,0x1c,//v
0x3c,0x40,0x30,0x40,0x3c,//w
0x44,0x28,0x10,0x28,0x44,//x
0x04,0x48,0x30,0x08,0x04,//y
0x44,0x64,0x54,0x4c,0x44,//z
0x08,0x36,0x41,0x41,0x00,//{
0x00,0x00,0x77,0x00,0x00,//|
0x00,0x41,0x41,0x36,0x08,//}
0x04,0x02,0x02,0x02,0x01,//~
};
模拟IIC相关功能函数
因为CC2540并不支持IIC,因此只能通过IO口的高低电平输出进行模拟,因此IIC操作显示屏的各项操作都写成一个个功能函数。
/**********************************************
//IIC Start
**********************************************/
void IIC_Start(void)
{
OLED_SCLK_Set() ;
OLED_SDIN_Set();
OLED_SDIN_Clr();
OLED_SCLK_Clr();
}
/**********************************************
//IIC Stop
**********************************************/
void IIC_Stop(void)
{
OLED_SCLK_Set() ;
OLED_SDIN_Clr();
OLED_SDIN_Set();
}
void IIC_Wait_Ack(void)
{
OLED_SCLK_Set() ;
OLED_SCLK_Clr();
}
/**********************************************
// IIC Write byte
**********************************************/
void Write_IIC_Byte(uint8 IIC_Byte)
{
uint8 i;
uint8 m,da;
da=IIC_Byte;
OLED_SCLK_Clr();
for(i=0;i<8;i++)
{
m=da;
// OLED_SCLK_Clr();
m=m&0x80;
if(m==0x80)
{OLED_SDIN_Set();}
else OLED_SDIN_Clr();
da=da<<1;
OLED_SCLK_Set();
OLED_SCLK_Clr();
}
}
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(uint8 IIC_Command)
{
IIC_Start();
Write_IIC_Byte(0x78); //Slave address,SA0=0
IIC_Wait_Ack();
Write_IIC_Byte(0x00); //write command
IIC_Wait_Ack();
Write_IIC_Byte(IIC_Command);
IIC_Wait_Ack();
IIC_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(uint8 IIC_Data)
{
IIC_Start();
Write_IIC_Byte(0x78); //D/C#=0; R/W#=0
IIC_Wait_Ack();
Write_IIC_Byte(0x40); //write data
IIC_Wait_Ack();
Write_IIC_Byte(IIC_Data);
IIC_Wait_Ack();
IIC_Stop();
}
void OLED_WR_Byte(uint8 dat,uint8 cmd)
{
if(cmd)
{
Write_IIC_Data(dat);
}
else
{
Write_IIC_Command(dat);
}
}
void OLED_Set_Pos(uint8 x, uint8 y)
{
OLED_WR_Byte(0xb0+y,OLED_CMD);
OLED_WR_Byte((((x+2)&0xf0)>>4)|0x10,OLED_CMD);
OLED_WR_Byte(((x+2)&0x0f),OLED_CMD);
}
void OLED_ShowChar(uint8 x,uint8 y,uint8 chr)
{
uint8 c=0,i=0;
c=chr-' ';//得到偏移后的值
if(x>Max_Column-1){x=0;y=y+2;}
OLED_Set_Pos(x,y);
for(i=0;i<8;i++)
OLED_WR_Byte(ascii_table_8x16[c*16+i],OLED_DATA);
OLED_Set_Pos(x,y+1);
for(i=0;i<8;i++)
OLED_WR_Byte(ascii_table_8x16[c*16+i+8],OLED_DATA);
}
void OLED_Clear(void)
{
uint8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD);
OLED_WR_Byte (0x02,OLED_CMD);
OLED_WR_Byte (0x10,OLED_CMD);
for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
}
}
主函数调用
最后就是在main函数中进行调用操作了,现实的效果为"JiangSuXueLi",同时下方会有一个数字不断自增。
主函数代码如下:
```c
int main()
{
uint8 value[20];
uint32 count=0;
HAL_BOARD_INIT();
HalLcd_HW_Init();
HalLcd_HW_WriteLine(1,“JiangSuXueLi”);
while(1){
IntToStr(value,count);
HalLcd_HW_WriteLine(2,(char *)value);
HalHW_WaitMS(1000);
count++;
}
}
```
在CC2540中一共有四个定时器,其中Timer1和Timer2为16位定时器,Timer3和Timer4为8位定时器,其中Timer2是射频专用定时器。在本实验中使用Timer1进行实验。
具体实现步骤:
预定义宏打开显示屏并对显示屏进行初始化
因为本实验依然需要用到OLED显示屏,所以第一步还是对OLED的相关操作,原理图与相关代码就直接参考OLED显示实验中的即可
初始化Timer1
配置Timer1为128分频、自动重装模式,代码中的BV为位操作
void InitTimer1()
{
T1CTL |= BV(2) | BV(3); //128分频,16MHz 128分频后,频率是16M/128==125000,
/*
自动重装0x0000到0xFFFF循环,总共循环65536次,每次耗时1/125000秒,
这样65536/125000=0.524288秒,也就是说没产生一次中断flag,就是0.5秒左右*/
T1CTL |= BV(0);
}
main函数调用
在main函数中,每触发一次中断就进行数字加一操作,并在显示屏第二行显示,时间间隔为0.5s
显示效果如下:
int main()
{
uint8 value[20];
uint32 count=0;
HAL_BOARD_INIT();
HalLcd_HW_Init();
InitTimer1();
HalLcd_HW_WriteLine(1,"JiangSuXueLi");
while(1)
{
if(IRCON & BV(1))//Timer1 flag
{
IRCON=0;//清除标记
IntToStr(value,count);
HalLcd_HW_WriteLine(2,(char *)value);
count++;
}
}
}
CC2540一共拥有两个串口:UART0 和UART1,UART0 和UART1都是串行通信接口,都可以分别运行于异步UART模式或者同步SPI模式,两个串口在功能上是一样的,可以通过设置在单独的IO口上。
UART0 对应的IO引脚关系:
UART1 对应的IO引脚关系:
CC2540配置串口的步骤:
UART0的相关寄存器如下:
具体操作步骤:
1. 配置串口寄存器
void InitUart(void)
{
P0SEL |= BV(2) | BV(3);//配置P0.2和P0.3为外设,非GPIO
U0CSR |= BV(7); //配置当前为UART,非SPI
U0GCR |= 11; //根据上述波特率设置表格设置115200波特率
U0BAUD |= 216;// 根据上述波特率设置表格设置115200波特率
UTX0IF = 0;//位寄存器,直接操作,清除中断标志
U0CSR |= BV(6);//允许接收数据
IEN0 |= BV(2);//打开接收中断
EA=1;//打开总中断
}
2.串口发送数据
void UartSendString(int8 *Data, uint16 len)
{
uint16 i;
for(i=0; i<len; i++)
{
U0DBUF = *Data++;
while(UTX0IF == 0);
UTX0IF = 0;
}
}
void UartSendByte(int8 byte)
{
U0DBUF = byte;
while(UTX0IF == 0);
UTX0IF = 0;
}
3.串口接收数据
当串口0触发接收中断后,直接将数据发送回去,一次接收一个字节
#pragma vector = URX0_VECTOR
__interrupt void UART0_ISR(void)
{
URX0IF = 0; // 清中断标志 ;
UartSendByte(U0DBUF); //收到后立即发送出去
}
4.main函数调用
void main(void)
{
CLKCONCMD &= ~0x40; //设置系统时钟源为32MHZ晶振
while(CLKCONSTA & 0x40); //等待晶振稳定
CLKCONCMD &= ~0x47; //设置系统主时钟频率为32MHZ
InitUart(); //调置串口相关寄存器
while(1)
{
}
}
最终实现效果:
在进行串口通信的时候务必要将拨码开关K3拨到右侧,不然串口助手会没有内容显示。
接下来我们在串口调试助手中发送什么就会接收到什么数据。
CC2540一共提供了4种电源工作模式:
在本实验中,采用PM2工作模式,并使用SLEEPTIMER睡眠定时器实现5秒休眠。
与实现睡眠唤醒有关的寄存器如下:
同时还需要睡眠定时器中断(ST0-ST3)对睡眠时间进行控制,需要注意的是读取睡眠定时器的当前计数值,顺序必须遵循:读ST0 →读ST1 →读ST2。而写入睡眠定时器的比较值,顺序必须遵循:写ST2 →写ST1 →写ST0。当比较值和计数值相同的时候触发中断。
具体实现步骤:
配置休眠定时寄存器
void InitSleepTimer(void)
{
ST2 = 0X00;
ST1 = 0X0F;
ST0 = 0X0F;
EA = 1; //开中断
STIE = 1; //睡眠定时器中断使能 0: 中断禁止 1: 中断使能
STIF = 0; //睡眠定时器中断标志 0: 无中断未决 1: 中断未决
}
配置休眠时间
void SetSleepTime(uint32 sec)
{
uint32 sleepTimer = 0;
//获取当前计数
sleepTimer |= ST0;
sleepTimer |= (uint32)ST1 << 8;
sleepTimer |= (uint32)ST2 << 16;
sleepTimer += ((uint32)sec * (uint32)32768);
//设置新的比较数值,计数到达后,产生中断。注意增加的计数一定要大于5
ST2 = (uint8)(sleepTimer >> 16);
ST1 = (uint8)(sleepTimer >> 8);
ST0 = (uint8) sleepTimer;
}
工作模式设置功能函数
void PowerMode(uint8 mode)
{
if(mode>0 && mode < 4)
{
SLEEPCMD |= mode; //设置系统睡眠模式
PCON |= BV(0); //进入PowerMode睡眠模式
}else
PCON &= ~BV(0); //从PowerMode恢复
}
main函数调用
在main函数中开始3S计时,计时结束后关闭屏幕显示,然后设置睡眠时间为5秒,5秒后系统自动唤醒,同时工作模式变为PM2。
void main(void)
{
uint8 i=0;
HAL_BOARD_INIT();
InitSleepTimer(); //初始化休眠定时器
while(1)
{
HalLcd_HW_Init();
HalLcd_HW_WriteLine(1,"time down count start");
for (i=0; i<3; i++) //倒计时3次提醒用户将进入睡眠模式
{
if(i==0)
HalLcd_HW_WriteLine(2,"3");
else if(i==1)
HalLcd_HW_WriteLine(2,"2");
else if(i==2)
HalLcd_HW_WriteLine(2,"1");
DelayMS(1000);
}
//熄灭lcd
P1DIR &= ~(0x01<<(2));
P1DIR &= ~(0x01<<(3));
P2_0 = 0;
/*
设置睡眠时间,睡眠5秒后唤醒系统,系统一旦进入睡眠模式后,程序将终止运行
直到睡眠定时到*/
SetSleepTime(5);
PowerMode(2); //进入睡眠模式PM2
}
}
“看门狗”对于单片机初学者来说是一个及其抽象的动物,怎么单片机里面还有“狗”,什么东西这么嚣张可以当“狗”看门。其实看门狗的原理和职责很简单,看门狗可以理解为一个定时器,当程序正常运行时并不会触发计时器,但是一旦程序出现问题比如跑飞或者是出现其他异常则会触发看门狗开始计时,一旦达到预定值则会触发系统的重启,是单片机的一个重要保护单元。
实现步骤:
配置看门狗寄存器
void InitWatchdog(void)
{
WDCTL = 0x00; //打开IDLE
WDCTL |= BV(3); //开启watchdog模式
WDCTL &= ~(BV(0)|BV(1));//1s复位
}
喂狗(让看门狗不触发)
void FeetDog(void)
{
WDCTL = 0xA0; //喂狗顺序
WDCTL = 0x50;
}
main函数调用
先对系统的时钟源进行设定,接下来就是初始化串口和看门狗设置,要实现的功能为在1S内不断通过串口助手输入“1”,才可以在串口打印“Running”,否则串口会一直输出“reset”。
void main(void)
{
CLKCONCMD &= ~0x40; //设置系统时钟源为32MHZ晶振
while(CLKCONSTA & 0x40); //等待晶振稳定
CLKCONCMD &= ~0x47; //设置系统主时钟频率为32MHZ
InitUart();
InitWatchdog();
UartSendString("reset\r\n",7);
while(1)
{
DelayMS(1000);
UartSendString("running\r\n",9);
}
}
实现效果:
间隔0.5S发送1,串口持续打印“running”。
Adc 是单片机获取模拟量的重要方式,在此为减少按键所占用的 io 口将演示如果用 1 路 adc 获取多个按键值。
在这我们需要用到液晶传感器底板的另一个硬件资源——五向按键,其可以实现上下左右中五个方向的按键效果,其电路原理图如下所示:
配置ADC寄存器
/*五向按键用到的ADC部分初始化*/
void JOYSTICK_ADC_Init()
{
ADCCON3 |= 0x80;//设置参考电压,这里为选择AVDD5
ADCCON3 |= 0x00;//设置分辨率为8位
ADCCON1 |= 0x30;//设置ADC启动方式,这里为软件启动,当st为1时 开始ADC转换
}
//adc采用通道选择,从0~7,分别对应AIN0 ~ AIN7
//采样通道 每次采样前均需重新设置!
void JOYSTICK_ADC_SelectChanel(uint8 ch)
{
ADCCON3 |= ch;//select ainx as input
//APCFG |= BV(ch); //不是必须的配置,又名ADCCFG
}
/*ADC启动和停止函数
s为true时开始采样
否则停止采样
*/
void JOYSTICK_ADC_Start(bool s)
{
if(s){
ADCCON1 |= BV(6);
}else{
ADCCON1 &= ~BV(6);
}
}
/*ADC是否采样结束
结束后返回非0数
*/
uint8 JOYSTICK_ADC_Busy()
{
return (ADCCON1&0x80);
}
读取ADC值
/*读取ADC采样值*/
uint16 JOYSTICK_ADC_Get()
{
//uint16 t = ((uint16)(ADCH)<<8)+ADCL;
uint16 t;
t=(uint16) (ADCL);
t|= (uint16) (ADCH << 8);
t>>=8; //8为分辨率,得到的值右移8位,即除以256
return t;
}
void JOYSTICK_POLL(void)
{
//设置本次的采样通道
JOYSTICK_ADC_SelectChanel(6);
//开始采样
JOYSTICK_ADC_Start(1);
//等待转换结束
while(!JOYSTICK_ADC_Busy());
//读取采样结果
JOYSTICK_Value = JOYSTICK_ADC_Get();
}
对读取的值进行判断比对进行按键绑定
在main函数通过While循环不断将采样电压值与预设值进行比对,实现不同方向的按键与其电压值进行绑定。
int main()
{
HAL_BOARD_INIT();
HalLcd_HW_Init();
JOYSTICK_ADC_Init();
JOYSTICK_ADC_Get();
HalLcd_HW_WriteLine(HAL_LCD_LINE_1, "JOYSTICK_TEST");
while(1){
JOYSTICK_POLL();
{
/*不同的采样电压对应不同的按键*/
if ((JOYSTICK_Value >= (77-8)) && (JOYSTICK_Value <= (77+8)))
{
HalLcd_HW_WriteLine(HAL_LCD_LINE_2,"UP");
}
else if ((JOYSTICK_Value >= (97-4)) && (JOYSTICK_Value <= (97+4)))
{
HalLcd_HW_WriteLine(HAL_LCD_LINE_2,"DOWN");
}
else if ((JOYSTICK_Value >= (105-3)) && (JOYSTICK_Value <= (105+3)))
{
HalLcd_HW_WriteLine(HAL_LCD_LINE_2,"LEFT");
}
else if ((JOYSTICK_Value >= (110-2)) && (JOYSTICK_Value <= (110+2)))
{
HalLcd_HW_WriteLine(HAL_LCD_LINE_2,"RIGHT");
}
else if ((JOYSTICK_Value >= (250)) || (JOYSTICK_Value <= (5)))
{
HalLcd_HW_WriteLine(HAL_LCD_LINE_2,"CENTER");
}
else
HalLcd_HW_WriteLine(HAL_LCD_LINE_2," ");
}
}
}
最终实现效果为往不同方向按下按键显示屏上会显示当前按键的方向:
该实验为本环节的最后一个实验,毕竟CC2540作为一个单片机,高低要连接一个传感器读一读它的数据是不是。
这里用的是温度光敏蜂鸣器温湿度传感器
电路原理图如下图所示:
实现步骤:
配置ADC寄存器
void HalAdcInit (void)
{
volatile uint8 tmp;
ADCCON1 = 0x30 | 0x0c| 0x03;
ADCCON2 = 0x80 | 0x00 | 0x0f;
/*
* After reset, the first ADC reading of the extra conversion always reads GND level.
* We will do a few dummy conversions to bypass this bug.
*/
tmp = ADCL; /* read ADCL,ADCH to clear EOC */
tmp = ADCH;
ADCCON3 = 0x80 | 0x00 | 0x0c;
while ((ADCCON1 &0x80) != 0x80); /* Wait for conversion */
tmp = ADCL; /* read ADCL,ADCH to clear EOC */
tmp = ADCH;
ADCCON3 = 0x80 | 0x00 | 0x0c;
while ((ADCCON1 &0x80) != 0x80); /* Wait for conversion */
tmp = ADCL; /* read ADCL,ADCH to clear EOC */
tmp = ADCH;
}
初始化传感器
void Sensor_Init(void)
{
P1DIR |= 0x04; //打开电源
P1 &=~0x04;
//初始化TC77引脚
P1DIR &= ~0x80;
P1DIR |= 0x30;
//初始化光敏电阻引脚
P0DIR &= ~0x02;Lingt
P0INP |= 0x02;
HalAdcInit();
//初始化蜂鸣器引脚
P1DIR |= 0x40;BEEP
P1_6 = 1;//OFF
}
读取传感器数据
int8 ReadTc77(void)
{
uint16 temp=0;
uint8 i;
MISO = 1;
SCK = 0;
CS_TC77 = 0;
for(i=0; i<16; i++)
{
temp <<= 1;
SCK = 1;
asm("nop");
if(MISO)temp++;
SCK = 0;
asm("nop");
}
CS_TC77 = 1;
i = temp >> 7;
return i;
}
uint16 HalAdcRead (uint8 channel, uint8 resolution)
{
int16 reading = 0;
uint8 i, resbits;
uint8 adctemp;
volatile uint8 tmp;
uint8 adcChannel = 1;
if (channel < 8)
{
for (i=0; i < channel; i++)
{
adcChannel <<= 1;
}
}
/* Enable channel */
ADCCFG |= adcChannel;
/* Convert resolution to decimation rate */
switch (resolution)
{
case 0x01:
resbits = 0x00;
break;
case 0x02:
resbits = 0x10;
break;
case 0x03:
resbits = 0x20;
break;
case 0x04:
default:
resbits = 0x30;
break;
}
/* read ADCL,ADCH to clear EOC */
tmp = ADCL;
tmp = ADCH;
/* Setup Sample */
adctemp = ADCCON3;
adctemp &= ~(0x0f | 0x30 | 0xc0 );
adctemp |= channel | resbits | 0x80;
/* writing to this register starts the extra conversion */
ADCCON3 = adctemp;
/* Wait for the conversion to be done */
while (!(ADCCON1 & 0x80));
/* Disable channel after done conversion */
ADCCFG &= (adcChannel ^ 0xFF);
/* Read the result */
reading = (int16) (ADCL);
reading |= (int16) (ADCH << 8);
/* Treat small negative as 0 */
if (reading < 0)
reading = 0;
switch (resolution)
{
case 0x01:
reading >>= 8;
break;
case 0x02:
reading >>= 6;
break;
case 0x03:
reading >>= 4;
break;
case 0x04:
default:
break;
}
return ((uint16)reading);
}
uint8 ReadSensorAdc(uint8 channel)
{
uint8 temp;
temp = HalAdcRead(channel,01);
return temp;
}
main函数展示
int main()
{
int8 temperature,light;
HAL_BOARD_INIT();
HalLcd_HW_Init();
Sensor_Init();//传感器初始化
uint8 lightBuf[10];
uint8 tempBuf[10];
while(1)
{
//采集温度
temperature = ReadTc77();
//采集光敏电阻
light = ReadSensorAdc(1);
//IntToStr(tempBuf,temperature);
//IntToStr(lightBuf,light);
sprintf((char *)&tempBuf,"T:%d",temperature);
sprintf((char *)&lightBuf,"P:%d",light);
HalLcd_HW_WriteLine(HAL_LCD_LINE_1, (char *)&tempBuf);
HalLcd_HW_WriteLine(HAL_LCD_LINE_2, (char *)&lightBuf);
HalHW_WaitMS(1000);
beep(1);
}
}