拿到STM32F469的板子也有一段时间了,除了发了个晒板贴,还没发开发帖子,研究了一段时间,但是有些高大上的功能还玩不转,所以先从简单的开始,来把经验分享一下。
主要内容有两个,如题,分别是温度湿度检测和障碍物检测报警
实现功能:
[1] 实时检测室内的温度和湿度,并通过串口发送,PC串口助手显示(LCD还没调好,所以先用串口输出温湿度数据)
[2] 当有障碍物进入开发板(光电管)一定范围时,开发板发出震动警报,并且亮红灯
硬件使用STM32F469IDISCO开发板+Gokit的扩展板,主要用到上面的DHT11温湿度传感器,检测温湿度;反射型光电传感器,检测有物体进入可检测区域;直流无刷电机,当光电传感器检测到无提示,电机启动,发出震动警报(电机上有齿轮,会震动,换成偏心轮震动更强烈)。涉及到的内容有GPIO,USART,SysTick,EXTI,TIM,PWM等。
1. 硬件分析:
STM32F469 DISCO开发板设计了一个兼容Arduino的接口,而GoKit扩展板也是兼容该接口的,刚好可以即插即用(可见兼容很重要啊)。
先查看看扩展板的原理图(由于这是别人家的图,就不发原理图了,有兴趣的自己去他们论坛下载):
温湿度传感器:
(
数据手册)
使用的是DHT11,使用单总线数据传输,只有一根数据线,命令和数据都通过该线传输,当主机发送开始信号后,从机响应,然后开始输出数据,DOUT引脚连接的是Arduino接口的D3脚。
光电传感器:
原理,左边的光电管导通,发出红外光,当观点管前面没有障碍物或距离较远,就不会有光反射回来,右边光电管截止,比较器正输入端电压高于负输入端,OUT输出高电平,当障碍物到光电管可检测距离之内时,右边光电接收管接收到红外光而导通,正输入端电压低于负输入端,OUT输出低电平,输出端OUT接的是Arduino D2。
直流无刷电机:
Aruidno的 D4,D5脚经电机驱动芯片后分别接入电机两端,一端输入PWM波形,另一端低电平,电机正传,反之,电机反转。
再看STM32F469DISCO开发板的Arduino接口部分:
D2,D3,D4,D5分别接到 PG13,PA1,PG12,PA2,由于stm32f469ni芯片的PG12引脚不具有PWM功能,所以D4无法驱动电机,只能实现一个方向的转动,不过也够了,这个帖子里电机是用来实现震动的,转向不重要。
所以,引脚设置如下:
D2——光管比较器的输出端——PG13——外部中断
D3——温湿度传感器DOUT——PA1——输出/输入
D5——电机PWM驱动引脚——PA2——TIM9_CH1
另外,板载虚拟串口,连接的是USART3,使用它输出调试信息和温度湿度数据:
USART3_TX——PB10——复用
USART3_RX——PB11——复用
2. 使用STM32CUBEMX生成工程
本帖使用STM32CUBEMX4.12.0,STM32F4CUBE 4.10.0,可以在官网或社区下载。
新建工程的帖子论坛已经很多了,我就不详细写了,主要说一下主要的配置:
没有按板子型号选,是因为如果选择开发板型号,cube会默认把该型号开发板上使用引脚全都初始化,这会带来很大的方便(特别是像这块板,引脚特别多),但我现在不想要这样,所以自己选芯片型号。
按照上面的分析,配置好相应的引脚,打开TIM9,PWM模式,打开USART3异步模式,另外如果使用外部晶振HSE,还需要打开RCC。加多一个PD3设置为输出模式,用于点亮板子上的绿色LED LD7,显示程序运行状态,截图中漏掉了。
时钟配置,随便设置一下就好,这里设置到最高180M只是为了测试玩玩,其实这几个应用完全没必要这么高。
PG13设置为上升沿下降沿双触发,因为要同时检测有东西进入和离开检测区域。
USART3的设置,默认即可。
定时器设置,向上计数,不分频,period=9000-1,pules=900,即设置PWM频率20K,占空比10%。
最后保存设置,生成MDK工程,打开工程,开始添加自己的代码。
3. 代码添加与测试。
首先,在主函数while循环中加入点灯的代码,测试看程序是否正常:
编译下载运行一气呵成,没任何问题。
然后完善串口部分的代码,使得我们可以使用printf函数来方便的输出想要的信息(温湿度)。
加入上面的代码的代码就行了,主函数中继续加入测试代码,
然后运行看效果:
到这里,准备工作做好了,下面才是真正的主要阶段,如何读取温湿度传感器的数据,以及根据红外传感器来控制电机。
l 读取温湿度传感器DHT11的数据
在写读取数据部分之前,先有些准备工作要做:
1. 实现微秒延时(delay_us)
以前用标准库的时候,基本都是用SysTick来做,ms和us延时只需要调用一个函数就行了,但是在cube库里面就不能这样做,为什么呢?看一下标准库ms和us延时的实现方法,每次根据时间计算出一个tick值,写入SysTick->LOAD寄存器,等待计数器到达0就行了,但是Cube库中,Systick已经用来实现固定的1ms的延时即 HAL_Delay(),每1ms都会产生一次中断,并且重装载并重新开始计数,如果我们还按之前的做法,就会破坏这个函数的准确性,造成很多cube库函数出现不可预知的问题,所以此法行不通。当然,ms延时已经有了,直接用HAL_Delay(),我们需要自己实现us延时。
方法肯定不止一种,首先想到的是使用一个定时器,比如基本定时器TIM6或者其他定时器,这样做当然是可以的。不过能不能还是用SysTick实现呢?答案当然也是可以的。
Cube库中,设置SysTick的时钟等于系统时钟,
设置Tick次数为SYSCLK/1000,即tick SYSCLK/1000次就是1ms,此时产生中断,那tick SYSCLK/1000000不就是1us吗,只是没有产生中断而已,所以实现方法就很简单了。
如图,直接用宏定义#defineDelay_ms HAL_Delay是为了移植方便,就不用去代码中一个一个把Delay_ms()改为HAL_Delay()了。
Delay_us可以写成一个函数,但是为了提高效率,我使用了宏定义(亲测在延时很小的时候效率相差很大),然后宏定义一个常量是当前系统时钟,当修改了系统时钟时,只需要修改这个宏就可以了。思路就是,先计算出延时时间所需要的Tick数,SYSCLK/1000000对应1 us,记录下当前SysTick->VAL的值,然后counter一直在递减,直到达到计算好的次数,延时时间到,退出,if语句成立时,表明定时器产生下溢,重装载了VAL。
测试一下,看是否正确:
延时1us、10us、100us误差都是0.125us,说明这是IO口翻转本身需要的时间,我们的延时函数精度还是非常高的。
2. 配置DHT11的Dout引脚
前面说了,DHT11使用单总线,意味着与之相连的引脚既要输出也要输入,分别用于发送命令和接受数据,由于程序中需要频繁的切换输入输出状态,同样处于效率考虑,我们用直接操作定时器的方式,并且依旧使用宏定义来实现,而不是使用库函数,当然,也就是把库函数自己写一遍而已,去掉了各种判断和循环。
这段代码完全考虑了移植性的问题,非常方便,只需要修改前三行就行了,不过STM32F1系列例外,该系列GPIO寄存器不一样,需要修改。
3. 准备工作做好了,可以开始读数据了
根据手册,读取数据前需要先发送开始命令,然后DHT11传感器产生回应,然后开始发送数据。对于主机来说就是
设置gpio为输出——发送开始命令——设置为输入——检测回应——接收数据
通信时序如下(数据手册):
第三幅图是从机回应信号,然后从机就开始发送数据,连续发5字节,前4字节是数据,第5字节是检验码,数据格式:
分析好了时序,就可以写代码测试了:
发送开始命令的函数如下:
然后然后主函数中测试:
每隔0.5秒发送一次开始命令,但是没做任何事情,只是把DATA引脚设置为输入状态,然后等待,看开始命令是否成功,传感器是否有数据发出来:
每0.5s发送一次开始信号
20ms低电平(手册写至少18ms)作为开始信号,然后拉高,并设置为输入状态,等待从机响应
从机相应信号,低/高电平分别80多微秒,
后面就是一连串的数据,一共5字节,可见我们只发送了开始信号,并不需要做其他”读”操作,数据即可自动发送出来,使用非常方便,接下来只需要按照协议解析数据就可以,并让数据显示出来就可以了,这部分就不详介绍了,直接看结果:
这是用串口打印出来,PC上用串口助手接收的结果,手册说该传感器25度是精度+-2度,现在我这里手机显示温度21度,还是比较接近的。不过这个温度传感器不适用于需要高精度测量的应用。
温湿度测量部分就写完了,好像有点啰嗦,有耐心看到这里的我表示非常感谢。
l 障碍物检测与报警
这一个就比较简单了,因为GPIO引脚和定时器参数在前面生成工程的时候已经配置好了,不过之前忘了打开EXTI中断,这里补上,然后重新生成工程,再编译:
要做的工作很简单,根据前面的分析,当没有障碍物在光电管前面时,比较器输出高,当障碍物进入可检测范围,挡住光电管,则比较器输出低,相应引脚上会产生下降沿中断,此时应打开点击,产生震动发出警报;当障碍物远离,比较器又变回输出高,此时产生上升沿中断,应关闭点击,解除警报。
得益于Cube库的方便,上面所述功能代码做起来都很简单:
中断回调函数,发生EXTI中断时会调用,在里面实现自己需要的功能即可。
打开和关闭电机,都只需要调用一个函数就能完成,非常方便。
编译上诉程序,下载到开发板中,就能顺利运行了,当有物体在光电管上面一定距离内,电机就会转动,发出震动,同时有个红色LED亮(这是硬件上设计的),当物体远离或离开正上方之后,震动停止,电机被关闭,LED熄灭,运行效果见视频。
从视频里可以看到,黑色物体检测最大距离只有大约15mm,而白色物体有40~50mm之多,因为白色物体反光能力强,黑色物体反光能力弱,所以要更近的距离,反射的光才足以使光电三极管导通,从而检测到物体。当然检测距离可以通过板子上的一个可调电阻在一定范围内调节。
最后附近是完整工程。
上面直接添加的视频好像不行,连接在这里:
http://v.youku.com/v_show/id_XMTQyMjkyMDc4MA==.html?from=y1.7-1.2
(注:代码一部分原创一部分参考了别人的,仅供学习交流)
晒板子帖子:
https://bbs.21ic.com/icview-1240984-1-4.html