#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); } }
|