硬件环境
开发板环境
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 ClkRCC_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_11void 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 lowdelay_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 highdelay_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;//时钟输出0return 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;elseIIC_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,发送nACKuint8_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();//发送nACKelseIIC_Ack(); //发送ACKreturn 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 maskunsigned char crc = 0xFF; // calculated checksumunsigned char byteCtr; // byte counter// calculates 8-Bit checksum with given polynomial @GZCXDZfor(byteCtr = 0; byteCtr < len; byteCtr++) {crc ^= (data[byteCtr]);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); //MSBIIC_Wait_Ack();IIC_Send_Byte(cmd&0xff); //LSBIIC_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[6];SHT30_CMD(SHT30_READ_HUMITURE);//读温湿度命令IIC_Start();IIC_Send_Byte(SHT30_ADDR+1); //发送设备地址,读寄存器IIC_Wait_Ack();buff[0]=IIC_Read_Byte(1);//继续读,给应答buff[1]=IIC_Read_Byte(1);//继续读,给应答buff[2]=IIC_Read_Byte(1);//继续读,给应答buff[3]=IIC_Read_Byte(1);//继续读,给应答buff[4]=IIC_Read_Byte(1);//继续读,给应答buff[5]=IIC_Read_Byte(0);//不继续给停止应答IIC_Stop();//printf("buff=%d,%d,%d,%d,%d,%d\r\n",buff[0],buff[1],buff[2],buff[3],buff[4],buff[5]);//CRC校验if(SHT3X_CRC(&buff[0],2)==buff[2] && SHT3X_CRC(&buff[3],2)==buff[5]){*temp=(-45+(175.0*((buff[0]< < 8)+buff[1])/65535.0))*10;*humi=10*100*((buff[3]< < 8)+buff[4])/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[6];uint16_t h[6];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[0],h[0]);enter_lowpower();exit_lowpower();}return 0;}
程序效果
模块采集的数据,在上位机的串口助手上接收到以16进制数据发送的温湿度数据。
上位机根据具体的需要再进行解析、判断或者分发。
功耗测试
此工程以合宙的IoT Power来采集功率耗数据,并做出基本的分析,具体效果如下图:
从上面的数据我们可以看出,待机电流为7.5微安左右,在每两分钟启用一次数据上报,最在工作电流为46.5mA,平均电流为110uA,平均功率为362微瓦。可以推算一下,1000mAH的电池可以持续供电100天左右。如果我们采用在温湿度正常的范围内缓存,每一个小时做一次数据上传,那么预计可以延长30倍的工作时间,那就是10年左右的待机。
讨论
CW32L031具有超低功耗的出色性能,此实验的意义验证了在电池供电的环境下,可以持续的工作数年的可能。433M无线超远距离无线转输模块可以提供长达5公里(空旷)数据传输,广泛适用于智慧农业等野外的数据持续采集。也可以把温湿度传感器更改为土壤湿度、门禁等传感器,实现无线报警等功能。
扫码加入QQ群3群| 610403240
188