tag 标签: CW32L031

相关博文
  • 热度 10
    2023-7-16 16:08
    475 次阅读|
    0 个评论
    CW32L031实现低功耗温湿度 【硬件环境】 CW32L031C8开发板 SHT30温湿度传感器 E31-TTL-50无线串口模块 【开发板环境】 Ubuntu20.0.4 【代码编辑器】 VSCODE ssh远程 【编译器】 arm-none-eabi-gcc 【工程包】 Cw32l031_gcc工程包 【工程概述】 本工程的核心分为sht30数据采集后,经无线串口模块发送给上位机,利用自动唤醒模块休眠指定时长后再次唤醒系统进行数据采集。 【初略原理图】 【程序流程图】 【 主要代码】 自动唤醒定时器 (AWT) 包含一个 16bit 向下计数器,并由一个可编程预分频器驱动。AWT 可选 5 种计数时钟源,可工作于定时模式或计数模式。当计数器时钟源为 LSE 或 LSI 时,AWT 可在深度休眠模式下保持运行,下溢出中断可唤醒 MCU 回到运行模式。具体配置代码如下: void Init_awt_power ( void ) { AWT_TimeCntInitTypeDef AWT_TimeCntInitStruct = { 0 }; RCC_APBPeriphClk_Enable2 ( RCC_APB2_PERIPH_AWT , ENABLE ); //Open AWT Clk RCC_SystemCoreClockUpdate ( RCC_Sysctrl_GetHClkFreq () ); RCC_LSI_Enable (); AWT_TimeCntStructInit ( & AWT_TimeCntInitStruct ); AWT_TimeCntInitStruct . AWT_ClkSource = AWT_CLKSOURCE_LSI ; AWT_TimeCntInitStruct . AWT_Prescaler = AWT_PRS_DIV32768 ; AWT_TimeCntInitStruct . AWT_Mode = AWT_MODE_TIMECNT ; AWT_TimeCntInitStruct . AWT_Period = 120 ; AWT_TimeCntInit (& AWT_TimeCntInitStruct ); __disable_irq (); NVIC_EnableIRQ ( AWT_IRQn ); __enable_irq (); //使能AWT下溢出中断 AWT_ITConfig ( AWT_IT_UD , ENABLE ); AWT_Cmd ( ENABLE ); //DeepSleep唤醒时,保持原系统时钟来源 RCC_WAKEUPCLK_Config ( RCC_SYSCTRL_WAKEUPCLKDIS ); } 软件IIC的配置,这里使用软件模拟实现。具体代码如下: #include "myiic.h" #define I2C1_SCL_GPIO_PORT CW_GPIOB #define I2C1_SCL_GPIO_PIN GPIO_PIN_10 #define I2C1_SDA_GPIO_PORT CW_GPIOB #define I2C1_SDA_GPIO_PIN GPIO_PIN_11 void delay_us ( uint32_t us ) { while ( us --) { __NOP (); __NOP (); __NOP (); __NOP (); __NOP (); } } void IIC_Init ( void ) { //配置PB10 为输出 //使能GPIOB时钟 CW_SYSCTRL AHBEN_f . GPIOB = 1 ; //配置PB10 为输出 CW_GPIOB ANALOG_f . PIN10 = 0 ; //设置 GPIOx_ANALOG.PINy 为 0,将端口配置为数字功能; CW_GPIOB DIR_f . PIN10 = 0 ; //设置 GPIOx_DIR.PINy 为 0,将端口配置成输出; CW_GPIOB OPENDRAIN_f . PIN10 = 0 ; //0:推挽输出 CW_GPIOB ODR_f . PIN10 = 1 ; CW_GPIOB ANALOG_f . PIN11 = 0 ; //设置 GPIOx_ANALOG.PINy 为 0,将端口配置为数字功能; CW_GPIOB DIR_f . PIN11 = 0 ; //设置 GPIOx_DIR.PINy 为 0,将端口配置成输出; CW_GPIOB OPENDRAIN_f . PIN11 = 0 ; //0:推挽输出 CW_GPIOB ODR_f . PIN11 = 1 ; } //IO方向设置(SDA) /*********xxxxxxxxxxxxxx*************/ void SDA_IN () { CW_GPIOB DIR_f . PIN11 = 1 ; //设置 GPIOx_DIR.PINy 为 0,将端口配置成输出; } void SDA_OUT () { CW_GPIOB DIR_f . PIN11 = 0 ; //设置 GPIOx_DIR.PINy 为 0,将端口配置成输出; CW_GPIOB OPENDRAIN_f . PIN11 = 0 ; //0:推挽输出 } //产生IIC起始信号 void IIC_Start ( void ) { SDA_OUT (); //sda线输出 IIC_SDA = 1 ; IIC_SCL = 1 ; delay_us ( 4 ); IIC_SDA = 0 ; //START:when CLK is high,DATA change form high to low delay_us ( 4 ); IIC_SCL = 0 ; //钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void IIC_Stop ( void ) { SDA_OUT (); //sda线输出 IIC_SCL = 0 ; IIC_SDA = 0 ; //STOP:when CLK is high DATA change form low to high delay_us ( 4 ); IIC_SCL = 1 ; IIC_SDA = 1 ; //发送I2C总线结束信号 delay_us ( 4 ); } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 /*********xxxx修改超时时间************/ uint8_t IIC_Wait_Ack ( void ) { uint8_t ucErrTime = 0 ; SDA_IN (); //SDA设置为输入 IIC_SDA = 1 ; delay_us ( 3 ); IIC_SCL = 1 ; delay_us ( 3 ); while ( READ_SDA ) { ucErrTime ++; if ( ucErrTime 250 ) { //printf("超时\n"); IIC_Stop (); return 1 ; } } IIC_SCL = 0 ; //时钟输出0 return 0 ; } //产生ACK应答 void IIC_Ack ( void ) { IIC_SCL = 0 ; SDA_OUT (); IIC_SDA = 0 ; delay_us ( 2 ); IIC_SCL = 1 ; delay_us ( 2 ); IIC_SCL = 0 ; } //不产生ACK应答 void IIC_NAck ( void ) { IIC_SCL = 0 ; SDA_OUT (); IIC_SDA = 1 ; delay_us ( 2 ); IIC_SCL = 1 ; delay_us ( 2 ); IIC_SCL = 0 ; } //IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void IIC_Send_Byte ( uint8_t txd ) { uint8_t t ; SDA_OUT (); IIC_SCL = 0 ; //拉低时钟开始数据传输 for ( t = 0 ; t < 8 ; t ++) { if (( txd & 0x80 7 ) IIC_SDA = 1 ; else IIC_SDA = 0 ; txd <<= 1 ; delay_us ( 2 ); //对TEA5767这三个延时都是必须的 IIC_SCL = 1 ; delay_us ( 2 ); IIC_SCL = 0 ; delay_us ( 2 ); } } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK uint8_t IIC_Read_Byte ( unsigned char ack ) { unsigned char i , receive = 0 ; SDA_IN (); //SDA设置为输入 for ( i = 0 ; i < 8 ; i ++ ) { IIC_SCL = 0 ; delay_us ( 100 ); IIC_SCL = 1 ; receive <<= 1 ; if ( READ_SDA ) receive ++; delay_us ( 100 ); } if (! ack ) IIC_NAck (); //发送nACK else IIC_Ack (); //发送ACK return receive ; } SHT30的采集程序如下: #include "sht30.h" #include "myiic.h" #define POLYNOMIAL_CXDZ 0x31 // X^8 + X^5 + X^4 + 1 //SHT3X CRC校验 unsigned char SHT3X_CRC ( uint8_t * data , uint8_t len ) { unsigned char bit ; // bit mask unsigned char crc = 0xFF ; // calculated checksum unsigned char byteCtr ; // byte counter // calculates 8-Bit checksum with given polynomial @GZCXDZ for ( byteCtr = 0 ; byteCtr < len ; byteCtr ++) { crc ^= ( data ); for ( bit = 8 ; bit 0 ; -- bit ) { if ( crc & 0x80 ) { crc = ( crc << 1 ) ^ POLYNOMIAL_CXDZ ; } else { crc = ( crc << 1 ); } } } return crc ; } //SHT30命令函数 //addr:表示产品的序号,因为SHT30使用IIC总线的话一条线上可以挂两个 void SHT30_CMD ( uint16_t cmd ) { IIC_Start (); IIC_Send_Byte ( SHT30_ADDR + 0 ); //发送设备地址,写寄存器 IIC_Wait_Ack (); IIC_Send_Byte (( cmd 8 )& 0xff ); //MSB IIC_Wait_Ack (); IIC_Send_Byte ( cmd & 0xff ); //LSB IIC_Wait_Ack (); IIC_Stop (); SysTickDelay ( 50 ); //命令发完后需要等待20ms以上才能读写 } //SHT30读取温湿度 //temp:温度,-400~1250,实际温度=temp/10,分辨率0.1℃,精度±0.3℃ //humi:湿度,0~1000,实际湿度=humi/10,分辨率0.1%rh,精度±3 //返回0成功,1失败 uint8_t SHT30_Read_Humiture ( int * temp , uint16_t * humi ) { uint8_t buff ; SHT30_CMD ( SHT30_READ_HUMITURE ); //读温湿度命令 IIC_Start (); IIC_Send_Byte ( SHT30_ADDR + 1 ); //发送设备地址,读寄存器 IIC_Wait_Ack (); buff = IIC_Read_Byte ( 1 ); //继续读,给应答 buff = IIC_Read_Byte ( 1 ); //继续读,给应答 buff = IIC_Read_Byte ( 1 ); //继续读,给应答 buff = IIC_Read_Byte ( 1 ); //继续读,给应答 buff = IIC_Read_Byte ( 1 ); //继续读,给应答 buff = IIC_Read_Byte ( 0 ); //不继续给停止应答 IIC_Stop (); //printf("buff=%d,%d,%d,%d,%d,%d\r\n",buff ,buff ,buff ,buff ,buff ,buff ); //CRC校验 if ( SHT3X_CRC (& buff , 2 )== buff && SHT3X_CRC (& buff , 2 )== buff ) { * temp =(- 45 +( 175.0 *(( buff << 8 )+ buff )/ 65535.0 ))* 10 ; * humi = 10 * 100 *(( buff << 8 )+ buff )/ 65535.0 ; if (* temp 1250 ) * temp = 1250 ; else if (* temp <- 400 ) * temp =- 400 ; return 0 ; } else return 1 ; } //SHT30初始化 void SHT30_Init () { IIC_Init (); } 在主程序中,我们首先对串口、IIC、AWT、SHT30进行初始化,然后进入采集程序,实现的代码如下: int main ( void ) { int t ; uint16_t h ; E31_UART_Init (); SHT30_Init (); USART_ITConfig ( CW_UART1 , USART_IT_RC , ENABLE ); Init_awt_power (); InitTick ( 24000000ul ); //初始化SysTick // 开启两线调试接口 RCC_SWDIO_Config ( RCC_SYSCTRL_SWDIOEN ); while ( 1 ) { SHT30_Read_Humiture ( t , h ); e31_send ( t , h ); enter_lowpower (); exit_lowpower (); } return 0 ; } 【程序效果】 模块采集的数据,在上位机的串口助手上接收到以16进制数据发送的温湿度数据。 上位机根据具体的需要再进行解析、判断或者分发。 【功耗测试】 此工程以合宙的IoT Power来采集功率耗数据,并做出基本的分析,具体效果如下图: 从上面的数据我们可以看出,待机电流为7.5微安左右,在每两分钟启用一次数据上报,最在工作电流为46.5mA,平均电流为110uA,平均功率为362微瓦。可以推算一下,1000mAH的电池可以持续供电100天左右。如果我们采用在温湿度正常的范围内缓存,每一个小时做一次数据上传,那么预计可以延长30倍的工作时间,那就是10年左右的待机。 【讨论】 CW32L031具有超低功耗的出色性能,此实验的意义验证了在电池供电的环境下,可以持续的工作数年的可能。433M无线超远距离无线转输模块可以提供长达5公里(空旷)数据传输,广泛适用于智慧农业等野外的数据持续采集。也可以把温湿度传感器更改为土壤湿度、门禁等传感器,实现无线报警等功能。