一、背景
前面为使用 RT-Thread Studio 做好了准备,并且完成了第一步:实现PWM驱动电机,操控IO 输出的 Pin 组件工程模板中已经安装,故在 IO 输出上没有遇到障碍,和 Arduino 的使用方式基本一致,但在自己添加 PWM 组件时遇到点挫折,走了点弯路,但也算是有收获,基本了解的 RT-Thread Studio 的硬件驱动组件(暂时只涉及 STM32)添加方式,最后自己重新梳理后,发现并不比 Arduino 困难,但在充分发挥单片机硬件特性上,应该是具有优势的,因为它的硬件驱动是基于 ST 官方提供的工具 STM32CubeMX,更为灵活、全面。
后续再基于自己梳理的驱动组件添加方式,尝试新的组件,但愿自己的总结是正确的。
本篇完成原来激活程序的后两个功能:
- 基于转动编码器的反馈控制电机转动;
- 采集电机工作的供电电压和驱动电流。
二、需求
从学习 RT-Thread角度考虑,本篇所做的需求是:
- 尝试IO中断的应用
- 尝试AD功能的实现
这两个是单片机应用中最基础的需求,看看基于 RT-Thread实现是否方便。
三、实施
这一步所做的尝试是在前面的程序基础上追加;后续所有的学习都是这个思路,每一步都是在前面的基础上增加,像盖楼一样,逐步形成一个有机的整体。而不是像学校内的实验那样,每个实验都是孤立的,相互之间缺少联系。
这种逐步积累性的学习过程,更接近实际应用;现实中的一个具体应用需求,往往是很多功能组合才能解决。这样做有两个好处:
- 可以间接地复习前面学过的内容。
- 学会处理多个功能之间的衔接。
但要实现这种不断累积的实验方式,且前后呼应,需要前面的内容可以为后面提供铺垫,否则难以为继。
就拿IO输出的连续来说吧,如果是用点亮LED作为素材,就很难引入中断反馈的内容,将两者有机的结合。虽说可以用按键控制LED亮、灭为题,但这有违正常应用的设计思路,通常真实应用中,对按键的响应采用查询方式,因为一是无需那么快的响应,二是按键需要相应的防误操作处理。而电机转动的编码器反馈却只能用中断采集!
3.1 实现编码器反馈采集
如前所述,编码器信号采集通常使用中断方式。此处尝试基于RT-Thread如何实现IO口中断输入及响应。
IO 口中断功能在 RT-Thread 中归在 Pin 设备中,创建工程时已经将此组件安装,故只需参考《RT-Thread编程指南》(官方文档)在 main.c 中编写中断相应代码即可,无需在配置上做任何操作。
在Arduino中,IO口中断使用方式如下:
定义中断服务函数,在产生中断时调用:
定义产生中断的IO口、触发方式、触发后调用的服务函数,也就是挂接中断:
在RT-Thread中也十分类似:
首先定义中断服务函数:
再在初始化过程中定义产生中断的IO口、触发方式、触发后调用的服务函数:
除函数名不同外,几乎一样,编译后执行一切顺利,可见各家的封装思路基本相同。
3.2 实现电机供电电压和工作电流采集
因为单片机应用中,数字量输入、输出在前面两步已经完成,模拟量输出也算是有了(PWM输出),另一个主要的功能就是模拟量采集。虽说目前采集了电机的供电电压和工作电流还没有实际的应用,但后续在完善小车功能上,这是一个基础,所以先行实现。关键是也为了体验一下RT-Thread的ADC组件。
这一步需要进行相应的配置,参考前一篇所述,前面两步不用再做了,从第三步开始。
第一步:在RT-Thread Setting中添加 ADC 组件:
第二步:在board.h 文件中添加ADC:
第三步:用STM32CubeMX配置AD硬件资源:
生成相应的AD驱动程序:
第四步:将上述AD初始化函数拷贝到 board.c 文件中:
第五步:在 stm32f4xx_hal_conf.h 中修改:
第六步:参考《RT-Thread 编程指南》在 main.c 中编写 AD 采样相应代码,对照原来的激活程序功能:连续采集四路数据10次,取平均值后转换的电压、电流,通过串口输出,在电脑的终端程序窗口显示。
Arduino 中支持用 print 函数通过串口方便的输出信息,在 RT-Thread 中,可以使用类似的函数 rt-kprintf 实现;因为我增加了 uLog 组件,故还可以使用 LOG_D 函数输出。这一点 RT-Thread 比 Arduino 更加完善、方便。
首先是对ADC部分初始化:
然后循环采样、输出:
对照原来 Arduino 程序,实现方式完全一样。编译后执行,结果 OK。看来前面总结的硬件驱动组件的添加方式是可行的。
在实现上述ADC功能过程中,遇到以下小问题:
1)因为将AD结果转换为电压、电流时,系数是浮点数,原来在Arduino中使用的语句在此编译提升语法错误,查阅了一下资料,未果。因这一步重点是 ADC 的使用,故暂时忽略,用整形计算代替了。后面再查查资料,看看如何化解。
不支持的语句:
miLeft = int(float(miLsum)/10*SCALE_CURRENT);
2)感觉AD的配置中,RT-Thread 只支持 IO 引脚的配置,并不支持 ADC 本身的性能改变,如AD的分辨率,似乎是固定设置好的。不知道我理解的是否对?如果如此,就和Arduino 类似了,少了许多灵活性。
3.3 RT-Thread 编程中的小疑问
在 RT-Thread 中,引脚是用内部定义的序号声明的,不能直接是 STM32 的引脚标注方式(如 PA1、PB0 等),但提供了一个宏定义 rt_pin_get() , 我在调试中感觉如果定义:
#define CT1_LEFT rt_get_pin(“PB.6”)
然后直接在程序中使用 CT1_LEFT,会在每次使用时,执行 rt_get_pin(),是不是会影响程序效率?
所以这次编写程序时,增加了若干 IO 口的序号变量,在初始化过程中将引脚编号赋值,之后直接使用引脚编号变量。如下:
#define LED0_PIN rt_pin_get("PC.13")
rt_uint16_t g_u16LedPin;
……
g_u16LedPin = LED0_PIN;
rt_pin_mode(g_u16LedPin, PIN_MODE_OUTPUT); // set LED0 pin mode to output
不知道这样是否是多此一举??
四、结语
通过上述尝试,感觉 RT-Thread 在使用上和 Arduino 的构思应该是很相似的,对于硬件资源的操控也很方便,借助于 STM32CubeMX 的助力,也算是基本实现了“与硬件无关的编程”。
在实施过程中也遇到一些疑惑,这些可以在后续的尝试中得到化解。
软、硬件环境都已就绪,后面开始真正的应用编程了,那才是真正的 RT-Thread 应用!
——————————
文章中程序下载:
RTT激活程序2:
链接:https://pan.baidu.com/s/1oVvVm7mwKIsyQmNYQmg9wQ
提取码:k537
RTT激活程序3:
链接:https://pan.baidu.com/s/1sw9wiw7vEtWmlbKX_5rUUg
提取码:gwwe