Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
ADC
RT-Thread
基于RT-Thread的STM32G4开发第二讲第二篇——ADC
发布于 2025-05-07 11:59:13 浏览:30
订阅该版
[tocm] 基于RT-Thread的STM32G4开发第二讲第二篇——ADC --- # 前言 本文使用的是RT-Thread最新的驱动5.1.0,兼容下面的所有驱动。使用的开发板是蓝桥杯嵌入式国信长安的开发板,MCU是STM32G431RBT6。 我已经写了基于STM32F4关于ADC的文章(见上一篇),为什么还要写基于STM32G4的呢。原因是RT-Thread对STM32G4的ADC外设的适配极其不好,初始化缺失还不报错,为了实现这个功能,花费了我很多时间,我觉得有必要分享出来。 本章同上一章有很多内容相似,我都重新说一遍,这样大家按选择看一篇文章就行 --- # 一、RT-Thread工程创建 先在RT-Thread studio中创建好工程,参考下面的文章使得驱动5.1.0全构建不报错和警告,如图所示。 [RT-Thread studio的驱动5.1.0报错修改](https://blog.csdn.net/weixin_58172114/article/details/147655302?spm=1001.2014.3001.5501)  **下面工程名改为IO_ADC** 不要着急修改时钟配置,这里按我方法来,打开自动生成的CubeMX Settings(找不到的话点击窗口,恢复窗口布局,在项目资源管理器下。在CubeMX中按裸机编程一样,把时钟和需要用到的外设都配置好。配置详情我就不说了,看前面的文章就行。 注意使用到的外设都要配置,开局使用串口1作为控制台串口,所以这里也要配置。示例如下   这里我使用了ADC1和ADC2,配置如下,对于ADC的详细参数,也要配置一下,可以作为后面RT-Thread的参考,关于ADC的详情配置见下文。 [STM32LL库编程系列第八讲——ADC模数转换](https://blog.csdn.net/weixin_58172114/article/details/147118834?spm=1001.2014.3001.5501)  **这里的IDE要选择EWARM,也就是保持默认,很重要**,其他照常  到这一步就可以生成工程了   第一次生成工程后要把cubeMX关闭掉,这样RT-Thread studio才会同步(下面每一步的图片参考上一篇文章) 点击左边文件,cubemx(没有的话,刷新一下),右键Src,资源配置,排除构建 打开cubemx的mian.c复制函数`void SystemClock_Config(void)`,包括函数名全部复制,在打开drivers/drv_clk.c,把`void system_clock_config(int target_freq_mhz)`函数删了,把复制的`void SystemClock_Config(void)`粘贴原地,接着全编译,没有问题。 到这一步你可以把工程保存好,在RT-Thread studio中基于STM32G431系类的驱动5.1.0的初始工程创建完成,以后再用就直接复制工程就行,不用重复创建了。这一点也希望官方优化,不需要我们这么麻烦。  # 二、ADC工程创建 打开cubemx/src/adc.h。复制函数`void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)`和`void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)`到drivers/board.c的末尾,把全局变量HAL_RCC_ADC12_CLK_ENABLED删除,并删除该全局变量的if判断,也就是这样。 ```c void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; if(adcHandle->Instance==ADC1) { /** Initializes the peripherals clocks */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12; PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* ADC1 clock enable */ __HAL_RCC_ADC12_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**ADC1 GPIO Configuration PB12 ------> ADC1_IN11 */ GPIO_InitStruct.Pin = GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } else if(adcHandle->Instance==ADC2) { /** Initializes the peripherals clocks */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12; PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* ADC2 clock enable */ __HAL_RCC_ADC12_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**ADC2 GPIO Configuration PB15 ------> ADC2_IN15 */ GPIO_InitStruct.Pin = GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } } void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) { if(adcHandle->Instance==ADC1) { __HAL_RCC_ADC12_CLK_DISABLE(); /**ADC1 GPIO Configuration PB12 ------> ADC1_IN11 */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_12); } else if(adcHandle->Instance==ADC2) { __HAL_RCC_ADC12_CLK_DISABLE(); /**ADC2 GPIO Configuration PB15 ------> ADC2_IN15 */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_15); } } ```  这两个函数不需要在board.h中去声明,有其他.h已经声明好了,所以这里复制过来就可以用。 打开board.h需要的ADC的宏,不需要再去stm32f4xx_hal_config.h中注释宏#define HAL_ADC_MODULE_ENABLED了,细心的同学可以发现了,drivers中更名为了stm32f4xx_hal_config_bak.h。而stm32f4xx_hal_config.h在cubemx/inc中了,已经在创建时开启宏HAL_ADC_MODULE_ENABLED了。  接着在RT-Thread Settings中打开ADC驱动 注意开启ulog日志,进入到里面开启使能浮点数支持,这将会使我们rt_kprintf能够输出浮点数。 (上面两步参考图片见上一篇) 全编译,会发现有如下报错  原因是没有声明函数`__HAL_ADC_ENABLE0`,这并不是头文件没有添加而是根本没有这个函数,经过我和前面工程的对比,发现所有使能ADC的函数换成了`ADC_Enable`,但是这里使用`ADC_Enable`的#if判断没有我们的型号,那就需要自己添加,如下(这也是bug,希望官方看到能修复)  还剩下两个报错原因是参数类型不一致,这和修改5.1.0报错一样  修改结构体和函数都行,他们保持一致就好,我的修改如下,我修改的是函数形参。 ```c struct rt_adc_ops { rt_err_t (*enabled)(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled); rt_err_t (*convert)(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value); rt_uint8_t (*get_resolution)(struct rt_adc_device *device); rt_int16_t (*get_vref) (struct rt_adc_device *device); }; static rt_err_t stm32_get_adc_value(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value) static rt_err_t stm32_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled) ``` 到此全编译程序没有错误,到这里ADC工程创建完成了 # 三、ADC功能实现 这里我要讲点网上没有的(起码此刻孤陋寡闻的我没找到) 点击**drivers/include/confing/adc_confing.h**这里有我们使用的ADC的初始化参数,rtthread studio并没有ADC参数控制函数,想要修改,只能在这改,希望官方更新一下,可以像uart外设这样,建一个ADC参数结构体,里面包含了所有参数设计,再利用`rt_device_control`函数写进去,这样才符合常理,不能老是去驱动文件里改啊,很难找的。 这里说笑了,对于国产软件生态,还需要我们大家共同努力完善。所以我愿意把我的发现免费分享出来。 打开**cubemx/src/adc.c**对照里面的参数设置,对adc_confing.h进行更改(这也是我前面说把参数配置完全,后面好参照) 我的设置如下,对比**cubemx/src/adc.c**这里有些参数设置不全,可以自行添加,也可以默认,这些缺失参数不重要,添加注意需要最后的 **\**,不然会报错。  打开**drivers/drv_adc.c**找到函数`stm32_get_adc_value`这里面有如下设置语句 ```c ADC_ChanConf.Channel = stm32_adc_get_channel(channel); ADC_ChanConf.Rank = 1; ``` 对比**cubemx/src/adc.c**你会发现通道参数设置缺失严重,这也就是为什么程序不报错但功能实现不了的原因(再次呼吁官方完善) 我的修改如下 ```c ADC_ChanConf.Channel = stm32_adc_get_channel(channel);//不变 #if defined(SOC_SERIES_STM32G4) ADC_ChanConf.Rank = ADC_REGULAR_RANK_1; #else ADC_ChanConf.Rank = 1; #endif #elif defined(SOC_SERIES_STM32G4) ADC_ChanConf.SamplingTime = ADC_SAMPLETIME_24CYCLES_5; #ifdef SOC_SERIES_STM32G4 ADC_ChanConf.OffsetNumber = ADC_OFFSET_NONE; ADC_ChanConf.SingleDiff = LL_ADC_SINGLE_ENDED; ADC_ChanConf.Offset = 0; #endif ``` 图片也附上  对于`ADC_ChanConf.Rank`我们配置cubemx时选的是1,也就是只有一个通道,但是不同芯片下的赋值不一样,有的直接是1,有的是ADC_REGULAR_RANK_1宏,这个宏的值为6。开始我没注意这部分,导致程序不报错,各种初始化也成功,但就是采集值永远是0,折磨我很久,问题就在这。 官方的条件编译指令的判断缺失了很多,我使用的芯片就没有,这里我也用条件编译指令加上,这样不会影响其他程序。 设置完成就有`HAL_ADC_ConfigChannel(stm32_adc_handler, &ADC_ChanConf);`到这ADC的初始化才结束。编译程序没有错误。 把board.c的#include
粘贴到board.h(不然很多引用board.h的文件不含drv_common.h,导致报错) APP文件夹里是我自定义的文件夹,其他函数不用管,本工程只用到ADC.c和ADC.h。注意创建文件夹要把头文件目录添加进构建啊。如何添加见本系列第一讲 ## 1.ADC.c 这里面包含adc初始化和线程初始化,代码逻辑我就不讲了,我的代码风格应该挺正规的,具体编写流程去看官方文档或其他人文章 和上一章的内容比只改变了通道号,其余没有变化,这也是操作系统的良好移植性。 ```c #include "ADC.h" #define ADC1_NAME "adc1" #define ADC2_NAME "adc2" #define REFER_VOLTAGE 3.3 #define CONVERT_BITS (1<<10) static void adc_thread_entry(void *parameter); rt_adc_device_t adc1_handle,adc2_handle; int adc_init(void) { rt_err_t adc1_flag,adc2_flag; adc1_handle = (rt_adc_device_t)rt_device_find(ADC1_NAME); adc2_handle = (rt_adc_device_t)rt_device_find(ADC2_NAME); if((adc1_handle == RT_NULL) || (adc2_handle == RT_NULL)){ rt_kprintf("failed to adc handle fine\n"); return -1; } adc1_flag = rt_adc_enable(adc1_handle, 11); adc2_flag = rt_adc_enable(adc2_handle, 15); if((adc1_flag != RT_EOK) || (adc2_flag != RT_EOK)){ rt_kprintf("failed to adc enable\n"); return -1; } rt_kprintf("adc1 and adc2 init success\n"); return 0; } int adc_thread_init(void) { rt_thread_t adc_thread; adc_thread = rt_thread_create("adc_thread", adc_thread_entry, RT_NULL, 1024, 9, 100); if(adc_thread == RT_NULL){ rt_kprintf("failed to adc thread create"); return -1; } if(rt_thread_startup(adc_thread) != RT_EOK){ rt_kprintf("failed to adc startup\n"); return -1; } return 0; } static void adc_thread_entry(void *parameter) { float adc1_V_old = 0,adc2_V_old = 0; float adc1_V_new,adc2_V_new; while(1) { adc1_V_new = (float)rt_adc_read(adc1_handle, 11)*REFER_VOLTAGE/CONVERT_BITS; adc2_V_new = (float)rt_adc_read(adc2_handle, 15)*REFER_VOLTAGE/CONVERT_BITS; if( ((int)(adc1_V_old *100) != (int)(adc1_V_new *100)) || ((int)(adc2_V_old *100) != (int)(adc2_V_new *100)) ){ rt_kprintf("get voltage for adc1 and adc2 is: %.2f and %.2f\n",adc1_V_new, adc2_V_new); adc1_V_old = adc1_V_new; adc2_V_old = adc2_V_new; } rt_thread_mdelay(100); } } ``` ## 2.ADC.h ```c #ifndef APP_ADC_H_ #define APP_ADC_H_ #include
#include
int adc_init(void); int adc_thread_init(void); #endif /* APP_ADC_H_ */ ``` ## 3.mian.c ```c #include
#define DBG_TAG "main" #define DBG_LVL DBG_LOG #include
#include "ADC.h" int main(void) { adc_init(); adc_thread_init(); while (1) { rt_thread_mdelay(1000); } return RT_EOK; } ``` 编译0错误0警告,到此工程结束。 # 四、效果展示和工程分享  这个时候效果和上一章一样,有点误差,这个时候我们加入校准函数 打开**drivers/drv_adc.c**在`HAL_ADC_Start`前加入校准函数`HAL_ADCEx_Calibration_Start(stm32_adc_handler,ADC_SINGLE_ENDED);`  重新编译和下载  发现误差基本消除了(最高有3.29V,接近理论3.3V,前面最高只能到3.25V) 工程上传百度网盘,包括IO_ADC和初始工程文件,免费下载。 通过网盘分享的文件:IO_ADC.zip 链接: https://pan.baidu.com/s/1wtQsLlgUVFpLt24pOOtUAA?pwd=br58 提取码: br58 通过网盘分享的文件:RT_driver_5.1.0_STM32G431RBTx.zip 链接: https://pan.baidu.com/s/1XsCLVMCYPWlEIj5bPXOCQg?pwd=tay6 提取码: tay6 --- # 总结 创建工程有点繁琐,如果有某些地方不会操作报错了,请下载工程,这些工程我是验证过的,没有问题。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
Pai同学
这家伙很懒,什么也没写!
文章
4
回答
0
被采纳
0
关注TA
发私信
相关文章
1
请大神帮忙看下风格 还是哪里有问题 照着串口驱动写的ADC驱动
2
ADC config
3
rt_thread_delay()和ADC采样之间的冲突
4
请教在官方BSP中的STM32F40X程序中加入ADC,串口没输出。
5
给RT-Thread添加ADC驱动框架
6
求助:ADC采样被干扰
7
【内核和外设学习营】十里 ADC光敏电阻电压采集实验
8
<内核学习营>+坦然+探索者stm32f407板子的ADC测试光传感器实验
9
【内核学习营】+青春+ADC读取光敏传感器实验
10
《内核学习营》+水一方+项目中应用的ADC实现电压采集
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
cubemx
I2C_IIC
UART
ESP8266
WIZnet_W5500
BSP
ota在线升级
PWM
flash
packages_软件包
freemodbus
潘多拉开发板_Pandora
ADC
GD32
定时器
编译报错
flashDB
keil_MDK
socket
中断
rt_mq_消息队列_msg_queue
Debug
ulog
SFUD
msh
C++_cpp
at_device
本月问答贡献
出出啊
1524
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
818
个答案
179
次被采纳
crystal266
555
个答案
162
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
1
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
1
次点赞
crystal266
2
篇文章
1
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部