完成一个仓储环境监测的模拟系统。具体要求如下:
1. DHT11模块完成温湿度测量,5s更新,数值显示于数码管,同时打印到串口。温度超过阈值触发蜂鸣器短鸣,同时启动风扇小马达;
2. LDR模块完成环境照度测量,4s更新,数值显示于数码管,同时打印到串口。照度超过阈值启动PWM调光(高、中、低三级调光)。
3. 按键循环切换显示模式,按第1下固定显示温度,按第二下固定显示照度,按第三下恢复默认的自动显示模式。按键用外部中断实现
int main(void)
{
systick_init(72);
led_init();
usart1_init(115200);
display_init();
tim4_init(1000,72); // 1ms的定时间隔
TIM3_CH2_PWM_Init(500-1,72-1);
dht11_init();
ldr_init();
//key_init();
while(1)
{
// 轮询DHT11的状态
if(dht11.state == WORK)
{
if(dht11.getData(&dht11.temp, &dht11.humi) == SUCCESS) // DHT11温湿度数据更新
{
printf("temperature:%d℃ humidity(RH):%d \r\n", dht11.temp, dht11.humi);
dsp.interface = TEMP;
dht11.onTempChange(dht11.temp_threshold); // 温度超过阈值的处理
}
dht11.state = STANDBY;
}
// 轮询LDR的状态
if(ldr.state == WORK)
{
ldr.light = ldr.getData();
printf("light intensity:%d \r\n", ldr.light);
dsp.interface = LIGHT;
ldr.onLightChange(ldr.high_threshold,ldr.low_threshold); // 照度超过阈值的处理
ldr.state = STANDBY;
}
// 轮询数码管界面状态
switch(dsp.interface)
{
case LIGHT: setLightValue(&dsp,1); break;
case TEMP: setTempValue(&dsp,1); break;
case HUMI: setHumiValue(&dsp,1); break;
default: break;
}
}
}#include "systick.h"
static u8 fac_us=0; //us延时倍乘数
static u16 fac_ms=0; //ms延时倍乘数
//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void systick_init(u8 SYSCLK)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us = SYSCLK/8;
fac_ms = (u16)fac_us * 1000;
}
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD = (u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL = 0x00; //清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}
while((temp&0x01) && !(temp&(1<<16))); //等待时间到达
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL = 0x00; //清空计数器
}
#include "display.h"
DisplayStruct dsp;
void segDisplay(void);
// 共阴极数码管,阴极位选(0有效),阳极段选(1有效)
const u16 segCode[11] = {
/* 0 1 2 3 4 5 6 7 */
0x003F, 0x0006, 0x005B, 0x004F, 0x0066, 0x006D, 0x007D, 0x0007,
/* 8 9 off */
0x007F, 0x006F, 0x0000 };
/*******************************************************************************
* @brief 初始化数码管用到的GPIO端口和dsp结构体
* @param None
* @retval None
*******************************************************************************/
void display_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
// PA0~PA7: 段选(分别连接引脚A~G);
// PB12~PB15:位选(分别连接引脚D1~D4)
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_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
dsp.currDigit = 1; // dsp结构体的初始化
dsp.SegDisplay = segDisplay;
}
/***********************************************************************************
* @brief 设置数码管要显示的4位数值。
* @param dsp:数码管结构体指针;value[4]:4位数值;dotBit:小数点位置(没有小数点则置0)
* @retval None
***********************************************************************************/
void setValue(DisplayStruct *dsp, u8 value[4], u8 dotBit)
{
dsp->digit[0] = value[0];
dsp->digit[1] = value[1];
dsp->digit[2] = value[2];
dsp->digit[3] = value[3];
dsp->dotDigit = dotBit;
}
void setLightValue(DisplayStruct *dsp, u8 dotBit)
{
dsp->digit[0] = 1; // 参数编号
dsp->digit[1] = 10; // 第二位空缺
dsp->digit[2] = ldr.light/10;
dsp->digit[3] = ldr.light%10; //
dsp->dotDigit = dotBit; // 小数点位置
}
void setTempValue(DisplayStruct *dsp, u8 dotBit)
{
dsp->digit[0] = 2;
dsp->digit[1] = 10;
dsp->digit[2] = dht11.temp/10;
dsp->digit[3] = dht11.temp%10;
dsp->dotDigit = dotBit;
}
void setHumiValue(DisplayStruct *dsp, u8 dotBit)
{
dsp->digit[0] = 3;
dsp->digit[1] = 10;
dsp->digit[2] = dht11.humi/10;
dsp->digit[3] = dht11.humi%10;
dsp->dotDigit = dotBit;
}
/*******************************************************************************
* @简 介 段选,让某一位数码管显示指定的数字
* @输入参数 value:段码数组的序号(0~9),代表要显示的数字
* @retval
* 备注:只操作PA的低8位,不要影响高8位。
********************************************************************************/
void seg(uint8_t value)
{
if(value < sizeof(segCode)) // 避免数组溢出
{
GPIOA->ODR &= 0xFF00;
GPIOA->ODR |= segCode[value];
}
}
/********************************************************************************
* 简 介 位选,一次只能选一位。
* 输入参数 com:位数(取值1~4,从左到右)
* 返回值 无
* 备注:只操作PB的高4位(置0),不要影响其他位。
********************************************************************************/
void com(uint8_t currDigit)
{
// 数码管位选(0有效)
GPIOB->ODR |= 0xF000;
GPIOB->ODR &= ~(0x1000<<(currDigit-1));
}
/********************************************************************************
* 简介 在数码管上逐位显示其段码(由Timer中断服务函数调用)
* 参数 无
* 返回值 无
********************************************************************************/
void segDisplay(void)
{
seg(10); // 消隐之前的显示内容
com(dsp.currDigit); // 位选,从COM1到COM4逐次移位,实现动态显示
if(dsp.currDigit != dsp.dotDigit) // 当前位不显示小数点
seg(dsp.digit[dsp.currDigit-1]);
else // 当前位要显示小数点
{
GPIO_Write(GPIOA, segCode[dsp.digit[dsp.currDigit-1]]| 0x0080);
//GPIOA->ODR &= 0xFF00;
//GPIOA->ODR |= (segCode[dsp.currDigit-1]|0x0080);
}
if(++dsp.currDigit > DSP_DIGIT) // 从当前位右移到下一位
dsp.currDigit = 1;
}
#include "usart.h"
int fputc(int ch,FILE *p) //函数默认的,在使用printf函数时自动调用
{
USART_SendData(USART1,(u8)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
return ch;
}
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART1_RX_STA=0; //接收状态标记
/*******************************************************************************
* 函 数 名 : USART1_Init
* 函数功能 : USART1初始化函数
* 输 入 : bound:波特率
* 输 出 : 无
*******************************************************************************/
void usart1_init(u32 baud)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //串口输出TX:PA9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; //串口输入RX:PA10
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
GPIO_Init(GPIOA,&GPIO_InitStructure);
//USART1 初始化设置
USART_InitStructure.USART_BaudRate = baud; //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}
/*******************************************************************************
* 函 数 名 : USART1_IRQHandler
* 函数功能 : USART1中断函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 r;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
if((USART1_RX_STA&0x8000)==0)//接收未完成
{
if(USART1_RX_STA&0x4000)//接收到了0x0d
{
if(r!=0x0a)USART1_RX_STA=0;//接收错误,重新开始
else USART1_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(r==0x0d)USART1_RX_STA|=0x4000;
else
{
USART1_RX_BUF[USART1_RX_STA&0X3FFF]=r;
USART1_RX_STA++;
if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
#include "timer.h"
#include "beep.h"
volatile uint16_t beep_time;
/*******************************************************************************
* 函 数 名 : tim4_init
* 函数功能 : TIM4初始化函数
* 输 入 : per:重装载值
psc:分频系数
* 输 出 : 无
*******************************************************************************/
void tim4_init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); // 使能TIM4的时钟
// 配置TIM的时基参数
TIM_TimeBaseInitStructure.TIM_Period = per; // 自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 分频系数
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置向上计数模式
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure); // 设置参数生效
// 配置中断参数
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); // 开启定时器的更新中断
TIM_ClearITPendingBit(TIM4,TIM_IT_Update); // 中断标志位清零
// 配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组(组2)
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; // 定时器4的中断通道(30号通道)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 中断通道使能
NVIC_Init(&NVIC_InitStructure); // NVIC设置参数生效
TIM_Cmd(TIM4,ENABLE); // 使能Timer4
}
/*******************************************************************************
* 函 数 名 : TIM4_IRQHandler
* 函数功能 : TIM4中断函数
* 输 入 : 无
* 输 出 : 无
* 备 注 : 定时间隔1ms
** *****************************************************************************/
void TIM4_IRQHandler(void)
{
static u16 cnt_dht11;
static u16 cnt_ldr;
// 蜂鸣器时间处理
if(beep_time > 0)
{
beep_time--;
if(beep_time == 0)
{
BEEP_OFF();
}
}
if(TIM_GetITStatus(TIM4,TIM_IT_Update)) // 检查更新中断是否产生
{
// 处理定时任务
if(++cnt_dht11 == dht11.period)
{
if(dht11.state == STANDBY)
{
dht11.state = WORK; // DHT11状态从STANDBY切换到WORK
}
cnt_dht11 = 0;
}
if(++cnt_ldr == ldr.period)
{
if (ldr.state == STANDBY)
{
ldr.state = WORK; // LDR状态从STANDBY切换到WORK
}
cnt_ldr = 0;
}
// 刷新数码管显示
dsp.SegDisplay();
}
TIM_ClearITPendingBit(TIM4,TIM_IT_Update); // 手动清除中断标志位
}
#include "dht11.h"
#include "relay.h"
#include "beep.h"
Dht11Struct dht11 = {0};
void SET_DHT11_IO_OUT(void);
void SET_DHT11_IO_IN(void);
void DHT11_RequestData(void);
u8 DHT11_RespondRequest(void);
u8 DHT11_Read_Data(u8 *temp, u8 *humi);
void DHT11_OnTempChange(u8 temp_threshold);
// DHT11_PIN初始化
// 返回值: 无
extern void dht11_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
// 数据引脚初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 数据引脚
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
GPIO_SetBits(DHT11_PORT, DHT11_PIN); // 初始输出状态:高电平
// 结构体初始化
dht11.period = DHT11_PERIOD;
dht11.temp_threshold = TEMP_THRESHOLD;
dht11.getData = DHT11_Read_Data; // 获取温湿度数据的函数
dht11.onTempChange = DHT11_OnTempChange; // 温度超过阈值的处理函数
// 做一次数据请求测试,检查设备状态
DHT11_RequestData();
if (DHT11_RespondRequest() == SUCCESS)
{
dht11.state = STANDBY;
printf("DHT11 Init OK!\r\n");
}
else
{
dht11.state = FAIL;
printf("DHT11 Check Error!\r\n");
}
}
// 复位DHT11,单片机向DHT11发起数据采集请求
// 时序图的黑线部分
static void DHT11_RequestData()
{
SET_DHT11_IO_OUT(); // 数据引脚配置为输出模式
DHT11_DQ_OUT = 0;
delay_ms(20); // 低电平持续至少18ms
DHT11_DQ_OUT = 1;
delay_us(30); // 高电平持续20~40us
}
// DHT11响应单片机的数据请求
// 返回值:SUCCESS or FAILURE
static u8 DHT11_RespondRequest()
{
u8 retry=0;
SET_DHT11_IO_IN(); // 数据引脚设为输入模式,接收DHT11的响应
while (DHT11_DQ_IN && retry<100) // 等待输入由高变低
{
retry++;
if(retry >= 100) return FAILURE; // 等待时间过长,返回异常。
delay_us(1);
}
retry=0;
while (!DHT11_DQ_IN && retry<100) // 低电平持续时间80us
{
retry++;
if(retry >= 100) return FAILURE; // 低电平持续时间过长,返回异常。
delay_us(1);
}
return SUCCESS;
}
//DHT11输出模式配置
static void SET_DHT11_IO_OUT()
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(DHT11_PORT,&GPIO_InitStructure);
}
//DHT11输入模式配置
static void SET_DHT11_IO_IN()
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入模式,最小系统板和DHT11之间并未接上拉电阻
GPIO_Init(DHT11_PORT,&GPIO_InitStructure);
}
//从DHT11读取一位
//返回值:bit 1或0
//注:此函数处并未做高低电平持续时间异常的处理,因DHT11自身有校验和,即便读错1位也不会造成最终数据的错误。
// 严格来讲,除了正常情况下返回“1”或“0”,还应增加异常情况的返回值(比如“2”)。
static u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN && retry<100) // 等待输入电平由高变低(低电平持续50us)
{
retry++;
delay_us(1);
}
retry=0;
while(!DHT11_DQ_IN && retry<100) // 等待输入电平由低变高
{
retry++;
delay_us(1);
}
delay_us(40); // 等待40us(高电平持续26~28us表示0,持续70us表示1)
if(DHT11_DQ_IN) return 1; // 40us后如果输入仍为高电平,则表示读入1;否则表示读入0。
else return 0;
}
//从DHT11读取一个字节
//返回值:读到的8位数据
static u8 DHT11_Read_Byte(void)
{
u8 i,byte;
byte = 0;
for (i=0;i<8;i++)
{
byte <<= 1; // 先前读取的数据(不足8位)全部左移一位
byte |= DHT11_Read_Bit(); // 最低位填入新读取的1位
}
return byte;
}
//从DHT11读取一次完整的数据
//temp:温度值(整数,范围:0~50°)
//humi:湿度值(整数,范围:20%~90%)
//返回值:0,正常; 1,失败
static u8 DHT11_Read_Data(u8 *temp, u8 *humi)
{
u8 buf[5];
u8 i;
DHT11_RequestData();
if(DHT11_RespondRequest() == SUCCESS)
{
for(i=0;i<5;i++) // 读取5组共40位数据
{
buf[i] = DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3]) == buf[4]) // 数据校验
{
*humi = buf[0]; // 湿度整数部分
*temp = buf[2]; // 温度整数部分
return SUCCESS;
}
}
return FAILURE;
}
// 采集DHT11数据并打印至串口
void dht11DataCollect()
{
u8 temp;
u8 humi;
if (DHT11_Read_Data(&temp, &humi) == SUCCESS)
printf("temperature:%d℃ humidity(RH):%d \r\n", temp, humi); // 输出到串口(重定向)
else
printf("DHT11 data error! \r\n");
}
// 温度阈值处理
void DHT11_OnTempChange(u8 temp_threshold)
{
if (dht11.temp > temp_threshold)
{
// 启动风扇(继电器低电平触发)
Relay_Low();
// 蜂鸣器短鸣(100ms)
BEEP_ON();
beep_time = 100; // 100ms
}
else
{
// 关闭风扇
Relay_High();
}
}
#include "ldr.h"
LdrStruct ldr;
static u8 getLightIntensity(void);
static void OnLightChange(u8 hiLight, u8 loLight);
/*******************************************************************************
* 函 数 名 : adc_init
* 函数功能 : ADC外设的初始化
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
static void adc_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
// 1.打开相关外设的总线时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
// 2.信号引脚的参数配置
GPIO_InitStructure.GPIO_Pin=LDR_PIN; // 信号引脚:PB0
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; // 设置模拟输入模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz; // 设置传输速率
GPIO_Init(LDR_PORT,&GPIO_InitStructure);
// 3. 输入时钟降频(<14MHz)
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 分频因子6,输入时钟为72M/6=12MHz
// 4. 初始化ADC参数
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent; //独立模式
ADC_InitStructure.ADC_ScanConvMode=DISABLE; //单次扫描
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE; //单次转换
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None; //软件触发
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right; //数据右对齐
ADC_InitStructure.ADC_NbrOfChannel=1; //只有1个通道
ADC_Init(ADC1,&ADC_InitStructure);
// 5. 使能ADC
ADC_Cmd(ADC1,ENABLE);
// 6. ADC校准
ADC_ResetCalibration(ADC1); //复位校准
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1); //开启并完成校准
while(ADC_GetCalibrationStatus(ADC1));
}
// LDR的设备初始化
extern void ldr_init()
{
adc_init();
ldr.period = LDR_PERIOD;
ldr.high_threshold = HI_THRESHOLD;
ldr.low_threshold = LO_THRESHOLD;
ldr.getData = getLightIntensity;
ldr.onLightChange = OnLightChange;
ldr.state = STANDBY;
}
/*******************************************************************************
* 函 数 名 : GET_ADC_Value
* 函数功能 : 获取通道ch的转换值,测量times次,取平均值
* 输 入 : ch:通道编号,Rank:规则序列中的第几个转换,取值1~16;
times:测量次数
* 输 出 : 通道ch的times次转换结果的平均值
*******************************************************************************/
static u16 Get_ADC_Value(u8 ch, u8 times) //获取ADC1通道ch的转换值
{
u8 i;
u32 Temp_val=0;
ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5);
for(i=0;i<times;i++)
{
ADC_SoftwareStartConvCmd(ADC1,ENABLE); // 开始转换
while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)); // 等待至单次转换结束
Temp_val += ADC_GetConversionValue(ADC1);
delay_ms(10);
}
return Temp_val/times;
}
/*******************************************************************************
* 函 数 名 : getLightIntensity
* 函数功能 : 将ADC转换值解读为光照强度
* 输 入 : 无
* 输 出 : 光照照度值(0~100, 0:照度最低;100:照度最高)
*******************************************************************************/
static u8 getLightIntensity(void) //通过ADC1 通道0的值获取亮度值
{
u16 value = 0;
u8 lightvalue = 0;
value = Get_ADC_Value(ADC_Channel_8,20);
lightvalue = 100 - (u16)(value/40.95);
return lightvalue;
}
// 添加PWM控制LED功能
static void set_led_brightness(u8 level)
{
switch(level)
{
case 0: // 低档
TIM_SetCompare2(TIM3, 125); // 25%占空比
break;
case 1: // 中档
TIM_SetCompare2(TIM3, 250); // 50%占空比
break;
case 2: // 高档
TIM_SetCompare2(TIM3, 375); // 75%占空比
break;
}
}
/*******************************************************************************
* 函 数 名 : OnLightChange
* 函数功能 : 照度超过阈值(高阈值和低阈值)的处理措施
* 输 入 : hiLight-高阈值; loLight-低阈值
* 输 出 : 无
*******************************************************************************/
static void OnLightChange(u8 hiLight, u8 loLight)
{
if (ldr.light > hiLight)
{
set_led_brightness(0);// 照度过高,降低亮度
}
else if (ldr.light < loLight)
{
set_led_brightness(2); // 照度过低,提升亮度
}
else // 正常范围
{
set_led_brightness(1); // 中等亮度
}
}
#include "key.h"
#include "pwm.h"
/*******************************************************************************
* 函 数 名 : TIM3_CH2_PWM_Init
* 函数功能 : TIM3通道2 PWM初始化函数
* 输 入 : per:重装载值
* psc:分频系数
* 输 出 : 无
*******************************************************************************/
void TIM3_CH2_PWM_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); // PB5引脚启用复用模式
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
// 选择TIM3部分重映射
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
/* PB5作为PWM的输出引脚 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB,&GPIO_InitStructure);
// 初始化TIM3
TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
// 设置TIM3_CH2的PWM模式,使能CH2输出,呈现出PPT展示的PWM波形
// PWM1即mode1,先输出有效电平,再输出无效电平;PWM2即mode2则正好相反。
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
// 设置有效电平为低电平(此案例中低电平点亮D2)
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC2Init(TIM3,&TIM_OCInitStructure); //输出比较通道2初始化
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIM3在 CCR2 上的预装载寄存器
TIM_Cmd(TIM3,ENABLE); //使能定时器
}
#include "relay.h"
void Relay_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//默认输出低电平
}
void Relay_High(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_11);
}
void Relay_Low(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_11);
}
#ifndef _RELAY_H_
#define _RELAY_H_
#include "main.h"
#define RELAY_HIGH 1
#define RELAY_LOW 0
void Relay_Init(void);
void Relay_High(void);
void Relay_Low(void);
#endif
#ifndef __PWM_H
#define __PWM_H
#include "main.h"
void TIM3_CH2_PWM_Init(u16 per,u16 psc);
#endif
#ifndef __LDR_H
#define __LDR_H
#include "main.h"
#define LDR_PIN GPIO_Pin_0
#define LDR_PORT GPIOB
#define LDR_PERIOD 4000 // ADC转换周期:4000ms
#define HI_THRESHOLD 70 // 照度高阈值
#define LO_THRESHOLD 30 // 照度低阈值
typedef struct{
u8 light; // 光照强度(相对强度:0~100)
u16 period; // 重复测量周期(s)
u8 high_threshold; // 高亮阈值
u8 low_threshold; // 低亮阈值
SensorState state; // 工作状态
u8 (*getData)(void); // 获取光照强度数据
void (*onLightChange)(u8 hiLight, u8 loLight); // 亮度变化的响应操作
} LdrStruct;
void ldr_init(void);
extern LdrStruct ldr;
#endif
#ifndef __DHT11_H
#define __DHT11_H
#include "main.h"
#define DHT11_PIN GPIO_Pin_12
#define DHT11_PORT GPIOA
#define DHT11_DQ_IN PCin(12) // 输入
#define DHT11_DQ_OUT PCout(12) // 输出
#define SUCCESS 0
#define FAILURE 1
#define DHT11_PERIOD 5000 // 数据采集周期:5000ms
#define TEMP_THRESHOLD 28 // 温度阈值(℃)
// 定义DHT11结构体
typedef struct{
u8 temp; // 温度值
u8 humi; // 湿度值
u16 period; // 重复测量周期(s)
u8 temp_threshold; // 温度阈值
u8 humi_threshold; // 湿度阈值
SensorState state; // 设备状态
u8 (*getData)(u8 *temp, u8 *humi); // 获取温湿度数据
void (*onTempChange)(u8 temp_threshold); // 高温的响应操作
} Dht11Struct;
void dht11_init(void);
extern Dht11Struct dht11;
#endif
#ifndef _usart_H
#define _usart_H
#include "main.h"
#define USART1_REC_LEN 200 //定义最大接收字节数 200
extern u8 USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART1_RX_STA; //接收状态标记
void usart1_init(u32 baud);
#endif
#ifndef __TIM_H
#define __TIM_H
#include "main.h"
//#include "stm32f10x.h"
void tim4_init(u16 per,u16 psc);
#endif
#ifndef __DISPLAY_H
#define __DISPLAY_H
#include "main.h"
#define DSP_DIGIT 4 // 4位数码管
typedef enum{LIGHT, TEMP, HUMI} DSP_Interface; // 数码管界面
typedef struct {
u16 digit[DSP_DIGIT]; // 数码管的4位数字
u8 currDigit; // 当前显示位(1~4)
u8 dotDigit; // 小数点位置(0~4,0代表不显示小数点)
DSP_Interface interface; // 界面显示哪个测量值
void(*SegDisplay)(void);
}DisplayStruct;
void display_init(void);
void segDisplay(void);
//void setValue(DisplayStruct *dsp, u8 value[4], u8 dotBit);
void setLightValue(DisplayStruct *dsp, u8 dotBit);
void setTempValue(DisplayStruct *dsp, u8 dotBit);
void setHumiValue(DisplayStruct *dsp, u8 dotBit);
extern DisplayStruct dsp;
#endif
在已有代码上完善该任务,STM32F103C8t6,用标准库
最新发布