前阵子蹲宿舍摸鱼的时候,整了个基于STM32的智能窗帘仿真项目,本来只是想应付一下课程设计,结果玩着玩着还挺有意思,今天把整个东西扒出来唠唠
调一下光敏电阻的亮度,屏幕上的数值会跟着变,按模式切换键可以改模式,按阈值按键可以调阈值,自动模式下亮度超过阈值就关窗帘,手动模式下按开关按键就能控制窗帘开和关。最后是核心的电机控制,用的是28BYJ-48五线四相步进电机,减速比挺大的,转一圈要好久,刚好适合窗帘用,驱动用ULN2003,毕竟STM32的IO口带不动电机的电流。按键这块我用了四个,分别是阈值加、阈值减、模式切换、手动开关,都是接在
1240-基于STM32控制的智能窗帘控制系统仿真(仿真加程序) 功能如下: 1.光敏电阻可以实时检测环境中的亮度情况 2.OLED显示屏可以实时显示测量环境亮度、阈值亮度、控制模式 3.两个设置按键可以实时修改环境亮度报警阈值 模式切换按键可以切换自动和手动模式 5.自动模式下,当环境亮度大于阈值时,步进电机转动,控制窗帘关闭当环境亮度小于阈值时,控制窗帘打开 6.手动模式下,开关按键可以手动开启窗帘和关闭窗帘
整个项目用到的东西其实挺常见的:STM32F103C8T6最小系统板、光敏电阻测亮度、12864的OLED屏显示数据、四个按键(两个调阈值、一个切模式、一个手动开关),还有28BYJ-48步进电机加ULN2003驱动板,仿真用Proteus就行,不用真的买硬件,搭个原理图就能跑。
首先是亮度检测这块,用的是PA0接光敏电阻的分压电路,毕竟PA0是ADC1_IN0,懒得改通道,直接用默认的就行。这里我踩过坑,一开始没给ADC校准,采出来的数值飘得厉害,晒个台灯都能跳几十个数,后来加上校准步骤才稳。贴个简化的初始化代码:
// ADC初始化,用的STM32标准库
void ADC1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
// PA0设为模拟输入,别开上下拉,光敏本身已经分压了
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换,不用每次手动触发
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE);
// 校准步骤,必须加,不然数值不准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
每次读数据的时候直接调用ADC_GetConversionValue(ADC1)就能拿到当前的ADC值,我偷懒直接把这个值当亮度显示了,其实可以套个分压公式转成实际照度,不过仿真里凑活看就行,逻辑对就ok。
接下来是OLED屏,用来显示当前亮度、阈值还有当前工作模式,用的是SSD1306驱动的12864屏,接的I2C引脚。写了个显示当前系统状态的函数,三行刚好打完所有信息:
void OLED_ShowSysStatus(u16 light, u16 threshold, u8 mode)
{
char buf[32];
OLED_Clear();
// 第一行打当前亮度
sprintf(buf, "Light: %d", light);
OLED_ShowString(0,0,(u8*)buf,16);
// 第二行打阈值
sprintf(buf, "Thresh: %d", threshold);
OLED_ShowString(0,20,(u8*)buf,16);
// 第三行打模式
mode ? OLED_ShowString(0,40,(u8*)"Mode: Manual",16) : OLED_ShowString(0,40,(u8*)"Mode: Auto",16);
OLED_Update(); // 这里一定要加!不然显存没刷新,啥都不显示,我之前踩过这个坑,烧进去半天没反应
}
这里要注意,每次显示完必须调用刷新函数,不然OLED的显存没更新,不会出东西,而且字体选16号刚好三行占满整个屏幕,不会挤。
1240-基于STM32控制的智能窗帘控制系统仿真(仿真加程序) 功能如下: 1.光敏电阻可以实时检测环境中的亮度情况 2.OLED显示屏可以实时显示测量环境亮度、阈值亮度、控制模式 3.两个设置按键可以实时修改环境亮度报警阈值 模式切换按键可以切换自动和手动模式 5.自动模式下,当环境亮度大于阈值时,步进电机转动,控制窗帘关闭当环境亮度小于阈值时,控制窗帘打开 6.手动模式下,开关按键可以手动开启窗帘和关闭窗帘
按键这块我用了四个,分别是阈值加、阈值减、模式切换、手动开关,都是接在PB口上,用的是最笨的轮询加延时消抖,毕竟按键不多,没必要搞外部中断。代码大概是这样:
void Key_Scan(void)
{
// PB0:阈值+,PB1:阈值-,PB2:模式切换,PB3:手动开关
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
delay_ms(10); // 消抖,防止按一下触发好几次
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
threshold += 50;
if(threshold > 4095) threshold = 0; // ADC是12位,最大值4095
}
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0); // 等待松手,不然按住一直加
}
// 阈值减的逻辑和加的差不多,就不重复贴了
// 模式切换键,按一下就切换自动/手动
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0)
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0)
{
work_mode = !work_mode;
}
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0);
}
// 手动开关只有在手动模式下才生效,不然自动模式按了也白按
if(work_mode == 1 && GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3) ==0)
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3) ==0)
{
curtain_status = !curtain_status;
Motor_Run(curtain_status);
}
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3) == 0);
}
}
这里消抖用的是10ms延时,足够滤掉按键的抖动了,要是用外部中断的话还要写中断服务函数,反而麻烦,轮询就够了。
最后是核心的电机控制,用的是28BYJ-48五线四相步进电机,减速比挺大的,转一圈要好久,刚好适合窗帘用,驱动用ULN2003,毕竟STM32的IO口带不动电机的电流。我把电机的四相接到PC0到PC3上,写了个转动函数:
// dir=0开窗帘,dir=1关窗帘
void Motor_Run(u8 dir)
{
u8 i;
// 四相八拍的驱动序列,扭矩比单相的大一点
u8 motor_seq[8] = {0x01,0x03,0x02,0x06,0x04,0x0c,0x08,0x09};
if(dir == 1)
{
// 关窗帘的话倒着走序列
for(i=7; i>=0; i--)
{
GPIO_Write(GPIOC, motor_seq[i]);
delay_ms(2); // 转速,太慢的话窗帘动得慢,太快会丢步
}
}
else
{
for(i=0; i<8; i++)
{
GPIO_Write(GPIOC, motor_seq[i]);
delay_ms(2);
}
}
}
这里要注意电机的供电,必须外接5V电源,不能用STM32的5V引脚,不然要么转不动,要么烧IO口,我第一次试的时候差点把开发板烧了,吓一跳。
最后把所有东西串到主函数里,自动模式的逻辑是:如果当前亮度高于阈值,就关窗帘;低于阈值就开窗帘,这里我加了个50的死区,防止亮度在阈值附近来回跳,不然电机一直启停,吵得要死。主循环大概是这样:
int main(void)
{
delay_init();
OLED_Init();
ADC1_Init();
GPIO_Init(); // 初始化按键和电机的GPIO
u16 threshold = 2000; // 默认阈值
u8 work_mode = 0; // 默认自动模式
u8 curtain_status = 0; // 默认窗帘开着
while(1)
{
u16 light_val = ADC_GetConversionValue(ADC1);
OLED_ShowSysStatus(light_val, threshold, work_mode);
Key_Scan();
if(work_mode == 0) // 自动模式
{
if(light_val > threshold)
{
Motor_Run(1);
curtain_status = 1;
}
else if(light_val < threshold -50) // 死区,防止来回跳
{
Motor_Run(0);
curtain_status = 0;
}
}
delay_ms(100); // 主循环加个延时,不然CPU占满
}
}
仿真的话直接在Proteus里搭个原理图就行:光敏电阻接PA0,OLED接I2C的PB6和PB7,四个按键接PB0到PB3,电机通过ULN2003接PC0到PC3,然后把编译好的hex文件烧进去,就能跑了。调一下光敏电阻的亮度,屏幕上的数值会跟着变,按模式切换键可以改模式,按阈值按键可以调阈值,自动模式下亮度超过阈值就关窗帘,手动模式下按开关按键就能控制窗帘开和关。
其实整个项目没啥难的,就是把各个模块拼起来,逻辑理清楚就行,中间踩了不少坑:ADC没校准、OLED忘了刷新、电机供电不够这些,都是小问题改过来就好了。要是想加功能的话,还可以加个蓝牙模块用手机遥控,或者加个红外接收用遥控器控制,反正随便造就行。
更多推荐


所有评论(0)