CC2530介绍:
CC2530 结合了领先的RF 收发器的优良性能,业界标准的增强型8051 CPU,系统内可编程闪存,8-KB RAM 和许多其它强大的功能。CC2530 有四种不同的闪存版本:CC2530F32/64/128/256,分别具有32/64/128/256KB 的闪存。CC2530 具有不同的运行模式,使得它尤其适应超低功耗要求的系统。运行模式之间的转换时间短进一步确保了低能源消耗。
模块介绍:
- IAR软件创建工程
- IO端口
- 外部中断
- 定时/计数器
- UART串口
- ADC转换
- 看门狗
一、使用IAR建立工程
- 先创建一个空的文件夹在D盘,并命名(如LED-TEST)
- 单击Project,选择Create New Project
- 选中Empty project,点击OK
- 输入文件名,将工程保存在已建立的文件夹中
- 点击File--->>New--->>File创建一个新的.c文件
- 点击File--->>Save保存.c文件
- 点击File--->>SaveWokespace保存工作区
- 鼠标右键Workspace的文件夹点击Add,将.c文件添加进工作区
- 右键单击Files,选择option,点击Device后的...,打开TexasInstruments文件夹,并选择CC2530F256
- 最后点击Debugger,选择Driver框的Texas Instruments
ps:配置以上只适合仿真,烧录需要另外配置Linker
最后输入代码,点击编译出现0错误0警告
#include<ioCC2530.h>
void main()
{
while(1)
{
}
}
二、IO端口
CC2530单片机采用QFN40封装,拥有40个引脚,21个IO口,P0和P1端口组各有8个IO口,P2端口只有5个IO口。
IO口可通过程序控制输入输出模式,输出模式指对外输出高低电平来控制相关电路的高低电平;输入模式指直接读取IO端口的逻辑电平
输入端口的三种模式
- 上拉模式(使用按键)
- 下拉模式
- 三态模式(ADC转换使用)
IO端口的寄存器
具体使用流程图
操控寄存器P1SEL和P1DIR来实现D3灯的闪烁
#include<ioCC2530.h>
#define uchar unsigned char
#define uint unsigned int
#define D3 P1_0
#define D4 P1_1
#define D5 P1_3
#define D6 P1_4
//延时函数
void delay(uint t)
{
while(t--);
}
//初始化led灯的端口
void Init_LED()
{
//将led的端口设置为通用IO口 清零操作
P1SEL&=~0X1B;
//将led的端口设置为输出方向 置一操作
P1DIR|=0X1B;
//关闭led
P1&=~0X1B;
}
void main()
{
Init_LED();
while(1)
{
D3=1;
delay(60000);
D3=0;
delay(60000);
}
}
操控按键实现LED的的亮灭
#include<ioCC2530.h>
#define uchar unsigned char
#define uint unsigned int
#define D3 P1_0
#define D4 P1_1
#define D5 P1_3
#define D6 P1_4
#define SW1 P1_2
//延时函数
void delay(uint t)
{
while(t--);
}
//初始化IO口
void Init_IO()
{
//将led的端口设置为通用IO口 清零操作
P1SEL&=~0X1B;
//将led的端口设置为输出方向 置一操作
P1DIR|=0X1B;
//将P1_2口设置为通用IO口 0000 0100
P1SEL&=~0X04;
//P1_2口设置为输入方向
P1DIR&=~0X04;
//INP设置为带上拉
P1INP&=~0X04;
P2INP&=~0X20;
//关闭led
P1&=~0X1B;
}
void Scanf_Key()
{
//判断按键是否按下
if(SW1==0)
{
//按键消抖
delay(100);
//如果按键还处于按下的状态
if(SW1==0)
{
D3=~D3;
}
}
}
void main()
{
Init_IO();
while(1)
{
Scanf_Key();
}
}
三、中断
CC2530具有18个中断源
中断的相关概念:
(1)中断源:引起中断的原因(发出中断申请的来源),如外部中断,定时器中断,ADC中断。
(2)中断请求:中断源要求CPU提供服务的请求,当有中断请求时,对应的标志位会被置位
(3)中断服务函数,CPU响应后执行的响应处理程序。
(4)中断向量:中断服务函数的入口地址。
中断类型
1.外部中断:单片机外部产生的中断,如IO端口输入由高变低或由低变高,最常见的是按键中断
2.内部中断:如定时器Timer,串口Uart,模数转换ADC等。
使能端口组(寄存器IEN0,IEN1,IEN2)
每个中断源都有一个中断开关,要使用中断功能,就需要开启中断功能开关,
所使用的寄存器有IEN0,IEN1,IEN2;其中IEN0和IEN1可位寻址,IEN2不可位寻址(可位寻址是指可以单独对寄存器的位进行操作,而不可位寻址是指要中断使能的时候只能对整个寄存器进行操作)
如:对IEN1寄存器的P0IE中断使能时可以写成:P0IE=1;
而对IEN2寄存器的P1IE中断使能时可以写成:IEN2 |= 0X10;
IEN0寄存器
IEN1寄存器
IEN2寄存器
P0IEN和P1IEN寄存器
使能端口组后,还需要设置当前端口组中具体那几个端口有外部中断功能,将不需要的端口屏蔽掉,如使能P1_2端口可写成: P1IEN |=0X04;
设置中断触发方式
(1)高低电平触发
(2)上升沿下降沿边沿触发
要设置触发方式,则需要设置它的PICTL寄存器
如设置P1_2端口下降沿触发,则为PICTL|=0X02;
中断寄存器配置流程
通过按键中断控制跑马灯启停
#include<ioCC2530.h>
#define uchar unsigned char
#define uint unsigned int
#define D3 P1_0
#define D4 P1_1
#define D5 P1_3
#define D6 P1_4
#define SW1 P1_2
uchar F_LED = 1;
void delay(uint t){
while(t--){
while(F_LED==0);
}
}
void Led_Test(){
P1SEL&=~0X1B;
P1DIR|=0X1B;
P1|=0X1B;
P1&=~0X1B;
}
void Led_Running(){
while(1){
D4=1;
D3=0;
D6=0;
D5=0;
delay(60000);
D4=0;
D3=1;
D6=0;
D5=0;
delay(60000);
D4=0;
D3=0;
D6=1;
D5=0;
delay(60000);
D4=0;
D3=0;
D6=0;
D5=1;
delay(60000);
}
}
void Init_SW1(){
P1SEL&=~0X04;
P1DIR&=~0X04;
P1INP&=~0X04;
P2INP&=~0X40;
IEN2|=0X10;
P1IEN|=0X04;
PICTL|=0X02;
EA=1;
}
#pragma vector = P1INT_VECTOR
__interrupt void P1Int(){
if((P1IFG&0X04)==0X04){
if(F_LED==1){
F_LED=0;
}
else{
F_LED=1;
}
}
P1IFG&=~0X04; //对引脚清零
P1IF=0; //对整个外部中断引脚端口清零
}
void main(){
Led_Test();
Init_SW1();
while(1){
Led_Running();
}
}
每当端口组产生中断时,该端口组的中断标志位就会置一,该标志位需要手动软件清零。
四、定时/计数器
定时/计数器概念:
对时钟信号或外部输入信号进行计数,当达到设定要求的值产生溢出时便向CPU提出处理请求,实现定时计数功能的外设。
定时器功能:
(1)定时器功能
(2)计算器功能
(3)输入捕获
(4)输出比较
(5)PWM功能
CC2530具有5个定时器
(1)定时器1是一个16位定时器,能够计算65536个脉冲,支持输入捕获,输出比较,PWM功能,具有自由运行模式,模模式,正计数/倒计数模式三种工作模式,是CC2530功能最为齐全的定时器。
(2)定时器2用于算法提供定时,一般不使用.
(3)定时器3/4是8位定时器,能够计数256个脉冲,支持输入捕获,输出比较,具有自由运行模式,倒计数,模模式,正计数/倒计数模式四种工作模式,
(4)睡眠定时器,是一个24位正计数定时器,运行在32kHz的时钟频率下。主要用于设置进入和退出低功耗模式之间的周期。
定时器的工作模式
(1)自由运行模式:从0X0000开始计数到0XFFFF时,重新载入从0开始计数
(2)模模式:与自由运行模式一样,但是可以自己设置最大值(溢出值)
(3)正计数/倒计数模式:从0X0000开始计数到最大值后,再从最大值计数到0X0000,。
使用定时器时需要初始化TxCTL
定时时间计算公式
设置定时时间需要向高八位TxCCxH和低八位TxCCxL寄存器写入值
代码
#include<ioCC2530.h>
#define uchar unsigned char
#define uint unsigned int
#define D3 P1_0
#define D4 P1_1
#define D5 P1_3
#define D6 P1_4
uint t1_count = 0; //计数器,每达到0.1s count++
void Init_Led(){
P1SEL&=~0X1B;
P1DIR|=0X1B;
//P1|=0X1B;
P1&=~0X1B;
}
void Init_T1(){
//设置高8位和低8位的值
T1CC0L=0XD4;
T1CC0H=0X30;
//模模式开启通道输出比较模式
T1CCTL0|=0X04;
//打开中断允许位
T1IE=1;
//打开总中断
EA=1;
//设置工作模式和时钟分频
T1CTL|=0X0E;
}
#pragma vector = T1_VECTOR
__interrupt void T1vector(){
//T1STAT&=~0X20;
t1_count++;
if((t1_count%10)==0){
D4=~D4;
}
if(t1_count==40){
D6=~D6;
t1_count=0;
}
}
void main(){
Init_Led();
Init_T1();
}
定时器初始化流程
五、UART串口
串口通讯:
指外设与计算机通过数据线,地线,时钟线按位进行数据传输的通信方式。
通信方式
(1)单工模式:一方发送,一方接收,信息只沿一个方向传输,使用一根数据线。
(2)半双工模式:使用一根数据线,既能发送又能接收,但不能同时收发,
(3)全双工模式:有两条线,既能发送也能接收。
波特率:每秒钟传输的数据位。
CC2530有两个串行接口USART0和USART1,分别能够运行于异步USART模式和同步SPI模式。
USART引脚映射
使用到的寄存器
波特率设置
PERCFG寄存器
串口初始化模板
示例代码
#include<ioCC2530.h>
#define D5 P1_3
#define uchar unsigned char
#define uint unsigned int
void delay(uint t){
while(t--);
}
void Init_LED(){
P1SEL&=~0X1B;
P1DIR|=0X1B;
P1|=0X1B;
delay(60000);
P1&=~0X1B;
}
//看门狗初始化为定时器模式且时间为1s;因为没有用到中断所以不需要打开中断使能
void Init_WDT(){
WDCTL=0X0C;
//IEN2|=0X20;
//EA=1;
}
//初始化串行口
void Init_UART(){
//IO引脚映射到备用位置1
PERCFG&=~0X01;
P0SEL|=0X0C;
//波特率设置为9600
U0BAUD=59;
U0GCR=8;
//两个寄存器的控制 控制寄存器和控制和状态寄存器
U0UCR|=0X80; //1000 0000 ; 清除单元
U0CSR|=0XC0; //1100 0000 ; 模式选择为UART模式,且打开接收使能
// UTX0IF=0;
//URX0IF=0;
//没有用到中断所以可以不打开
//IEN2|=0X04;
//URX0IE=1;
//EA=1;
}
//切换为32MHZ频率
void Init_CLK32(){
CLKCONCMD&=~0X40;
while(CLKCONSTA&0X40);
CLKCONCMD&=~0X07;
}
//发送字位函数
void UR0_SendByte(uchar dat){
U0DBUF=dat; //是UXBUF寄存器
while(UTX0IF==0); //UATR的中断标志位,要手动清零;等待UTX0IF=1时清零
UTX0IF=0;
}
//发送字符串
void UR0_SendString(uchar *str){ //参数字符串为指针
while(*str!='\0'){ //字符串最后一位是\0
UR0_SendByte(*str++); //调用发送字节的函数
}
}
void main(){
Init_CLK32();
Init_LED();
Init_WDT();
Init_UART();
while(1){
if(WDTIF==1){
WDTIF=0;
D5=1;
UR0_SendString("Hello Word!\r\n");
D5=0;
}
}
}
六、ADC转换
ADC是将输入的模拟信号转化为数字信号,如速度,光照,压力等,连续变化的物理量转化为相对于的电压和电流模拟信号。
模拟信号和数字信号
CC2530的ADC模块支持最高14位二进制的模拟数字转化,具有12位有效位,具有8个可配置通道,以及一个参考电压发生器。
使用到的寄存器
APCFG寄存器 (使用具体端口时需要开启)
ADCH和ADCL寄存器(ADC转化完成后将值存入此)
ADCCON1(当转换完成后ADCCON1第七位会置1)
ADCCON3(单次转换使用)
ADC转换后,获取的高八位值和低八位值需要进行处理
ADC_number=ADCH; //先获取高八位的值
ADC_number=(ADCH<<8)|ADCL; //将低八位的值装入
ADC_number=ADC_number>>5(取10位有效位)2^10=1023份
ADC_Vol=(3.3/1023)*ADC_number; //最后的值
ps:取有效位计算
具体代码
以查询方式来开启ADC采样
#include<ioCC2530.h>
#include<stdio.h>
#define D5 P1_3
#define uchar unsigned char
#define uint unsigned int
uint adc_number=0;
uchar str[128];
//初始化LED
void Init_LED(){
P1SEL&=~0X1B;
P1DIR|=0X1B;
P1|=0X1B;
P1&=~0X1B;
}
//看门狗设置为定时器模式 1s定时
void Init_WDT(){
WDCTL|=0X0C;
//IEN2|=0X20;
//EA=1;
}
//将系统时钟转为32MHZ
void Init_CLK(){
CLKCONCMD&=~0X40;
while(CLKCONSTA&0X40);
CLKCONCMD&=~0X07;
}
//初始化串口 UART模式 映射到UART0备用位置1 波特率设置为9600
void Init_UART0(){
PERCFG&=~0X01;
P0SEL|=0X0C;
U0BAUD=59;
U0GCR=8;
U0UCR|=0X80;
U0CSR|=0X0C;
UTX0IF=0;
URX0IF=0;
}
//写一个字位
void Send_Byte(uchar dat){
U0DBUF=dat;
while(UTX0IF==0);
UTX0IF=0;
}
//写一个字符串
void Send_String(uchar *str){
while(*str!='\0'){
Send_Byte(*str++);
}
}
//初始化ADC P0_0模拟io启用
void Init_ACD(){
APCFG|=0X01;
//ADCCON|=0XA0;
}
void Start_ADC_GetValue(){
D5=1;
ADCCON3|=0XA0; //1010 0000 AVDD5作为参考电压 256抽取率 AIN0位通道选择
while((ADCCON1&0X80)!=0X80); //读取ADCCON1寄存器,如果完成转换,第7位将被置1 1000 0000===》0x80 可以查询ADCIF标志位,但要软件清零
adc_number=ADCH;//将读取到的高八位传给adc_number
adc_number=(adc_number<<8 | ADCL); //将高八位左移8位,或上低8位得到读取到的16位值
adc_number=adc_number>>2; //低八位的1和0为无效位,所以要右移两位得到完整的值
sprintf((char *)str,"AIN0的采样结果为:%d\r\n",adc_number);
Send_String(str);
D5=0;
}
void main(){
Init_CLK();
Init_LED();
Init_WDT();
Init_UART0();
Init_ACD();
while(1){
if(WDTIF==1){
WDTIF=0;
Start_ADC_GetValue();
}
}
}
以中断方式获取ADC
#include<ioCC2530.h>
#include<stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define D5 P1_3
uint number=0;
uchar str[128];
void Init_LED(){
P1SEL&=~0X1B;
P1DIR|=0X1B;
P1&=~0X1B;
}
void Init_WDC(){
WDCTL|=0X0C;
}
void Init_CLK32(){
CLKCONCMD&=~0X40;
while(CLKCONSTA&0X40);
CLKCONCMD&=~0X07;
}
void Init_UART0(){
PERCFG&=~0X01;
P0SEL|=0X0C;
U0BAUD=59;
U0GCR=8;
U0UCR|=0X80;
U0CSR|=0XC0;
}
void Send_byte(uchar dat){
U0DBUF=dat;
while(UTX0IF==0);
UTX0IF=0;
}
void Send_String(uchar *str){
while(*str!='\0'){
Send_byte(*str++);
}
}
void Init_ADC(){
APCFG|=0X01;
ADCIE=1;
EA=1;
}
#pragma vector=ADC_VECTOR
__interrupt void ADC_Running(){
number=ADCH;
number=(number<<8)|ADCL;
number=number>>2;
sprintf((char *)str,"AIN0的采样结果是%d\r\n",number);
Send_String(str);
D5=0;
}
void main(){
Init_CLK32();
Init_LED();
Init_WDC();
Init_UART0();
Init_ADC();
while(1){
if(WDTIF==1){
WDTIF=0;
D5=1;
ADCCON3|=0XB0;
}
}
}
七、看门狗(Watch Dog Timer)
CC2530的看门狗定时器是一个安全功能,用于防止程序运行死锁或进入错误状态。看门狗定时器需要定期“喂狗”,也就是通过软件对其进行重置,以防止它超时并导致设备重启。
直接点理解看门狗就是个定时器,程序开始运行的时候开始计数,当计数值溢出,没有喂狗操作的时候WDT就使系统复位。
程序运行过程中,每隔一段时间内核发出指令让看门狗重新计数(喂狗),在最大间隔时间内,通过喂狗,系统就不会实现自动复位。
看门狗有两种模式
1.看门狗模式(用来监视系统)
2.定时器模式(和定时器一样,有四个可选的定时时间)
ps:定时器模式会产生中断请求
看门狗定时器控制寄存器WDCTL
喂狗序列:在看门狗时钟周期内,写入0XA到WDCTL.CLR;然后写入0X5到同一个寄存器位
通过代码实现两个LED闪烁8次,一个灯的闪烁带喂狗程序,一个灯的闪烁不带喂狗程序。
#include<ioCC2530.h>
#define uchar unsigned char
#define uint unsigned int
#define D3 P1_0
#define D4 P1_1
#define D5 P1_3
#define D6 P1_4
//延时函数
void delay(uint t)
{
while(t--);
}
//初始化LED
void Init_Led()
{ //配置灯的寄存器
P1SEL&=~0X1B;
P1DIR|=0X1B;
//把四个LED灯闭关
P1&=~0X1B;
}
//初始化看门狗
void Init_Wdt()
{
//0000 1000 设置为看门狗模式
WDCTL=0X08;
}
//喂狗程序
void FeedWD()
{
WDCTL|=0XA0;
WDCTL|=0X50;
}
//带喂狗的led闪烁
void Feed_Led()
{
D4=1;
delay(60000);
delay(60000);
D4=0;
delay(60000);
delay(60000);
FeedWD();
}
//不带喂狗的led闪烁
void NOTFEED_Led()
{
D6=1;
delay(60000);
delay(60000);
D6=0;
delay(60000);
delay(60000);
}
void main()
{
Init_Led();
Init_Wdt();
uint i=0;
while(1)
{
for(i=0;i<8;i++)
{
Feed_Led();
}
for(i=0;i<8;i++)
{
NOTFEED_Led();
}
}
}
最后观察可以看到,带喂狗程序的灯可以完整的闪烁8次,不带喂狗程序的灯在在看门狗时钟周期完成后直接被复位,不能正常闪烁8次