电路原理图如下:
目录
1 概述 1.1 背景 1.2 设计的目的与意义 2 方案简述 3 硬件电路 3.1 单片机的选择 3.2 MCU模块电路 3.3 人体红外检测模块电路 3.4 光照强度检测模块电路 3.5 照明灯模块电路 3.6 蜂鸣器模块电路和心跳灯模块电路 3.7 模式选择按键和模式指示灯模块电路 3.8 复位电路 3.9 USB转串口模块电路和SWD下载接口电路 3.10 电源模块电路 4 系统软件设计及实现 4.1 主程序流程图 4.2 手动模式流程图 4.3 自动模式流程图 5 调试 5.1 硬件调试规则 5.2 软件调试 5.3 调试的最终结果 设计心得 参 考 文 献 附录 1 概述
1.1 背景 随着计算机网络、通信、控制等技术的发展,智能建筑的发展越来越迅猛。目前,国内大多数智能建筑存在效率低、能耗高的现象。就智能建筑的照明系统来说,许多地方的灯是从早到晚开着的,不管这些房间或楼道是否有人,或者,当自然光照度很好时,不能及时关闭;当自然光照度不能满足人的需求时,又不能及时打开。这种照明系统,不仅造成能源的浪费,而且不能满足人对照明的需求,同时也给人的视力造成了很坏的影响。现代照明除了满足人的基本需求,应更注重能量的节省和使用上的便利。要做到合理、经济、节能,首先应采用先进成熟的技术和产品,如电光源、灯具、照明控制系统。 在大学校园的建设中,各大高校意识到了智能照明的重要性。相对商业楼宇而言,大学校园里的大功率动力和制冷设备比重较少,照明灯具则相对比重更多,所以控制教室照明是节能的关键。使用照明控制系统,更能体现其在节能方面的优势,提高学校的科学管理水平,而且还能节省开支。 1.2 设计的目的与意义 ① 良好的节能效果和延长灯具寿命 节能是照明控制系统的最大优势。传统的楼宇公共区域照明工作模式,只能是白天关灯,晚上开灯。而采用了智能照明控制系统后,可以根据不同场合、不同的人流量,进行时间段、工作模式的细分,把不必要的照明关掉,在需要时自动开启。同时,系统还能充分利用自然光,自动调节室内照度。控制系统实现了不同工作场合的多种照明工作模式,在保证必要照明的同时,有效减少了灯具的工作时间,节省了不必要的能源开支,也延长了灯具的寿命。 ② 改善工作环境,提高工作效率 良好的工作环境是提高工作效率的一个必要条件。合理地选用光源、灯具及性能优越的照明控制系统,都能提高照明质量。智能照明控制系统具有开关和调光两种控制方法,可以有效地控制各种照明场所的平均照度值,从而提高照度均匀性。同时,系统能根据不同的时间段,人们的不同需要,自动调节照度。 ③ 提高管理水平 智能照明控制系统是以自动控制为主、人工控制为辅的系统。在一般的情况下,不需要有人的参与,照明系统自动实现开关和调光功能,既大大减少了管理人员的数量,也排除了由于人为因素而出现的不定时开关,影响学校的正常教学、生活秩序的情况。 ④ 较好的投资收益效果 智能照明控制系统在节能和节省灯具使用的同时,有效节省了电费与管理费用的支出。根据一般的办公大楼运营的经验来看,节能效果能达到40%以上,一般的商场、酒店、地铁站等节能效果也能达到25%~30%。
2 方案简述
针对上述节能、环保、健康等问题研究,基于STM32微控制器和PWM调光的LED灯以STM32F103C8作为主控芯片,设置了手动控制模式和自动控制模式。在手动控制时,分为五个档,输出不同的PWM占空比对LED的电流进行控制,从而实现了对光度的手动调节。 在自动控制时,通过STM32F103C8芯片自带的AD转换不断检验光敏电阻的电压来间接测量感应光度,将电压和预设的阈值进行对比,调整PWM的占空比对LED的电流进行控制,从而实现了对光度的自动调节。 总体框图如下图2.1: 
图2.1总体框图 在实现主要功能的基础上,增加了自动模式和手动模式的指示灯以及蜂鸣器的提示功能。选择自动模式时,自动模式的指示灯亮,选择手动模式时,手动模式的指示灯亮。同时伴随着呼吸灯的闪烁。当进行关机的时候,蜂鸣器会发出声响,提示操作者系统已经关机。
3 硬件电路
3.1 单片机的选择 STM32是目前最流行的一款单片机, 现在以STM32F103为代表介绍它的各个参数。STM32微控制器是把那些作为控制应用所必需的基本内容都集成在一个尺寸有限的集成电路芯片上。按功能划分,它由如下功能部件组成,即内核,存储器,时钟、复位和电源管理,模数转换器,DMA控制器,I/O口,定时器,通信接口等。它们都是通过片内总线连接而成,对各种功能部件的控制是采用特殊功能寄存器的集中控制方式。其内部结构主要有以下几部分: - 内核:ARM32位的CortexTM-M3 CPU,最高72MHz工作频率,在存储器的0等待周期访问时可达1.25DMips/MHz(Dhrystone2.1),单周期乘法和硬件除法。
- 存储器:从64K或128K字节的闪存程序存储器,高达20K字节的SRAM。
- 时钟、复位和电源管理:2.0~3.6V供电和I/O引脚, 上电/断电复位(POR/PDR)、可编程电压监测器(PVD), 4~16MHz晶体振荡器,内嵌经出厂调校的8MHz的RC振荡器,内嵌带校准的40kHz的RC振荡器,产生CPU时钟的PLL,带校准功能的32kHz RTC振荡器。
- 2个12位模数转换器,1μs转换时间(多达16个输入通道):转换范围:0至3.6V,双采样和保持功能,温度传感器。
- DMA:7通道DMA控制器,支持的外设:定时器、ADC、SPI、USART等。
- 多达80个快速I/O端口:26/37/51/80个I/O口,所有的I/O口可以映像到16个外部中断,几乎所有的端口均可容忍5V的信号。
- 多达7个定时器:3个16位定时器,每个定时器有多达4个用于输入捕获/输出比较/PWM或脉冲计数的通道和增量编码器输入,1个16位带死区控制和紧急刹车,用于电机控制PWM高级控制定时器,系统时间定时器:24位自减型计数器。
- 多达9个通信接口:多达3个USART接口(支持ISO7816接口,LIN,IrDA接口和调制解调控制),多达2个SPI接口(18M位/秒),CAN接口(2.0B主动),USB2.0全速接口。
STM32具有较强的代表性以及该系列单片机资料较多,本设计采用STM32F103C8来实现。 3.2 MCU模块电路 MCU模块电路在整个控制系统中起到至关重要的作用,如图3.2所示。图中包含了时钟电路,采用8MHz的晶振和两个22pF的电容构成的振荡器,为MCU的工作提供有序的时间。电源引脚VBAT、VDD_1、VDD_2、VDD_3和VDDA接的是3.3V的高电平,VSS_1、VSS_2、VSS_3和VSSA接的是GND。 图中PA1引脚GZ_ADC连接的是光照强度检测模块,PA3引脚PERSON_CHECK连接的是人体红外检测模块的PC817的第4引脚,PA9引脚USART1_TX连接的是USB转串口模块的CH340G的第3引脚,PA10引脚USART1_RX连接的是USB转串口模块的CH340G的第2引脚,PA11引脚HM_key连接的是模式选择按键模块的KEY1,PA12引脚AUTO_key连接的是模式选择按键模块的KEY2,PA13引脚SWDIO连接的是SWD下载接口的第2引脚,PA14引脚SWCLK连接的是SWD下载接口的第3引脚,PA15引脚的BEEP连接的是蜂鸣器模块,PB3引脚的LED0连接的是心跳灯模块,PB4引脚的HM_led连接的是模式指示灯模块的HM_led,PB5引脚的AUTO_led连接的是模式指示灯模块的AUTO_led,PB7引脚的LEDPWM连接的是照明灯模块,PB8引脚和PB9引脚暂时没有用到。 以上提到的各个电路模块将在下面详细介绍。 
图3.2 MCU模块电路 3.3 人体红外检测模块电路 采用集成电路BIS0001,该芯片是一款具有较高性能的传感信号处理集成电路。它配以热释电红外传感器和少量外接元器件就可构成被动式的热释电红外开关、报警用人体热释电传感器等。它能自动快速开启各类白炽灯、荧光灯、蜂鸣器、自动门、电风扇、烘干机和自动洗手池等装置,特别适用于企业、宾馆、商场、库房及家庭的过道、走廊等敏感区域,或用于安全区域的自动灯光、照明和报警系统。BIS0001的引脚图如图3.3.1: 
图3.3.1 BIS0001的引脚图 由BIS0001构成的人体红外检测电路如图3.3.2所示:: 图3.3.2 人体红外检测电路 图3.3.2中,RE200B是热释电红外传感器,运算放大器OP1将热释电红外传感器的输出信号作第一级放大,然后由C9耦合给运算放大器OP2进行第二级放大,再经由电压比较器COP1和COP2构成的双向鉴幅器处理后,检出有效触发信号Vs去启动延迟时间定时器,输出信号Vo,Vo引脚的OUT连接PC817的第2引脚,Vo通过PC817对PERSON_CHECK进行控制,PERSON_CHECK连接的是MCU模块的PA3引脚,由此MCU可以知道此时PERSON_CHECK是高电平还是低电平,从而可以判断出有人经过还是无人经过。这个电路模块就是对人进行检测。 3.4 光照强度检测模块电路 光敏电阻又称光导管,由于光照产生的载流子都参与导电,在外加电场的作用下作漂移运动,电子奔向电源的正极,空穴奔向电源的负极,从而使光敏电阻器的阻值迅速下降。光敏电阻器是利用半导体的光电导效应制成的一种电阻值随入射光的强弱而改变的电阻器,又称为光电导探测器。入射光强,电阻减小,入射光弱,电阻增大。电路如下图3.4所示: 
图3.4 光照强度检测电路 图3.5 照明灯电路 图3.4中,光照强度检测电路由一个光敏电阻和一个普通电阻组成。GZ_ADC是和MCU模块的PA1引脚连接在一起的,通过检测外部光照的强弱,从而改变光敏电阻的阻值,使得GZ_ADC的到不同的电平信号,也即是PA1上的电平。光照强时,光敏电阻的阻值小,GZ_ADC得到低电平,光照较弱时,光敏电阻的阻值大,GZ_ADC得到高电平。由于以上现象,可以实现此次设计的一个必不可少的重要功能:有光的时候,LED灯不亮,无光的时候,LED灯亮。 3.5 照明灯模块电路 上图3.5展示了照明灯的驱动电路,此电路由一个发光二极管,一个三极管以及三个电阻组成。图中LEDPWM连接的是MCU模块的PB7引脚,当LEDPWM输出高电平时,三极管Q4导通,接通发光二极管DS1,使其发光,而LEDPWM输出低电平时,三极管截止,发光二极管也截止。通过编程可以实现PB7脚输出不同占空比的PWM波,可以调节通过三极管中的电流,从而实现不同亮度的调节。这个电路一方面是驱动LED灯,另一方面实现对LED灯的控制和亮度调节。 3.6 蜂鸣器模块电路和心跳灯模块电路 蜂鸣器模块电路如下图3.6.1所示,蜂鸣器模块电路主要是由一个蜂鸣器,一个三极管和两个电阻组成。蜂鸣器模块电路中的BEEP连接的是MCU模块的PA15引脚,此电路和照明灯驱动电路基本类似。当三极管基极BEEP输出高电平时,三极管Q1导通,接通蜂鸣器BEEP发声,而三极管基极BEEP输出低电平时,三极管截止,蜂鸣器BEEP构不成完整的回路,不能发声。 心跳灯模块电路如下图3.6.2所示,心跳灯模块电路主要是由一个发光二极管和一个电阻组成。LED0连接的是MCU模块的PB3引脚。LED0一旦接收到低电平,发光二极管就接通。所以,可以通过编程对PB3引脚在不同时刻输出高低电平,从而实现不同情况下的灯亮和灯灭。  图3.6.1蜂鸣器模块电路 图3.6.2心跳灯模块电路 3.7 模式选择按键和模式指示灯模块电路 模式选择按键模块电路如下图3.7.1所示,模式选择按键模块电路主要是由两个开关和两个电阻组成。模式选择按键电路中,HM_key连接的是MCU模块的PA11引脚,AUTO_key连接的是MCU模块的PA12引脚。PA11和PA12主要是用来检测HM_key和AUTO_key上是高电平还是低电平,通过软件编程,当PA11引脚得到低电平,也就是KEY1按下时,选择手动模式;当PA12引脚得到高电平,也就是KEY2按下,选择自动模式。 模式指示灯模块电路如下图3.7.1所示,模式指示灯模块电路主要是由两个发光二极管和两个电阻组成。模式指示灯电路中,HM_led连接的是MCU模块中的PB4引脚,AUTO_led连接的是MCU模块的PB5引脚。当MCU给PB4或PB5低电平时,模式指示灯电路中的DS3或DS4导通发光。选择手动模式时,手动模式指示灯亮,选择自动模式时,自动模式指示灯亮。 
图3.7.1 模式选择按键电路 图3.7.2 模式指示灯电路 3.8 复位电路 复位电路是一种用来使电路恢复到起始状态的电路。就像计算器的清零按钮的作用一样,以便回到原始状态,重新进行计算。当系统死机或者程序跑飞的时候,通过复位电路可以使系统恢复到初始状态。所以复位电路是系统中不可或缺的一部分。复位方式有手动复位和上电复位。 本设计的手动复位电路如图3.8所示,复位电路由一个按键,一个电阻和一个电容组成。图中的RESET连接的是MCU模块的NRST引脚,一旦按键按下,NRST就收到一个低电平,使MCU模块复位,从起始状态重新开始执行。 
图3.8 复位电路 3.9 USB转串口模块电路和SWD下载接口电路 USB转串口模块电路如图3.9.1所示,图中的USB中的VCC连接的是电源模块的VUSB,为USB模块的工作提供电压,CH340D-和CH340+分别连接CH340G驱动芯片的第6脚和第5脚,图中CH340G的TXD和RXD引脚分别连接MCU模块的USART1_RX和USART1_TX。USB得到的信息经过CH340G芯片的处理,通过TXD发送到MCU,MCU的信息通过RXD传达给USB实现外设与MCU的通信。 由一个12MHz的晶振和两个电容构成的振荡器,为CH340G提供时钟,让通信有条不紊的持续下去。 
图3.9.1 USB转串口电路 大家比较常用的是Jlink下载器,这种下载器有一个缺点就是使用的Jtag 20PIN接口,太多的PIN会导致一些小型的PCB板很拥挤,也会增加布线的难度。而使用SWD接口下载调试,只需要要使用4个PIN:GND,RST,SWDIO,SWDCLK ,而且下载速度可以达到10M/s,优势显而易见。SWD下载接口电路如下图3.9.2所示,图中SWDIO和SWCLK分别连接MCU模块的SWDIO和SWCLK引脚,上位机就可以通过这个下载接口把程序下载到板子里面,实现功能控制。 图3.9.2 SWD下载接口电路 3.10 电源模块电路 电源模块是可以直接贴装在印刷电路板上的电源供应器,其特点是可为专用集成电路(ASIC)、数字信号处理器 (DSP)、微处理器、存储器、现场可编程门阵列 (FPGA) 及其他数字或模拟负载供电。由于模块式结构的优点甚多,因此模块电源广泛用于交换设备、接入设备、移动通讯、微波通讯以及光传输、路由器等通信领域和汽车电子、航空航天等。 本设计的电源模块电路如下图3.10所示,电源模块有一块集成芯片AMS1117,三个电容,一个电阻和一个发光二极管组成。电源模块为中整个系统提供合适有效的电压。当通电时,发光二极管PWR会亮,提示本系统已经供电,可以进行其他操作了。 
图3.10电源模块电路
4 系统软件设计及实现
4.1 主程序流程图 软件部分的主要任务是完成对光照检测电路和对热释电传感器信号处理电路的输出信号进行处理。基于上述分析,系统软件设计流程如图4.1所示。


图4.1主程序流程图 由系统软件流程图4.1可以进行逻辑编程,通过编程基本实现了智能灯的各项功能。编程的代码见附录。 4.2 手动模式流程图 

 



图4.2 手动模式流程图 4.3 自动模式流程图

图4.3 自动模式流程图
5 调试
5.1 硬件调试规则 硬件调试是利用基本测试仪器(万用表、示波器等),检查用户系统硬件中存在的故障。 硬件调试可分为静态调试与动态调试两步进行。 静态调试是在用户系统未工作时的一种硬件检测。 第一步:目测。检查外部的各种元件或者是电路是否有断点。 第二步:用万用表测试。先用万用表复核目测中有疑问的连接点,再检测各种电源线与地线之间是否有短路现象。 第三步:加电检测。给板加电,检测所有插座或是器件的电源端是否符合要求的值。 第四步是联机检查。因为只有用单片机开发系统才能完成对用户系统的调试。 动态调试是在用户系统工作的情况下发现和排除用户系统硬件中存在的器件内部故障、器件连接逻辑错误等的一种硬件检查。动态调试的一般方法是由近及远、由分到合。由分到合是指首先按逻辑功能将用户系统硬件电路分为若干块,当调试电路时,与该元件无关的 器件全部从用户系统中去掉,这样可以将故障范围限定在某个局部的电路上。当各块电路无故障后,将各电路逐块加入系统中,在对各块电路功能及各电路间可能存在的相互联系进行调试。由分到合的调试既告完成。由近及远是将信号流经的各器件按照距离单片机的逻辑距离进行由近及远的分层,然后分层调试。调试时,仍采用去掉无关元件的方法,逐层调试下去,就会定位故障元件了。 结果如图5.1所示: 
图5.1 硬件调试 5.2 软件调试 软件调试是通过对程序的连接、执行来发现程序中存在的语法错误与逻辑错误并加以排除纠正的过程,也可以通过keil软件,能大大的减少设计过程中的错误。 正确建立keil工程,在工程中添加各个头文件和源程序,编写好自己的程序后进行调试,找错误。如图5.2.1所示错误: 
图5.2.1 软件调试过程中的错误 通过查阅资料以及请教他人,发现这个问题是由于一个宏定义没有添加,导致出现一个错误和这么多的警告。解决办法如图5.2.2所示,在Define栏中加上这个宏定义。 
图5.2.1 软件调试解决 5.3 调试的最终结果 软件调试的最终结果,如图5.3.1所示: 
图5.3.1 软件调试结果 硬件调试的最终结果,如图5.3.2所示: 
图5.3.2 硬件调试结果 设计心得
基于STM32的智能灯以STM32F103作为主控芯片,设置了手动控制、自动控制。在手动控制时,输出不同的PWM占空比实现了对光度的手动调节。在自动控制时,通过热释电传感器和处理电路,检测此时有没有人;通过检验光敏电阻的电压来间接测量感应光度,将电压和预设的阈值进行对比,调整PWM的占空比实现了对光度的自动调节。该LED灯电路简单,很大程度上节省电能,延长LED灯寿命,如果要用大量的灯,这种灯便于管理。 在本次课程设计中,主要有以下体会: 一、掌握了Altium Designer软件的用法和原理图的绘制以及PCB的布板; 二、掌握了keil软件的工程建立,以及对程序的编写。 三、熟悉了热释电继电器的应用。 通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论和实践中得出结论,才能真正为社会服务,从而提高自己的实际动手潜力和独立思考的潜力。在设计的过程中遇到问题,能够说得是困难重重,主要是对所学过的知识理解得不够深刻,掌握得不够牢固,理论和实践不能有机的结合在一起。在设计中遇到的很多专业知识问题,在同学和老师的耐心指导下,最后都迎刃而解了。此次设计也让我明白了思路即出路,有什么不懂不明白的地方要及时请教或上网查询,只要认真钻研,动脑思考,动手实践,就没有弄不懂的知识,而且收获颇丰。 此次课程设计,学到了很多课内学不到的东西,比如独立思考解决问题,查阅资料,出现差错的随机应变和与人沟通的能力,希望今后高质量的完成其他项目。同时,在老师的身上我们学也到很多实用的知识,对给过我帮忙的所有同学和各位指导老师表示衷心的感谢!
main.c文件
#include "main.h"
#define UART_LENTH 1024
char uart1_buff[UART_LENTH]="\0";
void SW_Config(void);
//手动模式的档位
#define LIGHT_MAX 90
#define LIGHT_MIN 2
#define HM_LEVEL 5
uint8_t now_hmlevel = 0;
#define CHPWM(x) ((LIGHT_MAX - LIGHT_MIN)*x/HM_LEVEL)
//光照的阈值
#define GZ_MAX 3980
#define GZ_MIN 585
#define GZ_ERROR 900
//热释电的滤波
#define PERSON_COUNT 20 //判断n次采样
uint8_t value = 0;
uint8_t flag_bitled = 1;
uint8_t beep_count = 0;
uint8_t flag_gz = 1;//有光 1 无光 0
uint8_t flag_person = 0;//有人 1 无人 0
uint8_t flag_auto = 1; //默认自动模式
int main()
{
uint8_t key = 0xff;//获取的按键值
uint16_t person_count[3]={PERSON_COUNT,0,0};//[0] -- 时间片的长度 [1] -- 高电平的次数 [2] -- 低电平的次数
uint16_t gz_value = 0; //光敏电阻的值
NVIC_SetPriorityGrouping(5);//设置中断优先级分组 占先 2 + 次级 2
SW_Config();//SWD模式
USART1_Config(115200);//串口初始化
// USART1_DMATx();
Delay_Init();//延时函数初始化
LED_Config();//LED初始化
BEEP_Config();//BEEP初始化
KEY_Config(); //按键初始化
Person_Config();//热释电初始化
TIM4_Config(72,1000,0); //定时器4初始化 产生PWM控制电灯
TIM3_Config(72,600); //定时器3初始化 实现无极调速 10us的速度
ADC1_Config();//ADC初始化
ADC1_DMAConfig();//DMA初始化
ADC1->CR2 |= (1<<22);//触发ADC1转换
LEDx_ON(LED3_PORT,LED3_PIN);//自动模式的指示灯打开
LEDx_OFF(LED2_PORT,LED2_PIN);//手动模式的指示灯关闭
while(1)
{
if(flag_auto)
{
if(flag_person == 1 && flag_gz == 0)//有人且无光
PWM_ChangeLight(100);//开灯
else
PWM_ChangeLight(0);//关灯
}
if(timeperson[0] > timeperson[1])//采样周期
{
if(person_count[0])
{
person_count[0]--;
if(PERSON) person_count[1]++;
else person_count[2]++;
}
else //启动下一次
{
printf("person_count[1] = %d\r\n",person_count[1]);
printf("person_count[2] = %d\r\n",person_count[2]);
//判断是否有人
if(person_count[1] > person_count[2]) //有人
{
flag_person = 1;
timeperson[1] = 1000;//检测到有人之后,采样变为1s一次,减少采样的频率
}
else
{
timeperson[1] = 20;////检测到无人之后,采样变为20ms一次,增加采样的频率
flag_person = 0;//无人
}
person_count[0] = PERSON_COUNT;
//清零
person_count[1] = 0;
person_count[2] = 0;
}
timeperson[0] = 0;
}
if(timekey[0] > timekey[1])//按键
{
key = Get_KeyValue();
switch(key)
{
case 11://关机
flag_auto = 0; //关闭自动模式
flag_bitled = 0; LEDx_OFF(LED1_PORT,LED1_PIN);//关心跳灯
LEDx_OFF(LED3_PORT,LED3_PIN);//自动模式的指示灯关闭
LEDx_OFF(LED2_PORT,LED2_PIN); //手动模式的指示灯关闭
beep_count = 4; //响2次
PWM_ChangeLight(0);//灯珠
break;//关机 -- 关闭灯 -- 蜂鸣器响 -- 指示灯关
case 1:
LEDx_OFF(LED3_PORT,LED3_PIN);//自动模式的指示灯关闭
LEDx_ON(LED2_PORT,LED2_PIN);//手动模式的指示灯打开
flag_bitled = 1; LEDx_ON(LED1_PORT,LED1_PIN);//开心跳灯
now_hmlevel++;//调节亮度等级
now_hmlevel%=5;
PWM_ChangeLight(CHPWM(now_hmlevel));
flag_auto = 0;
break;//手动
case 2:
LEDx_ON(LED3_PORT,LED3_PIN);//自动模式的指示灯打开
LEDx_OFF(LED2_PORT,LED2_PIN);//手动模式的指示灯关闭
flag_bitled = 1; LEDx_ON(LED1_PORT,LED1_PIN);//开心跳灯
//启动光照+热释电
flag_auto = 1;
break;//自动
}
timekey[0] = 0;
}
if(timebeep[0] > timebeep[1])
{
if(beep_count)
{
beep_count--;
BEEP_TOGGLE();//翻转
}
timebeep[0] = 0;
}
if(timeadc[0]>timeadc[1])//ADC开关量
{
gz_value = Get_GzValue(NULL);
// printf("gz_value =%d\r\n",gz_value);
// printf("flag_gz =%d\r\n",flag_gz);
if(gz_value != 0xffff)//判断是否获得数据
{
if(gz_value >= (GZ_MAX - GZ_ERROR))//无光
flag_gz = 0;
else
flag_gz = 1;
}
timeadc[0] = 0;
}
if(timeled[0] > timeled[1])//指示灯
{
if(flag_bitled)
LEDx_TOGGLE(LED1_PORT,LED1_PIN);
timeled[0] = 0;
}
}
}
void SW_Config(void)
{
//打开AFIO时钟
RCC->APB2ENR |= (1<<0);
//SWD模式
AFIO->MAPR &= ~(0x7<<24);
AFIO->MAPR |= (0x2<<24);
}
adc.c文件
#include "adc.h"
#include "stdio.h"
void ADC1_Config(void)
{
//配置ADC1_IN1 -- PA1
//打开时钟 A端口+ADC1
RCC->APB2ENR |= (1<<2)|(1<<9);
//PA1配置为模拟输入
GPIOA->CRL &= ~(0xF<<4);
//配置ADC1_IN1
//1.ADCCLK -- PCLK/8 -- 84/8
RCC->CFGR &= ~(0x3<<14);
RCC->CFGR |= (0x3<<14);
//2.配置规则组的转换长度+IN1放在第一位
ADC1->SQR1 &= ~(0xf<<20);
ADC1->SQR1 |= (0x0<<20);//n次转换
//规则组第1位置
ADC1->SQR3 &= ~(0x1f<<0);
ADC1->SQR3 |= (0x1<<0);
//3.配置转换模式:单次/连续 扫描/不连续
//单次/连续 -- ADC_CR2 CONT位
ADC1->CR2 |= (1<<1);
//ADC1->CR2 &= ~(1<<1);
//扫描 -- ADC_CR1 SCAN
ADC1->CR1 &= ~(1<<8);//不扫描
//ADC1->CR1 |= (1<<8);//扫描
//4.设置采样速率 3+12 = 15 ADCCLK
//设置采样速率:ADC_IN1 -- ADC_SMPR2 3个周期
ADC1->SMPR2 &= ~(0x7<<3);
ADC1->SMPR2 |= (0x7<<3);
//5.禁止不连续采样
ADC1->CR1 &= ~(1<<11);
//6.选择触发类型 软件触发
ADC1->CR2 |= (0x1<<20);
ADC1->CR2 |= (0x7<<17);
//7.设置数据对齐 -- 右对齐
ADC1->CR2 &= ~(1<<11);
//复位校准
ADC1->CR2 |= (1<<3);
while((ADC1->CR2 & (1<<3)) == 1);
//校准
ADC1->CR2 |= (1<<2);
while((ADC1->CR2 & (1<<2)) == 1);
//8.启动ADC ADC_CR2 -- ADON
ADC1->CR2 |= (1<<0);
}
#define ADC_A(x) (3.3*x/4096)
u16 dma_adcbuff[30];
void ADC1_DMAConfig(void)
{
//时钟打开
RCC->AHBENR |= (1<<0);
//ADC1配置DMA模式
ADC1->CR2 |= (1<<8);
//关闭DMA1
DMA1_Channel1->CCR &= ~(1<<0);
//清相关标志位
DMA1->IFCR |= (1<<0)|(1<<1)|(1<<2)|(1<<3);
//设置源和目标 -- 源 -- 外设(ADC_DR) 目标 -- 存储器(dma_adcbuff)
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
DMA1_Channel1->CMAR = (uint32_t)dma_adcbuff;
//配置传输数据的总数
DMA1_Channel1->CNDTR = sizeof(dma_adcbuff)/sizeof(dma_adcbuff[0]);
//优先级 -- 高
DMA1_Channel1->CCR &= ~(0x3<<12);
DMA1_Channel1->CCR |= (0x2<<12);
//方向:外设到存储器
DMA1_Channel1->CCR &= ~(0x1<<4);
//外设不增量
DMA1_Channel1->CCR &= ~(0x1<<6);
//存储器不增量
DMA1_Channel1->CCR |= (0x1<<7);
//外设数据的宽度
DMA1_Channel1->CCR &= ~(0x3<<8);
DMA1_Channel1->CCR |= (0x1<<8);
//存储器数据的宽度
DMA1_Channel1->CCR &= ~(0x3<<10);
DMA1_Channel1->CCR |= (0x1<<10);
//循环模式
DMA1_Channel1->CCR |= (0x1<<5);
//启动DMA1_1
DMA1_Channel1->CCR |= (0x1<<0);
}
//通过DMA处理数据 -- 滤波
//0xffff -- 数据没有获得
uint16_t Get_GzValue(uint16_t *pdata)
{
uint16_t value = 0XFFFF;
uint32_t i = 0;
uint32_t lenth = sizeof(dma_adcbuff)/sizeof(dma_adcbuff[0]);
uint32_t sum = 0;
if(DMA1->ISR &(1<<1))//DMA传输完成
{
//清标志位
DMA1->IFCR |= (1<<1);
for(i=0;i<lenth;i++)
sum+= dma_adcbuff[ i];
value = sum/lenth;
if(pdata != NULL)
*pdata = value;
}
return value;
}
beep.c文件
#include "beep.h"
void BEEP_Config(void)
{
//打开时钟 -- PA15
RCC->APB2ENR |= (1<<2);
//配置PA8-- 通用推挽输出 速度50MHz 无上拉下拉
GPIOA->CRH &= ~((0xf<<28));
GPIOA->CRH |= (0x3<<28);
//关闭蜂鸣器
BEEP(0);
}
delay.c文件
#include "delay.h"
void Delay_nus(uint32_t time)
{
while(time--)
{
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}
}
#define DELAY_1US 72
#define DELAY_1MS 72000
uint32_t runtime = 0;//保存当前系统运行的时间 单位ms
vu32 timeled[2] = {0,1000};//初始值 延时值
vu32 timebeep[2] = {0,100};
vu32 timekey[2] = {0,8};
vu32 timeusart1[2] = {0,100};
vu32 timeadc[2] = {0,1000};
vu32 timeperson[2] = {0,100};
void Delay_Init(void)
{
if(SysTick_Config(DELAY_1MS) == 1)//初始化滴答定时器
{
while(1);
}
TIM2_Config(1,72);
}
void SysTick_Handler(void)
{
runtime++;//每1ms加1次
timeled[0]++;
timebeep[0]++;
timekey[0]++;
timeusart1[0]++;
timeadc[0]++;
timeperson[0]++;
}
void Delay_ms(uint32_t time)
{
uint32_t time1 = runtime;//保存当前的系统时间
while((runtime - time1 )< time);
}
void TIM2_Config(uint16_t psc,uint16_t reload)
{
//打开时钟 TIM2
RCC->APB1ENR |= (1<<0);
//初始化TIM7
//分频
TIM2->PSC = psc-1;
//计数器清零
TIM2->CNT = 0;
//重装载初始化
TIM2->ARR = reload-1;
//TIM6 CR1 -- 使能(定时器、重装载值缓存区、事件)
TIM2->CR1 = 0;
//中断配置
//中断打开
TIM2->DIER |= (1<<0);
//设置中断通道使能以及优先级
NVIC_SetPriority(TIM2_IRQn,0xf);//2 2 11 11
NVIC_DisableIRQ(TIM2_IRQn);
//定时打开
TIM2->CR1 &= ~(1<<0);
}
//1us 进入中断一次
uint32_t run_ustime = 0;
void TIM2_IRQHandler(void)
{
TIM2->SR &= ~(1<<0);//清标志位
if(run_ustime > 0) run_ustime--;
}
void Delay_us(uint32_t time)
{
run_ustime = time;
TIM2->CNT = 0;//计数器清零
TIM2->CR1 |= (1<<0);//定时器打开
while(run_ustime); //延时等待
TIM2->CR1 &= ~(1<<0);//关闭定时器
}
key.c文件
#include "key.h"
#include "delay.h"
//KEY1 -- PA11 KEY2 -- PA12
void KEY_Config(void)
{
//打开端口时钟
RCC->APB2ENR |= (1<<2);
//配置为输入模式 -- 浮空输入
GPIOA->CRH &= ~((0xf<<12)|(0xf<<16));
GPIOA->CRH |= (0x4<<12)|(0x4<<16);
}
uint8_t Get_KeyValue(void)
{
static uint32_t count1 = 0;//检测到低电平的次数
static uint32_t count2 = 0;//检测到低电平的次数
if(KEY1 == 0)
{
count1++;
if(count1 > 120)
{
count1 = 0;
return 11;
}
}
else if(count1 > 15 && count1 <50)
{
count1 = 0;
return 1;
}
else //滤波
count1 = 0;
if(KEY2 == 0) count2++;
else if(count2 > 15)
{
count2 = 0;
return 2;
}
else //滤波
count2 = 0;
return 0xff;
}
led.c文件
#include "led.h"
#include "delay.h"
#include "bitband.h"
void LED_Config(void)
{
//打开LED1 2 3灯的时钟 -- B
RCC->APB2ENR |= sbit(3);
//配置PB3 4 5-- 通用推挽输出 速度50MHz 无上拉下拉
//通用输出 -- 清零赋值
GPIOB->CRL &= ~((0xf<<12)|(0xf<<16)|(0xf<<20));
GPIOB->CRL |= (0x3<<12)|(0x3<<16)|(0x3<<20);
//灭灯
LEDx_OFF(LED1_PORT,LED1_PIN);
LEDx_OFF(LED2_PORT,LED2_PIN);
LEDx_OFF(LED3_PORT,LED3_PIN);
}
light.c文件
#include "light.h"
void TIM3_Config(uint16_t psc,uint16_t reload)
{
//打开时钟 TIM3
RCC->APB1ENR |= (1<<1);
//初始化TIM3
//分频
TIM3->PSC = psc-1;
//计数器清零
TIM3->CNT = 0;
//重装载初始化
TIM3->ARR = reload-1;
//TIM3 CR1 -- 使能(定时器、重装载值缓存区、事件)
TIM3->CR1 = 0;
//中断配置
//中断打开
TIM3->DIER |= (1<<0);
//设置中断通道使能以及优先级
NVIC_SetPriority(TIM3_IRQn,0xf);//2 2 11 11
NVIC_EnableIRQ(TIM3_IRQn);
//定时关闭
TIM3->CR1 &= ~(1<<0);
}
uint16_t new_ccr = 0;
void TIM3_IRQHandler(void)
{
int offset = new_ccr - TIM4->CCR2;//求出偏差值
TIM3->SR &= ~(1<<0);//清标志位
if(TIM4->CCR2 <= TIM4->ARR && TIM4->CCR2 >= 0)//
{
//分为三种情况
if(offset == 0) TIM3->CR1 &= ~(1<<0);//关闭定时器,不修改CCR值
else if(offset > 0) TIM4->CCR2 += 1;
else if(offset < 0) TIM4->CCR2 -= 1;
}
}
//参数:pwmvalue 0~100
void PWM_ChangeLight(uint32_t pwmvalue)
{
//把PWMValue转换为CCR寄存器的值 pwmvalue(0~100) -- CCR(0~ARR重装载值)
uint16_t arr = TIM4->ARR;
//计算出要修改的PWM波的占空比所对应的CCR寄存器值
new_ccr = arr*pwmvalue/100;
//启动一个定时器去实现无极调光
TIM3->CR1 |= (1<<0);
}
person.c文件
#include "person.h"
void Person_Config(void)
{
//打开端口时钟
RCC->APB2ENR |= (1<<2);
//配置为输入模式 -- 浮空输入
GPIOA->CRL &= ~((0xf<<12));
GPIOA->CRL |= (0x4<<12);
}
tim.c文件
#include "tim.h"
//1KHz -- 1ms -- 72 1000 500
//PB7 -- TIM_CH2 -- 复用
void TIM4_Config(uint16_t psc,uint16_t arr,uint16_t ccr)
{
//B端口时钟 PB7 TIM4时钟打开
RCC->APB2ENR |= (1<<3);
RCC->APB1ENR |= (1<<2);
//PB7配置为复用功能
GPIOB->CRL &= ~((0xf<<28));
GPIOB->CRL |= (0xb<<28);
//TIM4的定时时长 -- 1ms
TIM4->CNT = 0;
TIM4->PSC = psc-1;
TIM4->ARR = arr-1;
TIM4->CCR2 = ccr;//比较寄存器值
//ARR的预装载 -- CR1
TIM4->CR1 = 0;
//输出模式、CCR预装载、快速使能
TIM4->CCMR1 = 0;
//PWM1模式
TIM4->CCMR1 &= ~(0x7<<12);
TIM4->CCMR1 |= (0x6<<12);
//有效电平高电平 使能OC输出
TIM4->CCER = 0;
TIM4->CCER |= (1<<4);
//开启定时器
TIM4->CR1 |= (1<<0);
}
usart.c文件
#include "usart.h"
#include "beep.h"
#include "led.h"
#include "string.h"
#include "stdio.h"
void USART1_Config(uint32_t brr)
{
float div;//分频数
uint32_t div_m;//整数部分
uint32_t div_f;//小数部分
//A端口时钟 PA9 PA10 USART1时钟打开
RCC->APB2ENR |= (1<<2)|(1<<14);
//PA9 PA10配置为复用功能
GPIOA->CRH &= ~((0xf<<4)|(0xf<<8));
GPIOA->CRH |= (0xb<<4);
GPIOA->CRH |= (0x4<<8);
//配置串口 232协议:字长 + 奇偶校验使能
USART1->CR1 = 0;
//停止位 1位
USART1->CR2 &= ~(0x3<<12);
//配置全双工模式:接收发送打开
USART1->CR1 |= (0x1<<2)|(0x1<<3);
//配置波特率
div = 72000000.0/(16.0*brr);
div_m = (uint32_t)div;
div_f = (uint32_t)((div-div_m)*16);
USART1->BRR = div_m<<4|div_f;
//使能串口1
USART1->CR1 |= (1<<13);
}
void USART1_NVICConfig(void)
{
//接收中断使能
USART1->CR1 |= (1<<5);
//NVIC
NVIC_SetPriority(USART1_IRQn,1);//占先 0 次级 1
NVIC_EnableIRQ(USART1_IRQn);
}
void USART1_Echo(void)
{
uint8_t data;
//接收数据 -- 判断数据是否到来(SR) -- 阻塞
while((USART1->SR & (1<<5)) == 0);
//保存数据
data = USART1->DR;
//发送数据 -- 等待上次发送完成(SR -- 7/6)
while((USART1->SR & (1<<7)) == 0);
USART1->DR = data;
}
uint8_t USART1_RecvData(void)
{
//uint8_t data;
//接收数据 -- 判断数据是否到来(SR) -- 阻塞
while((USART1->SR & (1<<5)) == 0);
//保存数据
//data = USART1->DR;
return USART1->DR;
}
void USART1_SendData(uint8_t data)
{
//发送数据 -- 等待上次发送完成(SR -- 7/6)
while((USART1->SR & (1<<7)) == 0);
USART1->DR = data;
}
void USART1_SendString(uint8_t *p)
{
while(*p)
{
USART1_SendData(*p++);
}
}
int fputc(int c, FILE *stream)
{
USART1_SendData((uint8_t)c);
return c;
}
uint8_t dmabuff[]="深圳信盈达郑州分公司\r\n";
void USART1_DMATx(void)
{
//时钟打开
RCC->AHBENR |= (1<<0);
//USART1_Tx配置DMA模式
USART1->CR3 |= (1<<7);
//关闭DMA1
DMA1_Channel4->CCR &= ~(1<<0);
//清相关标志位
DMA1->IFCR |= (1<<12)|(1<<13)|(1<<14)|(1<<15);
//设置源和目标 -- 源 -- 外设(USART1_DR) 目标 -- 存储器(dma_buff)
DMA1_Channel4->CPAR = (uint32_t)&USART1->DR;
DMA1_Channel4->CMAR = (uint32_t)dmabuff;
//配置传输数据的总数
DMA1_Channel4->CNDTR = sizeof(dmabuff)/sizeof(dmabuff[0]);
//优先级 -- 高
DMA1_Channel4->CCR &= ~(0x3<<12);
DMA1_Channel4->CCR |= (0x2<<12);
//方向:存储器到外设
DMA1_Channel4->CCR |= (0x1<<4);
//外设不增量
DMA1_Channel4->CCR &= ~(0x1<<6);
//存储器增量
DMA1_Channel4->CCR |= (0x1<<7);
//外设数据的宽度
DMA1_Channel4->CCR &= ~(0x3<<8);
DMA1_Channel4->CCR |= (0x0<<8);
//存储器数据的宽度
DMA1_Channel4->CCR &= ~(0x3<<10);
DMA1_Channel4->CCR |= (0x0<<10);
//循环模式
DMA1_Channel4->CCR &= ~(0x1<<5);
//关闭DMA1_4
DMA1_Channel4->CCR &= ~(0x1<<0);
}
////void USART1_DMATxTest(void)
////{
//// //关闭数据流
//// DMA2_Stream7->CR &=~(1<<0);
//// //清相关标志位
//// DMA2->HIFCR |= (1<<22)|(1<<24)|(1<<25)|(1<<26)|(1<<27);
//// //确定传输的次数
//// DMA2_Stream7->NDTR = sizeof(dmabuff)/sizeof(dmabuff[0]);
//// //启动DMA
//// DMA2_Stream7->CR |= (1<<0);
////}
////0 -- 失败 1 -- 成功
void Uart1_DMA_Printf(char *p,uint32_t lenth)
{
//关闭DMA1
DMA1_Channel4->CCR &= ~(1<<0);
//清相关标志位
DMA1->IFCR |= (1<<12)|(1<<13)|(1<<14)|(1<<15);
//修改存储器地址
DMA1_Channel4->CMAR = (uint32_t)p;
//确定传输的次数
DMA1_Channel4->CNDTR = lenth;
//启动DMA
DMA1_Channel4->CCR |= (0x1<<0);
}
完整的Word格式文档51黑下载地址:
基于STM32的智能灯.docx
(1.51 MB, 下载次数: 575)
|