Lab5 : uC/OS室溫計 - 快來再點個燈

實驗環(huán)境為Window10 + Keil 5.18,代碼中使用標準庫。

實驗準備

本次實驗硬件上需要七段數(shù)碼管LG3641BH,溫濕度傳感器DHT11,點燈用的小燈一個,面包板一塊加導線若干。

由于手頭公母線似乎不是很夠用,我把整個STM32都插到了面包板上,然后發(fā)現(xiàn)這么搞好像也蠻科學的樣子,整體連線清晰很多。

而軟件則需要標準庫的支持代碼以及uCOS-II源碼了。

標準庫代碼在Lab3的準備過程中已經(jīng)載入Keil。

uCOS-II的源碼可以在文末下載鏈接中下載。下載需要注冊一個賬號,順著寫就好了。吐槽一下,注冊的時候Phone Number竟然不需要填數(shù)字……有反饋說有的郵箱可能收不到注冊回執(zhí),我使用的是163郵箱進行注冊。

解壓

由于這個uCOS-II源碼包用的是標準庫而不是HAL,順勢而為也就改成標準庫了。雖然大同小異,不過函數(shù)名字換了一遍還是有點難受的。

本文沒有使用STM32CubeMX。

實驗步驟

0. 點燈

還是老步驟,拿到不會用的東西,先點個燈。

點燈程序

這次的點燈準備發(fā)揮uCOS-II的特長,一次性點兩個燈。有的小燈比較脆弱,接的時候連個電阻比較保險。不然有可能燒壞。

直接新建Keil工程,選擇好板子型號后進入運行環(huán)境選擇頁面。選擇需要的環(huán)境后便進入了工程。

Project Run-Time Environment

在工程內(nèi)寫入app.c文件即可。

#include "stm32f10x.h"
#include "stm32f10x_conf.h"

void GPIO_Configuration(void){
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_DeInit();
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void Delay_long(int times){
    unsigned int i, j;
    for (j=0; j<times; j++){
        for (i=0; i<0x3ffff; i++){
        }
    }
}

int main() {
    GPIO_Configuration();
    while(1){
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
        GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_SET);
        Delay_long(10);
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
        GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_RESET);
        Delay_long(10);
    }
}

這份代碼目前和uCOS-II一點關(guān)系都沒有,僅僅是為了測試小燈以及電路的通暢,順便再熟悉一下標準庫的GPIO操作。

初始化的函數(shù)中初始化了兩個GPIO口PA11以及PC13,分別對應接下來要點的兩個燈,其中PA11口的燈是外接的,而PC13口的燈為板子自帶。

在出了一些狀況之后,兩個小燈順利得開始了閃爍。

uCOS-II工程創(chuàng)建

工程創(chuàng)建步驟基本完全按照參考資料中一步一步移植ucos到stm32f103開發(fā)版(修訂版)所述步驟。當然,也別完全照著來,板子類型還是需要選自己的。同時,我用的是自己實驗準備中下載的源碼包,文中給出的那個并沒有使用過。

修訂主要是略微增加對keil功能的利用,提高對編譯器功能的利用可以提高建工程的速度和減少定義沖突

先到官方下載ucos源碼,比較接近的是micrium_stm32f103-sk_ucos-ii,本文就采用該文件

開發(fā)工具版本為MDK511

1.新建ucos工程,選擇STM32F103VE,選擇CMSIS下的CORE和Device下的Startup,以及Device下的StdPeriph Drivers下的Framework,RCC,和GPIO

2.工程中和實際目錄中都新建幾個目錄,APP,UCOS,BSP,LIB,CPU,Output

3.工程上右鍵,Options,Output頁簽,Select Foldeer for Objects,進入Output目錄,點擊OK

4.把Micrium\Software\uCOS-II\Source目錄中的文件拷貝到UCOS目錄下,并添加到工程中

5.工程Options中,C/C++頁簽,Include Paths,點擊后面省略號可選擇include目錄,添加UCOS路徑

6.復制Micrium\Software\EvalBoards\ST\STM3210B-EVAL\RVMDK\OS-Probe目錄下的文件app_cfg.h,os_cfg.h和includes.h到APP目錄中,并在Include Paths中添加APP

7.復制Micrium\Software\uCOS-II\Ports\arm-cortex-m3\Generic\RealView目錄下的所有文件到CPU目錄,添加到工程和Include Path中

8.工程Options中,C/C++頁簽,Defines中添加 USE_STDPERIPH_DRIVER

9.把RTE和RTE\Device\STM32F103VE添加進Include Paths中

10.修改os_cfg.h文件,#define OS_APP_HOOKS_EN 1為0

11.BSP目錄下新建BSP.c文件,添加內(nèi)容如下:

#include <bsp.h>
CPU_INT32U  BSP_CPU_ClkFreq (void) {
    RCC_ClocksTypeDef  rcc_clocks;
    RCC_GetClocksFreq(&rcc_clocks);
    return ((CPU_INT32U)rcc_clocks.HCLK_Frequency);
}
INT32U  OS_CPU_SysTickClkFreq (void) {
    INT32U  freq;
    freq = BSP_CPU_ClkFreq();
    return (freq);
}

12.復制Micrium\Software\EvalBoards\ST\STM3210B-EVAL\RVMDK\BSP目錄下的bsp.h到 BSP目錄中

13.復制Micrium\Software\uC-CPU\ARM-Cortex-M3\RealView目錄和Micrium\Software\uC-CPU目錄下的所有文件到CPU目錄下,并添加到工程和Include Path中

14.復制Micrium\Software\uC-LIB目錄下的所有.h文件到LIB目錄下,并添加到Include Path中

15.注釋掉bsp.h中的#include <stm32f10x_lib.h>和#include <lcd.h>

16.app_cfg.h文件中,修改為#define APP_OS_PROBE_EN 0

17.APP目錄下新建app.c文件,內(nèi)容為

#include <includes.h>
int main(){
  OSInit();
  OSStart();
  return 0;
}

18.注釋掉includes.h文件中的#include <stm32f10x_lib.h>和#include <lcd.h>

至此,第一步準備工作完成,雖然未實現(xiàn)任何功能,至少編譯不再報錯了

按照上面說的教程步驟跑一遍之后的工程文件及部分配置如下。

代碼清單
代碼清單
options - C/C++

但是此時可能還會有一些地方無法通過編譯。然后七翻八翻找到這個位置,發(fā)現(xiàn)兩個DEF_ENABLED都得置為0。

define置零

然后終于編譯過了。

uCOS-II多任務點燈

小燈的閃爍實際上可以看成是兩個獨立的事件,即使用兩個任務分別點燈即可。

void LED0_task(void* pdata){
    while(1){
        GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_SET);
        Delay_long(5);
        GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_RESET);
        Delay_long(5);
    }
}

void LED1_task(void* pdata){
    while(1){
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
        Delay_long(10);
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
        Delay_long(10);
    }
}

#define STK_Size 100
int LED0_Task_STK[STK_Size];
int LED1_Task_STK[STK_Size];
int Task_STK[STK_Size];

int main(){
    OS_CPU_SR cpu_sr=0;
    GPIO_Configuration();
    OSInit();
    OS_CPU_SysTickInit();
    OSTaskCreate(
        LED0_task,
        (void *)0,
        (OS_STK *)&LED0_Task_STK[STK_Size-1],
        1
    );
    OSTaskCreate(
        LED1_task,
        (void *)0,
        (OS_STK *)&LED1_Task_STK[STK_Size-1],
        2
    );
    OSStart();
    return 0;
}

uCOS-II中使用OSTaskCreate創(chuàng)建任務。該函數(shù)接收4個參數(shù),第一個參數(shù)是任務執(zhí)行的函數(shù),第二個參數(shù)是附帶的參數(shù),只有1個指針的位置,第三個參數(shù)是該任務使用的棧的位置,第四個參數(shù)是任務的優(yōu)先級。

而在執(zhí)行了OSStart后,uCOS-II會對任務進行調(diào)度。

看起來一切正常,編譯通過之后下板子本以為會看到燈閃爍起來的,然后程序就死掉了。

程序調(diào)錯

經(jīng)過單步調(diào)試,發(fā)現(xiàn)OSStart最終會進入os_cpu_a.asm的OSStartHighRdy函數(shù)中。在順序執(zhí)行之后,死在了OSStartHang上。

……
    CPSIE   I                      ; Enable interrupts at processor level

OSStartHang
    B       OSStartHang            ; Should never get here
……

感覺到了來自系統(tǒng)深深的惡意。不過肯定是哪里有問題。使用這個函數(shù)名進行百度之后發(fā)現(xiàn)——大家的程序原來都死在這了。

而在這個地方死循環(huán)的原因是uCOS-II所需的兩個中斷函數(shù)沒有調(diào)用到。

所以,根據(jù)各種教程,把兩個缺失的中斷函數(shù)補上即可。兩個中斷為startup_stm32f10x_md.s中的PendSV Handler 以及 SysTick Handler。

有兩種方法,第一種是自己寫一個函數(shù)接收中斷并向uCOS-II內(nèi)核傳遞消息,第二種方法是直接使用uCOS-II自帶的中斷函數(shù)替換原本的函數(shù)。

void SysTick_Handler(void){
    OS_CPU_SR  cpu_sr;

    OS_ENTER_CRITICAL();    // Tell uC/OS-II that we are starting an ISR
    OSIntNesting++;
    OS_EXIT_CRITICAL();
    
    OSTimeTick();  // Call uC/OS-II's OSTimeTick()  
    OSIntExit();   // Tell uC/OS-II that we are leaving the ISR
}
使用uCOS-II的OS_CPU_PendSVHandler替換中斷函數(shù)PendSV_Handler

解決了中斷問題之后,編譯,下載…………然后我一臉懵逼的看到只有一盞燈在閃。

那還是有問題嘍。_(:з」∠)_

經(jīng)過百度,發(fā)現(xiàn)類似于while(i--);之類的延時函數(shù)在uCOS-II中是屬于阻塞的延遲,即任務不會在這個時間點進行切換。而任務切換的方式需要調(diào)用uCOS-II中自帶的延時函數(shù)。

此時,調(diào)整Delay_long函數(shù)即可。

void Delay_long(int times){
    /*unsigned int i, j;
    for (j=0; j<times; j++){
        for (i=0; i<0x3ffff; i++){
        }
    }*/
    OSTimeDly(OS_TICKS_PER_SEC/10 * times);
}

至此,點燈成功。

小燈各自閃爍中……

1. 七段數(shù)碼管

我們用的型號為LG3641BH的七段數(shù)碼管是一款共陽極的數(shù)碼管。從LG3641BH Datasheet中可以找到該款元件的基本信息。

手動點亮數(shù)碼管

而為了使七段數(shù)碼管的四個數(shù)字同時亮起,使用時分復用的方式,高頻率點亮每個數(shù)碼管,使人眼看起來所有的數(shù)字都是同時亮起的。

咳咳,下面這倆ppt是邏輯課上的,所以別吐槽那個Spartan III。

七段數(shù)碼管顯示原理
七段數(shù)碼管顯示原理

據(jù)此可以寫出對應的顯示模塊。電路各種花式連線均可,只要能和程序?qū)纳稀R韵率俏业倪B法。

引腳對應關(guān)系

12, 9, 8, 6引腳分別是從左到右4個數(shù)字的陽極,屬于位選引腳,即輸出高電平意為選擇。這四個引腳分別對應stm32上的PA11, PA12, PC13, PC14。

而剩下的引腳為控制具體單段數(shù)碼管的引腳。標號0到7的引腳分別與PA0至PA7相連。

據(jù)此,可以寫出點亮七段數(shù)碼管的程序。

void GPIO_Configuration(void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_DeInit();
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);
    
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13
                                | GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11
                                | GPIO_Pin_12;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0
                                | GPIO_Pin_1
                                | GPIO_Pin_2
                                | GPIO_Pin_3
                                | GPIO_Pin_4
                                | GPIO_Pin_5
                                | GPIO_Pin_6
                                | GPIO_Pin_7;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void Delay_ms(int times){
    OSTimeDly(OS_TICKS_PER_SEC / 1000 * times);
}

void digit_select(int index){
    // 通過輸出高電平選擇點亮某個數(shù)字
    BitAction v[4];
    int i;
    for (i=0; i<4; i++){
        if (index == i){
            v[i] = Bit_SET;
        }else{
            v[i] = Bit_RESET;
        }
    }
    GPIO_WriteBit(GPIOA, GPIO_Pin_11, v[0]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_12, v[1]);
    GPIO_WriteBit(GPIOC, GPIO_Pin_13, v[2]);
    GPIO_WriteBit(GPIOC, GPIO_Pin_14, v[3]);
}

void digit_show(int dight, int point){
    // 通過對引腳輸出低電平點亮數(shù)碼管段
    int segment, i, base;
    BitAction v[8];
    switch (dight){
        case    0   :   segment = 0xee; break; // 0b11101110  0 -> 7
        case    1   :   segment = 0x24; break; // 0b00100100
        case    2   :   segment = 0xba; break; // 0b10111010
        case    3   :   segment = 0xb6; break; // 0b10110110
        case    4   :   segment = 0x74; break; // 0b01110100
        case    5   :   segment = 0xd6; break; // 0b11010110
        case    6   :   segment = 0xde; break; // 0b11011110
        case    7   :   segment = 0xa4; break; // 0b10100100
        case    8   :   segment = 0xfe; break; // 0b11111110
        case    9   :   segment = 0xf6; break; // 0b11110110
        default     :   segment = 0xda; break; // 0b11011010 error state
    }
    segment |= point != 0; // 小數(shù)點為最低位
    base = 1 << 8;
    for (i=0; i<8; i++){
        base >>= 1;
        // segment中某位的1表示點亮,而輸出低電平為點亮
        if ((segment & base )== 0){
            v[i] = Bit_SET;
        }else{
            v[i] = Bit_RESET;
        }
    }
    GPIO_WriteBit(GPIOA, GPIO_Pin_0, v[0]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_1, v[1]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_2, v[2]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_3, v[3]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_4, v[4]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_5, v[5]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_6, v[6]);
    GPIO_WriteBit(GPIOA, GPIO_Pin_7, v[7]);
}

void led_show(int digit){
    // 時分復用的方式輸出數(shù)字,每次調(diào)用led_show只輸出一位數(shù)字
    static int index = -1;
    int i;
    int base = 1000;
    index = (index + 1) % 4;
    for (i=0; i<index; i++){
        base /= 10;
    }
    digit = (digit / base) % 10;
    digit_select(index);
    digit_show(digit, 0);
}

int ledValue = 0; // 這個值由task0寫入,task1讀取,都是單向的,所以可以不考慮線程之間的沖突

void LED0_task(void* pdata){
    while (1){
        ledValue++;
        Delay_ms(200);
    }
}

void LED1_task(void* pdata){
    while(1){
        led_show(ledValue);
        Delay_ms(7);
    }
}

程序使用兩個task完成全部功能。首先是LED0_task,該任務每隔200ms修改一次ledValue的值,即修改需要顯示的值。而LED1_task每次顯示一位ledValue的值。

程序運行結(jié)果

2. DHT11數(shù)據(jù)讀取及顯示

DHT11是一款有已校準數(shù)字信號輸出的溫濕度傳感器。作為一種單總線設備,輸入輸出均為同一個引腳。

使用DHT11的時候,需要連接三個引腳,引腳1接VCC,引腳2接是stm32的某個GPIO口,引腳4接地。而引腳3懸空。

對DHT11來說,數(shù)據(jù)的傳輸步驟如下:

  1. stm32輸出低電平至少18ms(只有此處為ms,其余均為μs)。
  2. stm32輸出高電平20~40μs
  3. DHT11反饋低電平80μs
  4. DHT11反饋高電平80μs
  5. 以上為雙方握手,以下開始準備接受數(shù)據(jù)。數(shù)據(jù)總長40個bit,輸入為大端輸入,即高位的bit先進行傳輸。每個byte表示一個數(shù)值。按照接受順序分別表示濕度整數(shù),濕度小數(shù),溫度整數(shù),溫度小數(shù),校驗碼。校驗碼為前方四個byte的和。
  6. 對于每個傳輸?shù)腷it,DHT11會首先輸出50μs的低電平
  7. 而后以輸出高電平的時間決定每個bit的值。高電平持續(xù)時間為20~30μs的為bit 0,高電平持續(xù)時間為70μs的表示bit 1。

整個通信過程大約耗時4ms。

圖來自[Stm32程序控制DHT11](http://wenku.baidu.com/link?url=q45Du2opi0SljsMJGpJYJ8zrlNIjyEeLyYH-1aIrJG-s2PQAVEBpfGYKAtw5ChELNGy4s5YAIGhA470sRDyTWnZm6XwpKFWS2heoAJHjmWi)

根據(jù)傳輸協(xié)議寫成程序即可。

#define MAX_TICS 100000
#define DHT11_OK 0
#define DHT11_NO_CONN 1
#define DHT11_CS_ERROR 2
#define DHT11_PORT GPIOB
#define DHT11_PIN GPIO_Pin_0

void Delay_us(int times){
    // 我自己測大約這個delay函數(shù)需要的時間是1.4us * @times
    unsigned int i;
    for (i=0; i<times; i++){
        // 外層循環(huán)一次差不多1us,不過不是特別精確,所以不需要內(nèi)層的循環(huán)了
        /*
        unsigned int j;
        for (j=0; j<0x3fff; j++){
        }
        */
    }
}

void ErrorState(int state){
    // 使用死循環(huán)自殺,并顯示錯誤碼
    while (1){
        led_show(state);
        Delay_us(4000);
    }
}

void DHT11_Set(int state){
    // 設置DHT11 GPIO口的值
    BitAction s;
    if (state){
        s = Bit_SET;
    }else{
        s = Bit_RESET;
    }
    GPIO_WriteBit(DHT11_PORT, DHT11_PIN, s);
}

void DHT11_Pin_OUT(){
    // 調(diào)整DHT11 GPIO口為輸出模式
    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
    
    DHT11_Set(1);
}

void DHT11_Pin_IN(){
    // 調(diào)整DHT11_GPIO口為輸入模式
    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
    
    DHT11_Set(1);
}

uint8_t DHT11_Check(){
    // 獲取DHT11 GPIO口的數(shù)據(jù),下方算式與調(diào)用函數(shù)等價
    return (DHT11_PORT->IDR & DHT11_PIN)> 0;
    //return GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN);
}

void DHT11_Wait(int state, int place){
    // 等待GPIO口變?yōu)閟tate的值,超時自動自殺
    int loopCnt = MAX_TICS;
    while (DHT11_Check() != state){
        if (loopCnt -- == 0){
            ErrorState(1000 + state * 1000 + place);
        }
    }
}

void DHT11_Rst(){
    // stm32端輸出握手信號
    DHT11_Pin_OUT();
    DHT11_Set(0);
    Delay_us(25000);
    DHT11_Set(1);
    Delay_us(40);
    DHT11_Set(0);
    // 轉(zhuǎn)為接收模式準備讀入DHT11的握手信號
    DHT11_Pin_IN();
}

int val = 10;

uint8_t DHT11_Read_Byte(){
    // 讀入一個Byte
    int i, cnt;
    uint8_t data = 0;
    for (i=0; i<8; i++){
        cnt = 0;
        data <<= 1;
        // 當前為低電平,等待高電平
        DHT11_Wait(1, ++val);
        
        // 計算高電平持續(xù)的時間
        while (DHT11_Check() > 0){
            Delay_us(1);
            cnt++;
        }
        // 持續(xù)的足夠久則為bit 1
        data |= cnt > 5;
    }
    return data;
}

uint8_t DHT11_Read_Data(uint8_t *buf){
    // 從DHT11內(nèi)讀取數(shù)據(jù)的函數(shù)
    int i;
    unsigned int cpu_sr;
    // 為了關(guān)閉中斷進入臨界區(qū)
    OS_ENTER_CRITICAL();
    val = 10;
    // 發(fā)送握手消息
    DHT11_Rst();
    // 如果給予了回復
    if (DHT11_Check() == 0){
        // 等待低電平過去
        DHT11_Wait(1, 2);
        // 等待高電平過去
        DHT11_Wait(0, 3);
        // 握手完成,開始讀取40個bit
        for (i=0; i<5; i++){
            buf[i] = DHT11_Read_Byte();
        }
        
        // 重新將GPIO口置為輸出模式
        DHT11_Pin_OUT();
        OS_EXIT_CRITICAL(); 
        
        // 判斷校驗和是否滿足要求
        if (buf[0] + buf[1] + buf[2] + buf[3] == buf[4]){
            return DHT11_OK;
        }else{
            return DHT11_CS_ERROR;
        }
    }else{
        // 該分支表示沒有收到回復
        OS_EXIT_CRITICAL(); 
        return DHT11_NO_CONN;
    }
}

uint8_t DHT11_Humidity(uint8_t *buf){
    // 返回濕度
    return buf[0];
}

uint8_t DHT11_Temperature(uint8_t *buf){
    // 返回溫度
    return buf[2];
}

值得一提的是,上述代碼在出現(xiàn)異常情況的時候會直接進入ErrorState自殺,此時會在連入的LED上顯示錯誤號,根據(jù)錯誤號判斷進入ErrorState的位置進行debug即可。

在實踐過程中,如果DHT11不聽話,可以嘗試/多次嘗試/循環(huán)嘗試/隨機嘗試/組合嘗試以下幾種方法:

  1. 換幾根線
  2. 換一個DHT11
  3. 換一個STM32的引腳
  4. 換一塊面包板
  5. 玄學調(diào)參
  6. 換一個程序
  7. 換一個STM32
  8. 去吃個飯
  9. 去西湖散散心
  10. 換一個實驗

程序內(nèi)使用時直接調(diào)用DHT11_Read_Data函數(shù)進行讀取即可。

void LED0_task(void* pdata){
    uint8_t buf[5];
    int state;
    memset(buf, 0, sizeof(buf));
    
    while (1){
        state = DHT11_Read_Data(buf);
        switch(state){
            case DHT11_CS_ERROR:
                ledValue = 9002;
                break;
            case DHT11_NO_CONN:
                ledValue = 9001;
                break;
            case DHT11_OK:
                ledValue = DHT11_Temperature(buf);
                break;
        }
        Delay_ms(1000);
    }
}
正常顯示
使用電吹風后

參考資料:

下載鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容