【開發環境】
1、硬件:CW32L083VxTx StartKit 開發板,板載有8位LCD段碼屏。
2、軟件環境:MDK5。
3、溫濕度計:SHT30。
【硬件連接】
開發板 SHT30
PB11 SDA
PB10 SCL
DVCC VCC
DVSS GND
【功耗測試環境】
合宙IoT Power功耗測試神器。
【硬件框圖】
【軟件流程圖】
【主要代碼設計】
本工程主要代碼功能為溫濕傳感器SHT30的數據采集、LCD顯示、RTC自動喚醒。下面展示三個功能模塊的主要代碼:
1、SHT30采集模擬IIC通信,主要是IIC的時序產生,與SHT30的單次采集指令發送與數據讀取以及CRC。
IIC的時序產生主要代碼如下:
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 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("超時
");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的測量指令與數據獲取及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
",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=10100* ((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、平均功率為?;緷M足了以電池供電的環境下的超長工作。
【討論】
CW32L083集成了LCD控制器,可以實現數據采集、顯示的超低功耗工作。非常適合用于電池供電的環境下工作。本次試驗雖然獲得了理想效果,但是還有一些可以改進的地方。
1、在待機中的主要電流產生是LCD屏產生的功耗,如果在特殊的環境下,不需要長時間顯示,可以適時關閉LCD屏,這樣可以節約差不多4uA的工作電流。啟用按鍵來人工參與顯示數據,這樣又可以更進一步降低超機功耗。
2、在工作電流中,主要消耗的是SHT30的溫度轉換時產生的大電流。如果應用的生產環境,可以在等待溫度轉換時,降低MCU的主頻或者進入sleep模式以降低能耗。
[[]()]()
-
開發板
+關注
關注
25文章
5032瀏覽量
97371 -
溫濕度計
+關注
關注
1文章
23瀏覽量
8086 -
MDK5
+關注
關注
0文章
10瀏覽量
5740 -
CW32
+關注
關注
1文章
203瀏覽量
626
發布評論請先 登錄
相關推薦
評論