#申请原创# #有奖活动# @21小跑堂 @21小管家
【硬件环境】
- 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 回到运行模式。具体配置代码如下:
voidInit_awt_power(void)
{
AWT_TimeCntInitTypeDefAWT_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
voiddelay_us(uint32_tus)
{
while(us--)
{
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
}
}
voidIIC_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*************/
voidSDA_IN()
{
CW_GPIOB->DIR_f.PIN11 = 1; //设置 GPIOx_DIR.PINy 为 0,将端口配置成输出;
}
voidSDA_OUT()
{
CW_GPIOB->DIR_f.PIN11 = 0; //设置 GPIOx_DIR.PINy 为 0,将端口配置成输出;
CW_GPIOB->OPENDRAIN_f.PIN11 = 0; //0:推挽输出
}
//产生IIC起始信号
voidIIC_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停止信号
voidIIC_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_tIIC_Wait_Ack(void)
{
uint8_tucErrTime=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();
return1;
}
}
IIC_SCL=0;//时钟输出0
return0;
}
//产生ACK应答
voidIIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答
voidIIC_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,无应答
voidIIC_Send_Byte(uint8_ttxd)
{
uint8_tt;
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_tIIC_Read_Byte(unsignedcharack)
{
unsignedchari,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
returnreceive;
}
SHT30的采集程序如下:
#include"sht30.h"
#include"myiic.h"
#define POLYNOMIAL_CXDZ 0x31 // X^8 + X^5 + X^4 + 1
//SHT3X CRC校验
unsignedcharSHT3X_CRC(uint8_t *data, uint8_tlen)
{
unsignedcharbit; // bit mask
unsignedcharcrc = 0xFF; // calculated checksum
unsignedcharbyteCtr; // byte counter
// calculates 8-Bit checksum with given polynomial @GZCXDZ
for(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);
}
}
}
returncrc;
}
//SHT30命令函数
//addr:表示产品的序号,因为SHT30使用IIC总线的话一条线上可以挂两个
voidSHT30_CMD(uint16_tcmd)
{
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_tSHT30_Read_Humiture(int *temp,uint16_t *humi)
{
uint8_tbuff[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;
elseif(*temp<-400) *temp=-400;
return0;
}
elsereturn1;
}
//SHT30初始化
voidSHT30_Init()
{
IIC_Init();
}
在主程序中,我们首先对串口、IIC、AWT、SHT30进行初始化,然后进入采集程序,实现的代码如下:
intmain(void)
{
intt[6];
uint16_th[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();
}
return0;
}
【程序效果】
模块采集的数据,在上位机的串口助手上接收到以16进制数据发送的温湿度数据。
上位机根据具体的需要再进行解析、判断或者分发。
【功耗测试】
此工程以合宙的IoT Power来采集功率耗数据,并做出基本的分析,具体效果如下图:
从上面的数据我们可以看出,待机电流为7.5微安左右,在每两分钟启用一次数据上报,最在工作电流为46.5mA,平均电流为110uA,平均功率为362微瓦。可以推算一下,1000mAH的电池可以持续供电100天左右。如果我们采用在温湿度正常的范围内缓存,每一个小时做一次数据上传,那么预计可以延长30倍的工作时间,那就是10年左右的待机。
【讨论】
CW32L031具有超低功耗的出色性能,此实验的意义验证了在电池供电的环境下,可以持续的工作数年的可能。433M无线超远距离无线转输模块可以提供长达5公里(空旷)数据传输,广泛适用于智慧农业等野外的数据持续采集。也可以把温湿度传感器更改为土壤湿度、门禁等传感器,实现无线报警等功能。
|