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