小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

SHT30使用的學(xué)習(xí)過程2 SHT30驅(qū)動(dòng)程序

 goodwangLib 2020-05-13

SHT30使用的學(xué)習(xí)過程2代碼篇
給各位道個(gè)歉,代碼拖得有點(diǎn)久了,最近事情頗多,抱歉抱歉!

綜述

嗯,代碼篇我想把我寫的所有的代碼給各位需要使用sht30的朋友們介紹一遍,由于我這版是測(cè)試版,所以很多函數(shù)沒有封裝的很好,不過代碼可以用了,我測(cè)試的代碼已經(jīng)通過,測(cè)量溫度和濕度精確到小數(shù)點(diǎn)后1位,在這里想仔細(xì)給各位介紹一下我代碼的寫作過程,因?yàn)榫W(wǎng)上的代碼僅僅是代碼,很多開發(fā)sht30的小白(像我這樣的)沒辦法移植,或者根本不知道怎么移植,在這里我想詳細(xì)敘述我的代碼,包括最基本的I2C通信,所以可能本次內(nèi)容很啰嗦,希望各位見諒哈[by zwx lvmm]

I2C代碼部分

這部分是SHT30和單片機(jī)通信的基礎(chǔ)協(xié)議,I2C有四根線組成,除去vcc和gnd之外還有SCL (時(shí)鐘線)與 SDA (數(shù)據(jù)線),STM32F407參考手冊(cè)上寫,I2C最大的通信周期是4MHz,普通模式下是2MHz。這部分涉及I2C通信的時(shí)序,可以參考原子哥(正點(diǎn)原子)的相關(guān)視頻資源,我的基礎(chǔ)也是和原子哥的視頻學(xué)習(xí)的,這里簡(jiǎn)單介紹一下,有什么不清楚的可以參考原子哥的視頻。

  1. I2C時(shí)序:
    圖片來源:百度圖庫
    這個(gè)是I2C時(shí)序圖。整個(gè)I2C通信分為這樣幾個(gè)過程,I2C起始信號(hào),I2C數(shù)據(jù)寫入,I2C數(shù)據(jù)讀取,I2C應(yīng)答信號(hào),I2C結(jié)束信號(hào)等,接下來分別介紹。(代碼參考的原子哥代碼,我看懂了直接用的)

1.1 IO初始化:

上代碼,我的開發(fā)板是STM32F407,這部分屬于初始化配置,沒啥說的。


//IO方向設(shè)置
#define SDA_IN()  {GPIOB->MODER&=~(3<<(11*2));GPIOB->MODER|=0<<11*2;}	//PB11輸入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(11*2));GPIOB->MODER|=1<<11*2;} //PB11輸出模式
//IO操作函數(shù)	 
#define IIC_SCL    PBout(10) //SCL
#define IIC_SDA    PBout(11) //SDA	 
#define READ_SDA   PBin(11)  //輸入SDA 
void IIC_Init(void)
{			
  GPIO_InitTypeDef  GPIO_InitStructure;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB時(shí)鐘

  //GPIOB10,B11初始化設(shè)置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽輸出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
	IIC_SCL=1;
	IIC_SDA=1;
}

此部分代碼看的原子哥的,IO配置的方法也是學(xué)習(xí)原子哥的設(shè)置的。

1.2 I2C起始信號(hào):

如最開始的圖所示當(dāng)SCL是高電平的時(shí)候,把SDA從高電平拉至低電平就可以了,先上這部分的代碼

void IIC_Start(void)
{
	SDA_OUT();     //sda線輸出模式
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//開始信號(hào),scl=1,sda=1->sda=0。
	delay_us(4);
	IIC_SCL=0;//scl=0,拉低時(shí)鐘線,準(zhǔn)備數(shù)據(jù)的發(fā)送
}	 

這里為什么是延時(shí)4us,我猜測(cè)是按照標(biāo)準(zhǔn)頻率2MHz,最高4MHz計(jì)算一下,一個(gè)數(shù)據(jù)周期大概5us-2.5us之間,還要考慮信號(hào)的建立時(shí)間,所以選擇了4us作為一個(gè)周期。其余的不懂得可以看I2C的介紹。

1.3 I2C停止信號(hào):

void IIC_Stop(void)
{
	SDA_OUT();//sda線輸出模式
	IIC_SCL=0;
	IIC_SDA=0;//
 	delay_us(4);
	IIC_SCL=1; //結(jié)束信號(hào),scl=1,sda=0->sda=1。
	IIC_SDA=1;
	delay_us(4);							   	
}

1.4 I2C應(yīng)答信號(hào)和等待應(yīng)答信號(hào):

這個(gè)東西就是說,機(jī)器之間的通信嘛,肯定是像我們使用對(duì)講機(jī)一樣,說完了一句話,就要加一句over,對(duì)方聽到了你的over,才能說他想說的,要不然就會(huì)兩個(gè)人一起說,肯定亂套了,所以才會(huì)有這兩個(gè)信號(hào),而有的時(shí)候我們不用說over,比如我們對(duì)話結(jié)束了,和對(duì)方說再見,說完就意味著對(duì)話結(jié)束了,所以也可以不產(chǎn)生應(yīng)答信號(hào)。代碼如下,具體的時(shí)序邏輯同樣參考原子哥,各位可以自己學(xué)習(xí)一下,如果你沒學(xué),就理解一下這個(gè)代碼是干啥的就行,也一樣可以寫代碼。

void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}		
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      //SDA設(shè)置為輸入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//時(shí)鐘輸出0 	   
	return 0;  
} 

1.5 I2C發(fā)送數(shù)據(jù):

這部分是比較關(guān)鍵的,I2C發(fā)送數(shù)據(jù)這里是8位的模式,我看到的I2C一般都是數(shù)據(jù)8位8位發(fā)的,發(fā)送的時(shí)候一個(gè)周期是4us,SCL是高電平的時(shí)候要保證數(shù)據(jù)有效,所以數(shù)據(jù)必須要在SCL變高之前建立完畢,這里可能有些人不太明白數(shù)據(jù)的建立是什么意思,其實(shí)就是點(diǎn)平從0變?yōu)?不是一瞬間變化完成的,而實(shí)經(jīng)過一段時(shí)間漲上去的,其實(shí)就是需要一段時(shí)間才能點(diǎn)平0->1或者1->0,雖然這段時(shí)間很短,但是對(duì)于一些速度比較快的協(xié)議可能就不能忽略這個(gè)影響了。>>這個(gè)是移位操作符,不懂得可以去查一查。

void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低時(shí)鐘開始數(shù)據(jù)傳輸
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(1);   
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(1);
    }	 
}

1.6 I2C接收數(shù)據(jù):

這部分接收,和發(fā)送差不多,每個(gè)周期都分成了兩個(gè)2us

//一個(gè)參數(shù) ack  當(dāng)ack=1,發(fā)送應(yīng)答信號(hào),ack=0不發(fā)送應(yīng)答信號(hào)
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA設(shè)置為輸入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(2); 
    }					 
    if (!ack)
        IIC_NAck();//發(fā)送nACK
    else
        IIC_Ack(); //發(fā)送ACK   
    return receive;
}

2. SHT30代碼部分

這部分代碼是關(guān)于SHT30與單片機(jī)之間的通信的流程和對(duì)讀到的數(shù)據(jù)進(jìn)行處理,包括初始化,讀取數(shù)據(jù)與數(shù)據(jù)處理,這部分代碼是閱讀datasheet之后結(jié)合datasheet相關(guān)內(nèi)容寫的。

2.1 SHT30的初始化:

這個(gè)我是采用的SHT30的周期模式,這部分的內(nèi)容大家可以參考我上一篇博客所寫的,里面給出了相關(guān)內(nèi)容的介紹。
關(guān)于SHT30 datasheet的相關(guān)內(nèi)容
流程:
2C開始信號(hào)->7位I2C地址+0(寫操作標(biāo)志位)(前面介紹了,如果ADDR接低電平,那么這里就是0x88,如果接高電平就是0x8a)->命令MSB->命令LSB(eg 0x2130 高可重復(fù)性,1秒測(cè)量一次)-> I2C停止信號(hào)。

void SHT_Init(void)
{
   delay_ms(250); 
   //0x2130  表示周期模式 周期1ms
   	IIC_Start();
   	IIC_Send_Byte(0x88);
   	IIC_Wait_Ack();
   	IIC_Send_Byte(0x21);
   	IIC_Wait_Ack();
   	IIC_Send_Byte(0x30);
   	IIC_Wait_Ack();
     IIC_Stop();
   
   delay_ms(150); 
}

這樣設(shè)置完畢之后,SHT30每1秒就會(huì)自動(dòng)測(cè)量一次溫度,相關(guān)寄存器就更新一次值。

2.2 SHT30的讀取數(shù)據(jù):

我加了注釋,各位看代碼的注釋:

void sht30_read_temp_humi(u8 *p)
{
//這里和前面的一樣,也是寫一個(gè)命令給SHT30,這個(gè)命令是訪問SHT30轉(zhuǎn)換結(jié)果的寄存器的
   IIC_Start();
   IIC_Send_Byte(0x88);
   IIC_Wait_Ack();
   IIC_Send_Byte(0xe0);
   IIC_Wait_Ack();
   IIC_Send_Byte(0x00);
   IIC_Wait_Ack();
//下面是開始讀取數(shù)據(jù),其中的數(shù)組p存放結(jié)果,前三個(gè)存放溫度值,后三個(gè)是濕度值,在前三個(gè)溫度值里面,
//p[0]是溫度的高八位,p[1]是低八位,p[2]是CRC校驗(yàn),有關(guān)CRC校驗(yàn)的知識(shí)我是百度上面看的,
//要是各位不懂得話,可以不用crc校驗(yàn),直接用p[0]、p[1]就可以轉(zhuǎn)換出來溫度的值。
   IIC_Start();
   IIC_Send_Byte(0x89);
   IIC_Wait_Ack();
   
   //前五次讀取都要發(fā)送ack信號(hào),最后一次就不用發(fā)了。
   p[0] = IIC_Read_Byte(1);
   p[1] = IIC_Read_Byte(1);
   p[2] = IIC_Read_Byte(1);
   p[3] = IIC_Read_Byte(1);
   p[4] = IIC_Read_Byte(1);
   p[5] = IIC_Read_Byte(0);
   IIC_Stop();
}

2.3 數(shù)據(jù)處理函數(shù)

這里面有兩個(gè)全局變量用來存放轉(zhuǎn)換的溫度和濕度數(shù)據(jù),下面我也寫了數(shù)據(jù),各位可以參考

int sht30_data_process(void)
{
  u8 temporary[3];
  u16 data;
  u8 crc_result;
  //data_process.sht30_data_buffer這個(gè)變量是我程序里面使用的存放SHT30寄存器讀到的值的數(shù)組,
  //定義類型為 unsigned short int   sht30_data_buffer[6]
  sht30_read_temp_humi(data_process.sht30_data_buffer);
  //先處理溫度的相關(guān)數(shù)據(jù),位于數(shù)組的前三個(gè)
  temporary[0]=data_process.sht30_data_buffer[0];
  temporary[1]=data_process.sht30_data_buffer[1];
  temporary[2]=data_process.sht30_data_buffer[2];
  //crc校驗(yàn)
  crc_result=sht30_crc8_check(temporary,2,temporary[2]);
  //crc校驗(yàn)要是不成功就返回1,同時(shí)不會(huì)更新溫度值
  if(crc_result==0)
  {
  //把2個(gè)8位數(shù)據(jù)拼接為一個(gè)16位的數(shù)據(jù)
  data=((uint16)temporary[0] << 8) | temporary[1];
  //溫度轉(zhuǎn)換,將16位溫度數(shù)據(jù)轉(zhuǎn)化為10進(jìn)制的溫度數(shù)據(jù),這里保留了一位小數(shù),data_process.SHT30_temperature這是一個(gè)全局變量,至于為什么變量名字里面有個(gè).不懂得各位可以百度一下c語言結(jié)構(gòu)體的相關(guān)說明。
  data_process.SHT30_temperature = (int)((175.0 * ((float)data) / 65535.0 - 45.0) *10.0);
  }
  else
  {
  return 1;
  }
  temporary[0]=data_process.sht30_data_buffer[3];
  temporary[1]=data_process.sht30_data_buffer[4];
  temporary[2]=data_process.sht30_data_buffer[5];
  //crc校驗(yàn)
  crc_result=sht30_crc8_check(temporary,2,temporary[2]);
  	if(crc_result==0)
  {
  //參考上面溫度的代碼
  data=((uint16)temporary[0] << 8) | temporary[1];
  data_process.SHT30_humidity = (int)((100.0 * (float)data / 65535.0) *10.0); 
  return 0;
  }
  else
  {
  return 2;
  }
}

2.4 CRC校驗(yàn)函數(shù)

關(guān)于CRC校驗(yàn)的相關(guān)知識(shí)可以百度查一下,我對(duì)這個(gè)了解不是很深,我只知道這就是一個(gè)校驗(yàn)碼,是確保通信過程中數(shù)據(jù)傳輸沒問題的,沒有缺少任何數(shù)據(jù),當(dāng)我們接收到數(shù)據(jù)后,按照一定的方式計(jì)算一個(gè)crc校驗(yàn)碼,經(jīng)過和提供的crc校驗(yàn)碼進(jìn)行比較,如果兩個(gè)碼一樣,那么數(shù)據(jù)就是沒問題的。以前做別的比賽的時(shí)候聽大佬說過計(jì)算過程,就寫在下面了,下面有一個(gè)數(shù)字0x31那個(gè)是crc校驗(yàn)所用的函數(shù),數(shù)據(jù)手冊(cè)上寫了使用0x31,這里就直接用了

 int  crc8_compute(u8 *check_data, u8 num_of_data)
{
 	uint8_t bit;        // bit mask
 uint8_t crc = 0xFF; // calculated checksum
 uint8_t byteCtr;    // byte counter

 // calculates 8-Bit checksum with given polynomial
 for(byteCtr = 0; byteCtr < num_of_data; byteCtr++) {
     crc ^= (check_data[byteCtr]);
 	//crc校驗(yàn),最高位是1就^0x31
     for(bit = 8; bit > 0; --bit) {
         if(crc & 0x80) {
             crc = (crc << 1) ^ 0x31;
         }  else {
             crc = (crc << 1);
         }
     }
 }
 return crc;
}
int sht30_crc8_check(u8 *p,u8 num_of_data,u8 CrcData)
{
  uint8_t crc;
  crc = crc8_compute(p, num_of_data);// calculates 8-Bit checksum
 if(crc != CrcData) 
 {   
     return 1;           
 }
 return 0;
}

最后說一下怎么使用我的這些代碼,大家可以把所有的代碼都放到一個(gè)c文件里面,然后主函數(shù)里面先初始化一下IO口,然后初始化一下SHT30,之后就可以調(diào)用一次sht30_data_process();這個(gè)函數(shù)就可以得到溫度值了(別忘記那個(gè)函數(shù)里面提到的全局變量)(我設(shè)置的模式是1s一次,所以SHT30內(nèi)部寄存器只會(huì)1s改變一次數(shù)據(jù),如果程序設(shè)置的讀取周期太快,也不會(huì)一直變化哦,也是1s一次,如果想要刷新頻率快一些,可以嘗試其他模式,不過太快了個(gè)人感覺沒什么必要)。
親自測(cè)試過了,這個(gè)代碼可以用,測(cè)得的室溫是26.1度左右,數(shù)據(jù)不是很穩(wěn)定,有0.2度左右的波動(dòng)。由于數(shù)據(jù)顯示在0.96的OLED上,太小了,照片不是很清楚,就沒圖了~
技術(shù)小白自己摸爬滾打?qū)懙拇a,希望大佬指正~~感謝感謝,也希望幫助有需要的人。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多