第一章 PWM调节
第二章 ADC采样
第三章 光敏传感器
第四章 智能灯光亮度调节器(终)
目录
前言
一、准备工作
二、项目详情
三、实现过程
四、调试过程
总结
前面三章对PWM、ADC和光敏传感器三个重要的部分进行了学习,本文将对整个项目进行完整地实现,该项目我们使用了PWM、ADC、按键中断、定时器中断、LCD显示、串口打印、LED等模块,该智能灯光亮度调节器有两个模式:手动模式和自动模式;在手动模式下,我们可以使用WK按键和KEY1按键对开发板上的LED0(红色)手动的调节灯光亮度档位,一共有五个档位。按下KEY2按键,进入自动模式。自动模式,LED0的灯光亮度档位会根据光敏传感器采集到的环境亮度进行自动调节,按下KEY0则退回手动模式。
提示:以下是本篇文章正文内容,下面案例可供参考
本案例使用的是正点原子的战舰开发板,一根烧录线,一根串口线,串口调试助手使用的是XCOM
可点赞、收藏和评论留言,私我拿代码我下载使用。
(1)5档调节灯光亮度,可分为手动和自动两种调节方式,调节方式由按键设置;
(2)自动调节时依据当前亮度变化调节,通过板上的光敏电阻获取环境亮度;
(3) 1秒采集一次当前环境灯光亮度,1秒的时间间隔由定时器设置;
(4)把当前的环境亮度和灯光亮度上传到串口调试助手;
(5)在显示屏上显示出当前的环境亮度和灯光亮度等级。
(1)对于5档调节灯光亮度可以通过设置按键,让按键来实现对灯光亮度调升和调降,灯光亮度的调节需要设置PWM来调节占空比来调节灯五个不同挡位显现不用的五个灯光亮度;
(2)自动调节灯光亮度需要使用光敏传感器与ADC获取当前环境亮度,再根据环境亮度的不同来调控灯的亮度;
(3)通过设置定时器来实现1秒采集一次当前环境灯光亮度;
(4)配置好串口函数所需参数来实现把当前的环境亮度和灯光亮度上传到串口调试助手;
(5)对LCD显示板块所需参数进行配置能够实现显示屏显示当前环境亮度和灯光亮度等级。
下面写出只有部分模块:
TIM3_PWM初始化:
//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OCInitStructure.TIM_Pulse=0;
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
TIM4初始化:
void TIM4_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM4中断,允许更新中断
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM4, ENABLE); //使能TIMx外设
}
ADC3初始化:
//初始化ADC3
//这里我们仅以规则通道为例
//我们默认仅开启通道6
void Adc3_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3 , ENABLE ); //使能ADC3通道时钟
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,ENABLE);//ADC复位
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,DISABLE);//复位结束
ADC_DeInit(ADC3); //复位ADC3,将外设 ADC3的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式: 独立模式
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数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC3, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC3, ENABLE); //使能指定的ADC3
ADC_ResetCalibration(ADC3); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC3)); //等待复位校准结束
ADC_StartCalibration(ADC3); //开启AD校准
while(ADC_GetCalibrationStatus(ADC3)); //等待校准结束
}
光敏传感器初始化:
//初始化光敏传感器
void Lsens_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);//使能PORTF时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//PF8 anolog输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOF, &GPIO_InitStructure);
Adc3_Init();
}
5档调节灯光亮度可以通过设置按键,让按键来实现对灯光亮度调升和调降,灯光亮度的调节需要设置PWM来调节占空比来调节灯五个不同挡位显现不用的五个灯光亮度;
/***********按键处理函数***********/
void key_handle(u8 key)
{
if(key)
{
switch(key)
{
case WKUP_PRES: //手动模式下,按下WK按键,提高一档灯光。
delay_ms(10);
if(flag_mode == 0)
{
if(LEDlevel<100)LEDlevel+=20;
}
break;
case KEY2_PRES: //手动模式下,按下KEY2按键,进入自动模式。
delay_ms(10);
flag_mode = 1;
automatic_mode();
break;
case KEY1_PRES: //手动模式下,按下KEY1按键,降低一档灯光。
delay_ms(10);
if(flag_mode == 0)
{
if(LEDlevel>20)LEDlevel-=20;
else LEDlevel=0;
}
break;
}
}else
delay_ms(10);
TIM_SetCompare2(TIM3,LEDlevel);
}
其中,KEY1可以降低灯光档位,WK_UP按键可以提高灯光档位。
case WKUP_PRES: //手动模式下,按下WK按键,提高一档灯光。
delay_ms(10);
if(flag_mode == 0)
{
if(LEDlevel<100)LEDlevel+=20;
}
break;
case KEY1_PRES: //手动模式下,按下KEY1按键,降低一档灯光。
delay_ms(10);
if(flag_mode == 0)
{
if(LEDlevel>20)LEDlevel-=20;
else LEDlevel=0;
}
break;
WK_UP按键按下后,变量LEDlevel会增加20; KEY1按键按下后,变量LEDlevel会减少20;
改变LEDlevel后,会对TIM3的比较值进行赋值。
TIM_SetCompare2(TIM3,LEDlevel);
而在main函数中,我们已经对TIM3配置为:
TIM3_PWM_Init(100,0);//ARR为100,CCR为0
假设我们初始值LEDlevel为0,按下WK_UP按键后,LEDlevel值增加为20,并使用TIM_SetCompare2(TIM3,LEDlevel)赋值给CCR,现在PWM为20/100,为灯光的第一档。
自动调节灯光亮度需要使用光敏传感器与ADC获取当前环境亮度,再根据环境亮度的不同来调控灯的亮度
//读取Light Sens的值
//0~100:0,最暗;100,最亮
u8 Lsens_Get_Val(void)
{
u32 temp_val=0;
u8 t;
for(t=0;t4000)temp_val=4000;
return (u8)(100-(temp_val/40));
}
//获得ADC3某个通道的值
//ch:通道值 0~16
//返回值:转换结果
u16 Get_Adc3(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC3, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC3,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC3, ENABLE); //使能指定的ADC3的软件转换启动功能
while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC3); //返回最近一次ADC3规则组的转换结果
}
通过Lsens_Get_Val()函数,我们可以采集当前的环境亮度,该函数中调用了ADC的采样函数。
/***********自动模式函数***********/
void automatic_mode(void)
{
while(flag_mode == 1)
{
if(KEY0 == 0)flag_mode = 0; //按下KEY0按键,退出自动模式
else
{
if((adcx>0)&&(adcx<=20)) //当前环境亮度小于20,灯光亮度调至最高的第五档
{
TIM_SetCompare2(TIM3,100);
LEDlevel=100;
}
if((adcx>20)&&(adcx<=40)) //当前环境亮度小于40大于20,灯光亮度调至第四档
{
TIM_SetCompare2(TIM3,80);
LEDlevel=80;
}
if((adcx>40)&&(adcx<=60)) //当前环境亮度小于60大于40,灯光亮度调至第三档
{
TIM_SetCompare2(TIM3,60);
LEDlevel=60;
}
if((adcx>60)&&(adcx<=80)) //当前环境亮度小于80大于60,灯光亮度调至第二档
{
TIM_SetCompare2(TIM3,40);
LEDlevel=40;
}
if((adcx>80)&&(adcx<=95)) //当前环境亮度小于95大于80,灯光亮度调至第一档
{
TIM_SetCompare2(TIM3,20);
LEDlevel=20;
}
if((adcx>95)&&(adcx<=100)) //当前环境亮度小于100大于95,关闭灯光
{
TIM_SetCompare2(TIM3,0);
LEDlevel=0;
}
}
}
}
其中,adcx为Lsens_Get_Val()函数的返回值,即当前的环境亮度,自动模式函数中我们对adcx进行判断,在使用TIM_SetCompare2(TIM3,CCR)进行赋值,改变PWM值从而改变了LED0的亮度,实现自动根据环境亮度改变LED亮度。
通过设置定时器来实现1秒采集一次当前环境灯光亮度;
配置好串口函数所需参数来实现把当前的环境亮度和灯光亮度上传到串口调试助手;
对LCD显示板块所需参数进行配置能够实现显示屏显示当前环境亮度和灯光亮度等级。
//定时器4中断服务程序
void TIM4_IRQHandler(void) //TIM4中断
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
adcx=Lsens_Get_Val();
LCD_ShowxNum(30+10*8,140,adcx,3,16,0); //显示ADC的值 显示环境亮度
LCD_ShowxNum(30+10*8,160,LEDlevel,3,16,0); //显示ADC的值 显示灯光等级
printf("===========================\r\n\r\n");
printf(" 环境亮度:%d \r\n\r\n",adcx); //串口打印环境亮度
printf(" 灯光亮度:%d \r\n\r\n",LEDlevel); //串口打印灯光亮度
if(flag_mode == 0)printf("\n=========手动模式==========\r\n\r\n\r\n\r\n");
if(flag_mode == 1)printf("\n=========自动模式==========\r\n\r\n\r\n\r\n");
}
}
在main函数中,我们对TIM4初始化为每1秒中断一次,是通过一下函数实现:
TIM4_Int_Init(1999,35999);//ARR为1999,PSC为35999
TIM4的时钟频率为72M,计算公式为
72M/(ARR+1)/(PSC+1)
所以本次项目TIM4的频率为72000000/(1999+1)/(35999+1)为1,所以每1秒中断一次。
中断服务函数中:
adcx=Lsens_Get_Val();
使用变量adcx保存光敏传感器获取环境亮度函数的返回值,该操作则实现了功能三:每秒采集一次环境亮度。
LCD_ShowxNum(30+10*8,140,adcx,3,16,0); //显示ADC的值 显示环境亮度
LCD_ShowxNum(30+10*8,160,LEDlevel,3,16,0); //显示ADC的值 显示灯光等级
printf("===========================\r\n\r\n");
printf(" 环境亮度:%d \r\n\r\n",adcx); //串口打印环境亮度
printf(" 灯光亮度:%d \r\n\r\n",LEDlevel); //串口打印灯光亮度
if(flag_mode == 0)printf("\n=========手动模式==========\r\n\r\n\r\n\r\n");
if(flag_mode == 1)printf("\n=========自动模式==========\r\n\r\n\r\n\r\n");
在TIM4中断服务函数中,我们通过以上语句分别在LCD与串口打印窗口上输出了环境亮度与灯光亮度,实现了项目的功能四与功能五。
代码可点赞、收藏和评论留言找我拿哦。
LSENS_VAL为当前环境亮度,范围为0~100;LED_LEVEL为当前灯光亮度,范围为0~100。
上电后,模式为手动模式Manual mode,灯光亮度为0,在LCD屏上可看到。
(1)按下WK_UP按键,灯光亮度增加一档,灯光亮度值为20。再次按下WK_UP按键,灯光亮度增加至第二档,灯光亮度值为40;
第一档灯光亮度为20
第二档灯光亮度为40
(2) 按下KEY1按键,灯光亮度降低一档,灯光亮度值为20。
(1) 按下KEY2按键,进入自动模式Automatic mode:
进入自动模式后,光敏传感器将自动感应环境亮度,并且根据环境亮度改变灯光亮度;
(2)盖住光敏传感器使得检测到的环境亮度为0,灯光亮度自动调节为100,最高档。
(3)打开手电筒照射光敏传感器,使得检测到的环境亮度为97,灯光亮度自动调节为0。
(4)按下KEY0按键,退出自动模式Automatic mode,回到手动模式Manual mode。
(1)连接上串口线,打开XCOM串口调试助手(网盘资料中有)。
(2)手动模式
(3)自动模式
到这里你已经完成属于你的第一个STM32小项目啦!!!你已经学习收获了许多功能: PWM、LED、KEY、UART、ADC、光敏传感器等等。希望能在后续关注我的文章,感谢观看!!