本文主要是关于CC1101的相关介绍,并着重对CC1101驱动在STM32F103的移植进行了详尽的阐述。
浅谈CC1101驱动在STM32F103的移植
首先明确:CC1101是通过SPI与MCU进行通信的。根据从TI官方上获得CC1101驱动,直接先移植SPI部分,STM32F103提供了SPI1和SPI2两条SPI总线,可自行选择,对于SPI的移植,直接参考STM32开发板上关于通过SPI操作Flash示例代码,对于SPI的配置与TI提供的驱动代码里的SPI配置保持一致。SPI移植完成之后,接上CC1101射频模块,测试SPI是否能正常通信,主要通过向CC1101任意可读可写寄存器写一个任意值,然后再读出该寄存器里的值,通过串口打印出该值,通过以上操作判断SPI是否正常通信,SPI移植是否成功。当然,这里使用到了串口,所以需要同时将串口的代码实现,同样参考串口实例。
其次,当STM32与CC1101的SPI通信完成后,果断开始CC1101后续驱动的移植。移植过程中,所有变量名、函数名与TI提供的驱动里的保持一致,当然CC1101寄存器配置也保持移植。对于移植初期,我并没有太多的关心CC1101的时序问题,只关心怎么去移植,这也是自己的一个不好的习惯,所以初期移植的时候,对着TI提供的驱动代码,TI代码里有什么函数,我也移植什么函数;函数里有CS管脚的操作,也对应在操作在STM32下定义的CS管脚;TI里延时多长,我也跟着在STM32下延时相应的时间。整个驱动移植下来,关于CC1101的驱动函数也大多了然在心了。
最后,TI驱动里提供的是轮询的方式收发数据,对于初期来说,首先需要实现CC1101的工作,编译调试移植到STM32上的CC1101驱动代码,看见数据从接收端串口打印出的那瞬间,心情真心不错基于STM32F103的CC1101驱动移植。
当然,轮询的方式并不适合我在实际中应用,改用中断方式接收数据,且使用FIFO小于64BYTE,对于中断接收,做如下总结:
方法一:配置寄存器IOCFGx.GDOx_CFG=0x06(可查看CC1101数据手册通用引脚部分),以下降沿触发中断。
方法二:配置寄存器IOCFGx.GDOx_CFG=0x01,以上升降沿触发中断。
对于中断的试用,具体可根据对IOCFGx.GDOx_CFG的配置实现。
本以为,这样之后就完成了CC1101的驱动移植,最后才知道,自己菜得不行,移植的代码只是纯粹的数据收发,当导师问道有无CCA、CS检测等等,我就茫然了,恰好又是期末了,大牛导师最后自己去重新移植了有CCA检测机制的驱动(主要参考TI官方提供的SimpliciTI代码),心里真心难受基于STM32F103的CC1101驱动移植,好失败,后期只对CC1101驱动进行了维护和改进。
关于在后期维护和改进中,主要解决两个问题:
1.通信距离
测试参考SimpliciTI移植的CC1101驱动,其通信距离只有20几米,能穿透一堵墙,无法满足我们的需求,通过增加发射功率PA值,通信距离也不太明显。查阅资料,通信距离与应用环境、通信速率、PA等有关,与是准备将它通信速率(TI提供的是空中速率为250K的),修改为10K的速率,开始以为纯粹配置一下MDMCFG3 为10K就行,但最终是必须配置channel filter bandwidth 、frequency deviation等,这些配置均建议通过TI提供的SmartRF Studio 软件进行配置。速率改变也必须注意CCA监测过程中RSSI可用等待时间,RSSI可参考其TI提供的DN505文档。最后,通过降低传输速率,将PA值设为10db,测试通信距离在空旷场地下可达到200多米,可穿透三堵墙,已经满足项目需要。但测试期间,修改后的10K速率驱动,经常CCA检测失败,虽然通过DN505文档修改了RSSI等待的时间,但依旧这样,最终此问题纠结了我3天,最后突发奇想地去修改了调制解调方式为2-FSK后,CCA检测正常运行,至今还是没明白为什么会这样。
2.关于GDO0的问题
由于项目中是定时发送数据,接收端采用GDO0以中断方式接收数据,但在测试中发现,终端正常运行不定时间后,中断无法产生,调试为发现FIFO溢出等,通过去TI论坛上查找,发现有类似问题,但没有彻底的解决方法,目前主要通过设置定时阈值,超过阈值未接收到数据,就判定为GDO0中断出现问题,便执行FIFO刷新操作,然后终端又能继续正常运行,
CC1101与STM32的低功耗
1.stm32低功耗
(1)进入stop模式
由于项目需要在睡眠时也保留RAM的数据,顾考虑采用stop模式以减少STM32 的功耗,进入stop的方法很简单,直接调用库函数中的 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); 此处,我选择了关闭电压转换器以进一步降低功耗,使用指令WFI进入,关于STOP进入方式的选择,可参考前文提供的博客。
(2)STM32的唤醒
由于,应用中采用发送完数据变进入休眠,并定时唤醒发送,且STOP模式下RTC正常工作,所以在本应用中采用了RTC闹钟周期中断唤醒,对于RTC的使用即配置可如下:
void RTC_Configuration(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_ClearITPendingBit(EXTI_Line17);
EXTI_InitStructure.EXTI_Line = EXTI_Line17;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
PWR_BackupAccessCmd(ENABLE);
BKP_DeInit();
#ifdef RCC_LSE
RCC_LSEConfig(RCC_LSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{
}
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
#else
RCC_LSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
{
}
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
#endif
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
RTC_SetPrescaler(32767);
RTC_WaitForLastTask();
RTC_ITConfig(RTC_IT_ALR, ENABLE);
RTC_WaitForLastTask();
RTC_Alarm_Interrupt(DISABLE);
}
以上,主要注意RTC时钟的选择,选择RCC_LSE ,时间比较精确,但会产生相对长一点的唤醒时延选择;RCC_LSI,时间则不那么准确,且功耗要多一点,但产生的唤醒时延较小,具体可查阅STM32使用手册关于低功耗部分的介绍。
2.CC1101低功耗
(1)进入掉电模式
CC1101进入IDEL状态 一》 使用掉电模式(SPWD)即可。
(2)唤醒
直接操作拉低CS管脚即可。
3.调试低功耗
前期调试,只分别测试了STM32和CC1101在休眠功能上的实现,即是否能进入休眠以及是否能够成功进行唤醒,未对实际功耗进行测试(由于硬件的特殊性)。
后期第一次测试STM32+CC1101整体模块低功耗模式下功耗为4点几mA,顿时就无语了关于STM32低功耗+CC1101低功耗;然后果断挑断模块上的所有LED灯,再次测试,功耗直接降到1mA左右,此时虽然有所下降,但离手册上的几uA真不是一个档次的,但对于菜鸟的我此时根本不知道怎么继续减少功耗了,好吧,我只有去茫茫网络中寻找低功耗的蛛丝马迹了,果然在http://www.openedv.com/posts/list/18372.htm#116532里找到了希望,真心感谢博主的分享,于是赶紧把用到的SPI管脚以及串口管脚安装博主提供的修改,并关闭所有不用的PIN,但最终测试功耗依然未降低,这就纳闷了,为啥还是每降低呢关于STM32低功耗+CC1101低功耗,突然想起了不久前看过的STM32L系列低功耗的芯片,同时在有个低功耗经验师兄的提醒下,果断参考了其官方提供的超低功耗代码关于STM32低功耗+CC1101低功耗,并在自己的项目中进行如下操作:
在每次进入休眠前:
#关闭所有时钟以及外设(如本项目中用到的串口、SPI、timer)
#将所有I/O口改为GPIO_Mode_AIN状态
void DisableGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure
;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC
|RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO, ENABLE);
//Configure all GPIO port pins in Analog Input mode (floating input trigger OFF)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_Init(GPIOE, &GPIO_InitStructure);
//GPIOs Periph clock disable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC
|RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, DISABLE);
}
在每次唤醒后:
#开启休眠前所关闭的对应时钟以及外设
#初始化使用到的GPIO口
添加了如上的处理后,再次测试低功耗,终于让我看见了uA级别的功耗关于STM32低功耗+CC1101低功耗,但功耗最低时在40uA左右,与根据芯片手册提供的低功耗数据相比,还是有很大的距离,不过至此,通过软件进一步实现低功耗,我已经无法再想到其他的方法,外设该关的都已经关掉,功耗在40uA左右,目前能想到的就是硬件上功耗降降低,
结语
关于CC1101的相关介绍就到这了,如有不足之处欢迎指正。