基于STM32的智能灯控系统(光敏传感器+WS2812/LED)涉及PWM/DMA/ADC

news2026/3/14 19:09:37
一、前言这是实验室项目要求实现的一个小功能自己又想试一下写博客都说有帮助所以打算试一下如有错误请指正谢谢大家并且我发现CSDN的各种标题都长得差不多可能看着会很混乱强烈推荐配合目录食用实现的功能亮度或光照变暗时WS2812或者LED自动变亮其中LED可以实现随光照强度自动调整亮度。二、使用到的核心模块STM32F103C8T6、WS2812、LED、光敏传感器。光敏传感器检测环境亮暗光越强电阻越小输出电压变化。有AO 模拟量测亮度和DO 数字量亮 / 暗开关用来做光控灯、亮度检测。配合LED时使用四引脚光敏传感器VCC与GND正常接入面包板AO模拟输出配合ADC使用接入ADC指定的IO引脚引脚持续获取光敏传感器输出的ADC值达到自己设置的阈值触发代码对LED进行点亮并根据光敏传感器输出的ADC时时调整亮度。WS2812模块WS2812 就是自带驱动芯片的 RGB 灯珠。一根信号线就能串很多个发不同数据就能亮任意颜色时序要求很严一般用PWMDMA驱动。若是多个WS2812级联上一个WS2812的DO接入下一个WS2812的DI。三、开发环境STM32cubeMXkeil5使用HAL库开发下载工具为ST-Link。四、实验1原理WS2812光敏传感器光敏传感器WS2812实现天暗自动开灯光敏传感器作用及其运作原理使用四引脚光敏传感器VCC与GND正常接入面包板DO数字输出可通过电位器调节阈值接入设定IO引脚引脚检测到电平变化后触发相应代码则对WS2812进行点亮或者实现其他功能。WS2812作用及其运作原理VCC接5V但是我自己实测接3.3V也没问题GND接GNDDIN接设定的IO引脚。使用TIM生成PWMDMA的形式对WS2812实现控制。并且WS2812有0码、1码和reset三种输入信号的概念可以理解为专属于WS2812的低电平0和高电平1以此来对WS2812模块进行编程可以通过特殊方式下面的原理讲给WS2812生成0码和1码和reset。WS2812原理在指定计数周期下1.25us不同的高电平信号时长能输出0码、1码和reset信号。简单记短高电平 0长高电平 1长低电平 复位。WS2812运作原理每颗灯珠能按照G, R, B绿红蓝三种颜色组合实现炫彩的灯光效果。而要修改其色彩需要对其输入24个bit长度的0码/1码信号进行调整。严格按照G, R, B绿红蓝顺序每种颜色需8个字节长度的0码/1码信号作为颜色数据3种颜色即需要24位。例如红色1111 1111绿色0000 0000蓝色0000 0000红色255,0,0红色1111 1111绿色1111 1111蓝色1111 1111白色255,255,255关于如何调整颜色数据因为涉及到一个计数周期内的高电平时长0码/1码的设置我们考虑使用PWM波通过调节PWM波的占空比实现调整一个时钟周期内的高电平时长从而实现输出0码/1码/reset信号的目的。至于使用DMA是因为WS2812对时序要求十分严格cpu手动控制时可能一个小小的中断甚至可能是因为cpu慢了一点就会导致时序错误其次不使用DMA对cpu占用严重cpu就得一直盯着翻转电平严重干扰cpu执行其他任务。用了 DMACPU 把 “发灯珠数据” 这个活交给 DMA 后自己就能去处理其他任务直接提高效率。一段话总结本项目通过四引脚光敏传感器检测环境亮度光敏电阻随光线强弱改变阻值经模块比较器从 DO 引脚输出高低电平来判断天黑或天亮电平变化触发单片机动作单片机利用定时器 PWMDMA驱动 WS2812 灯珠通过调整 PWM 占空比生成符合时序要求的 0 码、1 码和复位信号按 GRB 顺序控制灯光颜色DMA 保证时序精准且不占用 CPU最终实现环境变暗时自动点亮 WS2812 灯光的完整流程。实验1STM32cubeMX配置流程引脚配置使用到的引脚如下图串口引脚可用来调试打印电压用不冲突光敏传感器引脚选择接入PA0任意的可输入的引脚配置为默认上拉Pull-up。WS2812引脚由于要使用到PWM我们选择TIM1的PWM通道1。引脚为PA8。PWM参数配置PWM参数配置基于我的时钟主频为8MHz如图折叠起来的使用默认参数分频系数不选择分频。计数模式向上计数。自动重装载值9。不使用影子寄存器。计算是否符合WS2812的计数周期1.25us8MHz周期为125ns因为自动重装载值为9则计数器需计数91次则125ns计算10次的时间1.25us符合WS2812严格的时序要求。DMA参数配置模式为正常模式。为TIM1_CH1添加DMA数据搬运方向为内存到外设对应代码中存有WS2812颜色数据的数组到TIM1_CH1通道的寄存器。设置外设地址不自增内存地址自增。设置数据长度为半字。实验1核心代码解读其余配置类代码由STM32cubeMX生成这里不做展示只展示核心代码重要的宏定义与变量#define WS2812_NUM 16 //灯珠数量我买的模块直接集成了16个灯珠 #define WS2812_BITS 24 //每个灯珠的24bit颜色数据位 #define TIM1_ARR 9 //PWM的ARR值能直接修改方便适应其他的主频 //结合主频8MHz、ARR9高电平持续时间 (CCR1) * 时钟周期。要达成0码高电平约0.4us需要CCR31码高电平约0.8us需要CCR6。 #define CODE0_HIGH 3 //根据1.25us的周期我计算出来的0码所需要设置的高电平的占空比 #define CODE1_HIGH 6 //根据1.25us的周期我计算出来的1码所需要设置的高电平的占空比 uint32_t WS2812_buf[WS2812_NUM*WS2812_BITS];//存取WS2812颜色数据WS2812设置颜色函数/** * brief 设置WS2812灯带的颜色 * param r 红色分量 (0-255) * param g 绿色分量 (0-255) * param b 蓝色分量 (0-255) * retval 无 */ void WS2812_SetColor(uint8_t r, uint8_t g, uint8_t b) { // 循环计数器 int8_t i 0, j 0; // 将RGB三色合并为32位数据(格式: GRB) uint32_t color ((uint32_t)g 16) | ((uint32_t)r 8) | (uint32_t)b; // 缓冲区索引记录当前写入位置 uint16_t idx 0; // 双重循环为每一颗LED生成对应的颜色数据 for(j 0; j WS2812_NUM; j) // 遍历所有LED { for(i WS2812_BITS - 1; i 0; i--) // 从高位到低位处理24位颜色数据 { // 检查当前位是否为1 if(color (1 i)) // 如果该位为1 { WS2812_buf[idx] CODE1_HIGH; // 存储表示1的时序数据 } else // 如果该位为0 { WS2812_buf[idx] CODE0_HIGH; // 存储表示0的时序数据 } } } }写入颜色数组函数/** * brief 发送WS2812颜色数据到灯带 * note 通过TIM1的PWMDMA方式发送数据实现精确时序控制 * retval 无 */ void WS2812_SendData(void) { // 停止之前的DMA传输确保通道空闲 HAL_TIM_PWM_Stop_DMA(htim1, TIM_CHANNEL_1); // 启动PWM DMA传输 // 参数1: 定时器句柄 htim1 // 参数2: 定时器通道 TIM_CHANNEL_1 // 参数3: 要发送的数据缓冲区指针 WS2812_buf // 参数4: 数据长度 WS2812_NUM * WS2812_BITS // 说明: DMA会自动将缓冲区中的数据按顺序发送每个数据决定PWM的高电平时间 HAL_TIM_PWM_Start_DMA(htim1, TIM_CHANNEL_1, (uint32_t*)WS2812_buf, WS2812_NUM * WS2812_BITS); // 延时1ms等待DMA传输完成 // 注意: 此延时时间需要根据实际数据量调整 // 如果延时太短可能导致数据未发送完太长会影响响应速度 HAL_Delay(1); }main函数int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM1_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ // 初始状态点亮所有LED为白色(200,200,200) WS2812_SetColor(200, 200, 200); WS2812_SendData(); // 发送数据到LED灯带 while (1) { //读取光敏传感器的信号 if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) GPIO_PIN_RESET) { //低于阈值点亮WS2812为白色可自己调整颜色 WS2812_SetColor(200,200,200); WS2812_SendData(); } else { //高于阈值关灯 WS2812_SetColor(0,0,0); WS2812_SendData(); } HAL_Delay(50); //延时用于防止DMA在前一次传输未完成时又被启动导致数据错乱 } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ /* USER CODE END 3 */ }效果演示请关注dy或者b站无敌贵点大王五、实验2原理普通LED光敏传感器光敏传感器LED实现天暗自动开灯并且自调亮度光敏传感器作用及其运作原理使用四引脚光敏传感器VCC与GND正常接入面包板AO模拟输出接入设定IO引脚引脚实时监测光敏传感器的模拟输出触发相应代码对LED进行点亮并调节亮度。LED作用及其运作原理具象体现ADC值对电压的影响。一段话总结通过光敏传感器的模拟输出AO持续获得因光照变化的ADC值再通过实时变化的ADC值实时修改PWM波的占空比从而实现实时修改LED引脚的电压值实现控制LED的开关和亮暗调节。实验2STM32cubeMX配置流程引脚配置使用到的引脚如下图串口引脚可用来打印电压调试用不冲突光敏传感器引脚选择接入PA0任意的可输入的引脚配置为默认上拉Pull-up。LED引脚由于要使用到PWM我们选择TIM1的PWM通道1。引脚为PA8。PWM参数配置PWM参数配置基于我的时钟主频为8MHz如图折叠起来的使用默认参数分频系数7使得频率变成1MHz。计数模式向上计数。自动重装载值999使得周期变成1ms。使用影子寄存器防止PWM波形突变。ADC参数配置使用ADC1通道0独立模式不打开扫描不打开连续转换转换通道数量为1。实验2核心代码解读重要的宏定义和变量/* ADC采样值范围定义 */ #define ADC_MAX 4096 /* ADC最大采样值12位ADC理论最大值4095这里用4096便于计算*/ #define ADC_MIN 1000 /* ADC最小有效采样值用来控制触发LED点亮的阈值值越大点亮LED的亮度越大*/ /* PWM比较值范围定义便于适应不同时钟频率下的PWM值*/ #define PWM_CCR_MAX 999 /* PWM最大比较值对应100%占空比TIM1周期设为999*/ #define PWM_CCR_MIN 0 /* PWM最小比较值对应0%占空比输出低电平*/获取ADC值函数/** * brief 获取ADC采样值8次采样取平均软件滤波 * param void * return uint32_t 返回8次采样的平均值范围0-4095 * note 此函数会阻塞运行直到完成8次采样 * 每次采样间隔取决于ADC转换时间约几微秒 */ uint32_t Get_ADCValue() { uint32_t ADC_Value 0; /* 最终返回的平均值 */ uint32_t sum 0; /* 8次采样的累加和 */ uint32_t count 0; /* 循环计数器 */ /* 1. 启动ADC转换 */ HAL_ADC_Start(hadc1); /* 连续采样8次用于软件滤波 */ for(count 0; count 8; count) { /* 2. 等待转换完成 * HAL_MAX_DELAY无限等待直到转换完成 * 函数会在转换完成后返回 */ HAL_ADC_PollForConversion(hadc1, HAL_MAX_DELAY); /* 3. 读取转换结果并累加 * HAL_ADC_GetValue() 返回12位ADC值0-4095 */ sum HAL_ADC_GetValue(hadc1); /* 注意这里没有停止ADC下次循环继续启动 * 每次循环都 等待-读取确保每次采样独立 */ } /* 4. 计算8次采样的算术平均值 * count8sum是8次总和 * 平均值 总和 / 8 */ ADC_Value sum / count; /* 整数除法自动取整 */ /* 5. 停止ADC降低功耗 * 采样完成后关闭ADC直到下次需要采样再开启 */ HAL_ADC_Stop(hadc1); /* 6. 返回滤波后的ADC值 */ return ADC_Value; }计算并调整CCR的函数/** * brief 将ADC采样值映射到PWM比较值CCR * param ADC_Value 获取的ADC采样值0-4095 * return uint16_t 映射后的PWM比较值0-999 * note 实现ADC值到PWM占空比的线性映射 */ uint16_t Get_CCR(uint32_t ADC_Value) { uint16_t CCR; /* 存储计算得到的PWM比较值 */ /* 映射关系 ADC_MIN (1000) → 0 (LED熄灭) ADC_MAX (4096) → 999 (LED最亮) */ if(ADC_Value ADC_MIN) ADC_Value ADC_MIN; /* 低于下限则取下限最暗 */ if(ADC_Value ADC_MAX) ADC_Value ADC_MAX; /* 高于上限则取上限 最亮*/ /* 线性映射公式 * 公式CCR 999 * (ADC - 1000) / (4096 - 1000) * 将[ADC_MIN, ADC_MAX]区间线性映射到[PWM_CCR_MIN, PWM_CCR_MAX]区间 */ CCR (PWM_CCR_MAX * (ADC_Value - ADC_MIN) / (ADC_MAX - ADC_MIN)); return CCR; /* 返回计算得到的PWM比较值 */ }main函数int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM1_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ //提前打开PWM通道1 HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); /* USER CODE END 2 */ /* Infinite loop */ while (1) { //调用函数先获取ADC值在通过ADC值转换成CCR值 uint32_t ADC_ValueGet_ADCValue(); uint16_t dutyGet_CCR(ADC_Value); //设置转换好的CCR值实现控灯效果 __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, duty); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }效果演示请关注dy或者b站无敌贵点大王

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2412079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…