SHT30的测量指令与数据获取及CRC主要代码如下:
- #include "sht30.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[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); //MSB
- IIC_Wait_Ack();
- IIC_Send_Byte(cmd&0xff); //LSB
- IIC_Wait_Ack();
- IIC_Stop();
- SysTickDelay(500);//命令发完后需要等待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();
- }
2、LCD屏的显示,分为两个部分,一个是定义了段码显示的高、低位显示数组; 二是封装了数量显示了函数,具体代码如下:
- /* 段码低8(左) */
- static uint8_t num_L[10] = {
- 0x0d, //0
- 0x00, //1
- 0x0e, //2
- 0x0a, //3
- 0x03, //4
- 0x0b, //5
- 0x0f, //6
- 0x00, //7
- 0x0f, //8
- 0x0b, //9
-
- };
- /* 段码高8(右) */
- static uint8_t num_H[10] = {
- 0x07,
- 0x06,
- 0x03,
- 0x07,//3
- 0x06,//4
- 0x05, //5
- 0x05, //
- 0x07, //7
- 0x07, //8
- 0x07, //9
- };
- void Lcd_clear(void)
- {
- CW_LCD->RAM0 = 0;
- CW_LCD->RAM1 = 0;
- CW_LCD->RAM8 = 0;
- CW_LCD->RAM9 = 0;
- }
- void show_nums(uint32_t num)
- {
- uint8_t i=0;
- uint8_t j;
- uint32_t temp;
- temp = num;
- //空显示
- Lcd_clear();
- if(temp == 0)
- show_num(0,0,0);
- while(temp>0)
- {
- j = temp%10;
- show_num(i,j,0);
- temp /=10;
- i++;
- }
- }
- /**
- *功能:显示数字到LCD段码屏上
- *输入参数1:显示在哪个位上7-0
- *输入参数2:需要显示数字
- *输入参数3:是否需要显示小数点
- */
- void show_num(uint8_t wei, uint8_t num, uint8_t doit)
- {
- uint8_t temp_H;
- temp_H = num_H[num];
- if(0 != doit)
- {
- temp_H = temp_H + 8 ; //第四位置1显示小数点
- }
- switch(wei)
- {
- case 7:
- {
- //显示第7个数码管
- CW_LCD->RAM0 |= temp_H <<8 | num_L[num];
- break;
- }
- case 6:
- {
- //显示第6个数码管
- CW_LCD->RAM0 |= (temp_H<<8 | num_L[num]) <<16;
- break;
- }
- case 5:
- {
- //显示第5个数码管
- CW_LCD->RAM1 |= num_L[num];
- CW_LCD->RAM8 |= temp_H;
- break;
- }
- case 4:
- {
- //显示第4个数码管
- CW_LCD->RAM8 |= temp_H<<16 | num_L[num]<<8;
- break;
- }
- case 3:
- {
- //显示第3个数码管
- CW_LCD->RAM8 |= num_L[num]<<24;
- CW_LCD->RAM9 |= temp_H;
- break;
- }
- case 2:
- {
- //显示第2个数码管
- CW_LCD->RAM9 |= temp_H<<16 | num_L[num]<<8;
- break;
- }
- case 1:
- {
- //显示第1个数码管
- CW_LCD->RAM1 |= temp_H<<8;
- CW_LCD->RAM9 |= num_L[num]<<24;
- break;
- }
- case 0:
- {
- //显示第0个数码管
- CW_LCD->RAM1 |= temp_H<<24 | num_L[num]<<16;
- break;
- }
- }
-
- }
- void LCD_Configuration(void)
- {
- LCD_InitTypeDef LCD_InitStruct = {0};
- LCD_InitStruct.LCD_Bias = LCD_Bias_1_3;
- LCD_InitStruct.LCD_ClockSource = LCD_CLOCK_SOURCE_LSI;
- LCD_InitStruct.LCD_Duty = LCD_Duty_1_4;
- LCD_InitStruct.LCD_ScanFreq = LCD_SCAN_FREQ_128HZ;
- LCD_InitStruct.LCD_VoltageSource = LCD_VoltageSource_Internal;
- __RCC_LCD_CLK_ENABLE();
- RCC_LSI_Enable();
- LCD_Init(&LCD_InitStruct); //基本配置
- // BTL004 LCD 对应的连接
- //PA12 COM3
- //PA11 COM2
- //PA10 COM1
- //PA09 COM0
- //PA08 SEG0
- //PC09 SEG1
- //PC08 SEG2
- //PC07 SEG3
- //PC06 SEG4
- //PD15 SEG32
- //PD14 SEG33
- //PD13 SEG34
- //PD12 SEG35
- //PD11 SEG36
- //PD10 SEG37
- //PD09 SEG38
- //PD08 SEG39
- //PB15 SEG5
- //PB14 SEG6
- //PB13 SEG7
- // 分配引脚
- LCD_COMConfig(LCD_COM0 | LCD_COM1 | LCD_COM2 | LCD_COM3, ENABLE);
- LCD_SEG0to23Config(0x0000FF, ENABLE);
- LCD_SEG32to55Config(0x0000FF,ENABLE);
- CW_LCD->RAM[0] = 0;
- CW_LCD->RAM[1] = 0;
- CW_LCD->RAM2 = 0;
- CW_LCD->RAM3 = 0;
- CW_LCD->RAM4 = 0;
- CW_LCD->RAM5 = 0;
- CW_LCD->RAM6 = 0;
- CW_LCD->RAM7 = 0;
- CW_LCD->RAM8 = 0;
- CW_LCD->RAM9 = 0;
- CW_LCD->RAM10 = 0;
- CW_LCD->RAM11 = 0;
- CW_LCD->RAM12 = 0;
- CW_LCD->RAM13 = 0;
- LCD_Cmd(ENABLE);
- CW_LCD->RAM0 = 0;
- LCD_ContrastConfig(LCD_Contrast_Level_6);
- LCD_DriveVoltageConfig(LCD_INRS_LEVEL_0);
- }
3、功耗控制主要是通过进入深度睡眠模式来实现节能,并通过RTC的AWT模块来实现定时唤醒。在此模块中,我们配置了AWT时钟源为RTC_AWTSOURCE_FROM_RTC1HZ_1即1秒为单位的唤醒,我们可以通过RTC_AWTARR 唤醒定时器重载值,来实现以秒为单位的休眠时长。主要代码如下:
- //进入低功耗设置
- void entry_power(void)
- {
- // //1,先判断是否上电复位
- RTC_InitTypeDef RTC_InitStruct = {0};
- RTC_AWTTypeDef RCT_AWTStruct = {0};
- RCC_LSE_Enable(RCC_LSE_MODE_OSC, RCC_LSE_AMP_NORMAL, RCC_LSE_DRIVER_NORMAL); // 选择LSE为RTC时钟
- RTC_InitStruct.DateStruct.Day = 0x24; //设置日期,DAY、MONTH、YEAR必须为BCD方式,星期为0~6,代表星期日,星期一至星期六
- RTC_InitStruct.DateStruct.Month = RTC_Month_June;
- RTC_InitStruct.DateStruct.Week = RTC_Weekday_Monday;
- RTC_InitStruct.DateStruct.Year = 0x23;
- RTC_InitStruct.TimeStruct.Hour = 0x11; //设置时间,HOUR、MINIUTE、SECOND必须为BCD方式,用户须保证HOUR、AMPM、H24之间的关联正确性
- RTC_InitStruct.TimeStruct.Minute = 0x58;
- RTC_InitStruct.TimeStruct.Second = 0x59;
- RTC_InitStruct.TimeStruct.AMPM = 0;
- RTC_InitStruct.TimeStruct.H24 = 0;
- RTC_InitStruct.RTC_ClockSource = RTC_RTCCLK_FROM_LSE;
- RTC_Init(&RTC_InitStruct); //
- //设置自动唤醒
- RCT_AWTStruct.AWT_ClockSource = RTC_AWTSOURCE_FROM_RTC1HZ_1;
- RCT_AWTStruct.AWT_ARRValue = 60;
- RTC_AWTConfig(&RCT_AWTStruct);
- RTC_AWTCmd(ENABLE);
- RCC_APBPeriphClk_Enable1(RCC_APB1_PERIPH_RTC, ENABLE);
- RTC_ITConfig(RTC_IT_AWTIMER, ENABLE);
- }
4、在主程序中,我们先初始基本外设后进行循环的采集——显示——休眠——唤醒来实现温湿度采集的目标,主程序主要代码如下:
- int32_t main(void)
- {
- uint16_t temp;
- int t[20];
- uint16_t h[20];
- RCC_Configuration();
- NVIC_Configuration();
- LCD_Configuration();
- InitTick(8000000);
- SHT30_Init();
- Lcd_clear();
- SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
- entry_power();
- while(1)
- {
- SHT30_Read_Humiture(t,h);
- temp = t[0];
- Lcd_clear();
- show_num(2, temp/100,0);
- show_num(1, (temp/10)%10,1);
- show_num(0, temp%10,0);
- temp = h[0];
- show_num(7, temp/100,0);
- show_num(6, (temp/10)%10,1);
- show_num(5, temp%10,0);
- CW_SYSCTRL->AHBEN_f.GPIOB = 0;
- __DSB();
- __WFI();
- SHT30_Init();
- }
- }
【实现的效果】
我们设定60秒中唤醒进行一次温显度采集,实现了休眠电流为5uA,综合平均工作电流为13uA、平均功率为。基本满足了以电池供电的环境下的超长工作。
【讨论】
CW32L083集成了LCD控制器,可以实现数据采集、显示的超低功耗工作。非常适合用于电池供电的环境下工作。本次试验虽然获得了理想效果,但是还有一些可以改进的地方。
1、在待机中的主要电流产生是LCD屏产生的功耗,如果在特殊的环境下,不需要长时间显示,可以适时关闭LCD屏,这样可以节约差不多4uA的工作电流。启用按键来人工参与显示数据,这样又可以更进一步降低超机功耗。
2、在工作电流中,主要消耗的是SHT30的温度转换时产生的大电流。如果应用的生产环境,可以在等待温度转换时,降低MCU的主频或者进入sleep模式以降低能耗。