完成一个仓储环境监测的模拟系统。具体要求如下:
1. DHT11模块完成温湿度测量,5s更新,数值显示于数码管,同时打印到串口。温度超过阈值触发蜂鸣器短鸣,同时启动风扇小马达;
2. LDR模块完成环境照度测量,4s更新,数值显示于数码管,同时打印到串口。照度超过阈值启动PWM调光(高、中、低三级调光)。
3. 按键循环切换显示模式,按第1下固定显示温度,按第二下固定显示照度,按第三下恢复默认的自动显示模式。按键用外部中断实现。
#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 "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 "dht11.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)
{
// 启动风扇(低电平触发)
}
else
{
// 关闭风扇
}
}
#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); //使能定时器
}
数码管是4位共阴极,段选连PA0~7,位选PB12~15,STM32F103C8T6,用标准库
最新发布