#include <stdio.h>
#include <stm32f10x.h>
#define RS_0 (GPIOA->BRR = GPIO_BRR_BR0)
#define RS_1 (GPIOA->BSRR = GPIO_BSRR_BS0)
#define RW_0 (GPIOA->BRR = GPIO_BRR_BR1)
#define RW_1 (GPIOA->BSRR = GPIO_BSRR_BS1)
#define E_0 (GPIOA->BRR = GPIO_BRR_BR2)
#define E_1 (GPIOA->BSRR = GPIO_BSRR_BS2)
#define DHT11_W0 (GPIOB->BRR = GPIO_BRR_BR0)
#define DHT11_W1 (GPIOB->BSRR = GPIO_BSRR_BS0)
#define DHT11_R ((GPIOB->IDR & GPIO_IDR_IDR0) != 0)
#define MANTISSA(n) ((uint8_t)((n) * 390625 / 1e6)) // 保留兩位小數
// 延時n毫秒(0<n<6553)
void delay_ms(uint16_t nms)
{
TIM1->ARR = nms * 10 - 1; // 從0計數到9為1ms, UIF在CNT=0時置位
TIM1->PSC = 7199; // 72000kHz/7200=10kHz -> 0.1ms
TIM1->EGR = TIM_EGR_UG; // 保存上面的設置
TIM1->SR &= ~TIM_SR_UIF; // 清UIF
TIM1->CR1 = TIM_CR1_OPM | TIM_CR1_CEN; // 開定時器, 使用非循環模式
while ((TIM1->SR & TIM_SR_UIF) == 0); // 等待UIF置位
// 返回時保留UIF=1, 下次延時時再清除
}
// 延時n微秒(0<n<65536)
void delay_us(uint16_t nus)
{
TIM1->ARR = nus - 1;
TIM1->PSC = 71; // 72MHz/72=1MHz -> 1us
TIM1->EGR = TIM_EGR_UG;
TIM1->SR &= ~TIM_SR_UIF;
TIM1->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;
while ((TIM1->SR & TIM_SR_UIF) == 0);
}
// 用於延長1602液晶E信號的高電平持續時間
void delay_short(void)
{
uint8_t i;
for (i = 0; i < 10; i++);
}
void LCD1602_BusyWait(void)
{
RS_0;
RW_1;
E_1;
GPIOC->CRL = 0x44444444; // 讀埠
while (GPIOC->IDR & GPIO_IDR_IDR7);
GPIOC->CRL = 0x33333333;
E_0;
}
void LCD1602_WriteCmd(uint8_t cmd)
{
LCD1602_BusyWait();
RS_0;
RW_0;
GPIOC->ODR = cmd;
E_1;
delay_short();
E_0;
}
void LCD1602_WriteData(uint8_t data)
{
LCD1602_BusyWait();
RS_1;
RW_0;
GPIOC->ODR = data;
E_1;
delay_short();
E_0;
}
void LCD1602_Init(void)
{
LCD1602_WriteCmd(0x38);
LCD1602_WriteCmd(0x01);
LCD1602_WriteCmd(0x0c);
DAC->CR = DAC_CR_EN1 | DAC_CR_EN2;
DAC->DHR8R1 = 69; // 對比度電壓: 約1.0V, 埠: PA4
DAC->DHR8R2 = DAC->DHR8R1; // PA5輸出同樣大小的電壓
}
// printf內容往液晶上顯示
// 工程屬性里的Use MicroLIB必須打勾
int fputc(int ch, FILE *fp)
{
LCD1602_WriteData(ch);
return ch;
}
int8_t DHT11_Read(uint8_t data[])
{
uint8_t i, j;
DHT11_W1;
GPIOB->CRL = (GPIOB->CRL & 0xfffffff0) | 7; // 開漏輸出50MHz
DHT11_W0;
delay_ms(18);
DHT11_W1;
delay_us(30);
GPIOB->CRL = (GPIOB->CRL & 0xfffffff0) | 4; // 浮空輸入
if (!DHT11_R)
{
delay_us(80);
if (DHT11_R)
{
for (i = 0; i < 5; i++)
{
data[i] = 0;
for (j = 0; j < 8; j++)
{
while (!DHT11_R); // 等待低電平
// 對高電平計時
TIM1->ARR = TIM_ARR_ARR; // 最大超時時間65.536ms
TIM1->PSC = 71;
TIM1->EGR = TIM_EGR_UG; // UG=1會自動將CNT清零, 但會把UIF置1
TIM1->SR &= ~TIM_SR_UIF;
TIM1->CR1 = TIM_CR1_OPM | TIM_CR1_CEN;
while (DHT11_R) // 等待高電平
{
if (TIM1->SR & TIM_SR_UIF)
return i; // 高電平超時, 返回實際讀取的數據個數
}
data[i] <<= 1;
if (TIM1->CNT >= 50) // CNT+1為測出的us數
data[i] |= 1;
}
}
while (!DHT11_R);
// 數據檢驗
if (((data[0] + data[1] + data[2] + data[3]) & 0xff) != data[4])
return -2; // 數據校驗出錯
return i; // 成功
}
}
return -1; // 傳感器出錯, 未讀到數據
}
int main(void)
{
int8_t n;
uint8_t data[5];
RCC->APB1ENR = RCC_APB1ENR_DACEN;
RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_TIM1EN;
GPIOA->CRL = 0x00000333;
GPIOB->CRL = 0x00000004;
GPIOC->CRL = 0x33333333;
LCD1602_Init();
while (1)
{
LCD1602_WriteCmd(0x80); // 跳到第一行
n = DHT11_Read(data);
if (n == 5 || n == -2)
{
// 以小數方式顯示濕度(百分數*256=小數)
//printf("H:%d.%02d T:%d.%02d ", data[0], MANTISSA(data[1]), (int8_t)data[2], MANTISSA(data[3]));
// 以百分數方式顯示濕度
printf("H:%d%% T:%d.%02d ", data[0] * 100 / 256, (int8_t)data[2], MANTISSA(data[3]));
LCD1602_WriteCmd(0xc0); // 換行
printf("Sum:%d Recv:%d%c ", (data[0] + data[1] + data[2] + data[3]) & 0xff, data[4], (n == -2) ? '!' : ' ');
// 感嘆號表示校驗錯誤
// 校驗錯誤一般可忽略,因為得到的溫濕度值是正確的
}
else
{
// 讀數據時出錯
printf("Error! ");
LCD1602_WriteCmd(0xc0);
printf("n=%d ", n);
}
delay_ms(1000);
}
}