【CS32L015C8T6】ADC驱动配置方法(内附完整代码及注释)
本文介绍了芯海CS32L015C8T6单片机的ADC配置方法。该MCU采用12位精度ADC,具有17个外部和5个内部输入通道,但缺乏DMA功能。文章详细阐述了配置流程:1)设置GPIO模拟输入功能;2)配置ADC参数(包括自动累加功能);3)实现中断处理。重点讲解了ADC单通道中断回调函数的实现方法,并提供了完整代码示例。作者分享了实际开发中的经验,指出官方驱动代码的不足,帮助开发者避免重复造轮子
目录
四、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~
前言:
长颈鹿最近在使用芯海的CS32L015C8T6单片机进行工程开发,发现网上有关这款单片机的资料非常少,而且官方提供的驱动代码有些不适用,因此决定记录一下造轮子的过程,前车之鉴,后车之师,希望能够帮助后来的开发者减少一些重复性造轮子的工作,少走一些弯路。
一、CS32L015系列单片机ADC资源简介:
这款MCU只有一个ADC,以下是对其资源的介绍和点评:
1、12位精度ADC,分辨率4096,能够适用一些简单场景的ADC功能开发;
2、有17个外部输入通道和5个内部输入通道;
3、没有DMA功能,硬件资源是比较紧凑的,这款MCU使用Cortex-M0+内核,主频最高只有24MHz,属于慢速单片机,如果需要使用ADC或者串口接收大量数据,还是建议选择具有DMA功能的单片机,不然要消耗大量的CPU计算资源,系统运行速度会很慢;
4、CS32L015C8T6胜在价格实惠;
二、ADC配置流程:
CS32L015的ADC配置思路和大部分单片机是一样的,主要区别就在于官方提供的HAL库中的函数实现或者参数不同。以下是配置的流程:
- 配置GPIO模拟输入功能;
- 配置ADC参数,将ADC特定通道映射到GPIO的端口;
- 设置ADC中断优先级;
- 开启ADC中断,链接中断回调函数;
- 启动ADC中断模式;
三、ADC配置完整代码及注释:
1、配置GPIO口模拟输入功能:
芯海的片子在将GPIO口配置为ADC模拟输入的功能时,官方例程中把GPIO中与ADC无关的功能也都配置了一遍,例如GPIO硬件按键消抖、负载驱动能力等参数,这些结构体参数在硬件层面都映射到了具体的寄存器,这是芯海的一个特点, 区别于ST单片机。
例如当GPIO口需要配置为输入模式时,不管是模拟信号输入还是普通的输入模式, GPIOx.OpenDrain 这个参数一定要配置成 GPIO_OPENDRAIN(输出模式一定要配置成开漏输出,不能是推挽),即使输入模式和输出选项并不相关。芯海的单片机也一定要这样配置,因为牵涉到底层寄存器的硬件映射,不能觉得是无关项就爱谁谁。
/*****************************************************************************
[函数名称]MX_ADC_MspInit
[函数功能]ADC底层GPIO初始化
[参 数]
[备 注]
*****************************************************************************/
void MX_ADC_MspInit()
{
GPIO_InitTypeDef GPIOx = {0};
GPIOx.Mode = GPIO_MODE_ANALOG; //工作模式:模拟输入
GPIOx.OpenDrain = GPIO_OPENDRAIN; //输出模式:开漏输出
GPIOx.SlewRate = GPIO_SLEW_RATE_HIGH; //端口速度:高速
GPIOx.DrvStrength = GPIO_DRV_STRENGTH_HIGH; //驱动负载能力:强,无关项
GPIOx.Pull = GPIO_NOPULL; //上下拉电阻:无
GPIOx.Debounce.Enable = GPIO_DEBOUNCE_DISABLE; //按键抖动过滤:关闭,无关项
GPIOx.Pin = AD_BAT_Pin; //GPIO端口选择:AD_BAT_Pin
HAL_GPIO_Init( AD_BAT_GPIO_Port, &GPIOx ); //GPIO初始化
}
2、配置ADC相关参数:
CS32L015在配置ADC时,有一些参数和STM32是有明显区别的,例如这个“AutoAccumulation”参数,控制ADC采集数据自动累加的功能,如果设置了多个通道,开启这个功能后,按照用户手册的意思,ADC会自动将所有通道采集的数据累加到一个结果中,最终输出的结果需要手动除以 “NbrOfConversion”参数中配置的 通道数和采样次数 + 1,这个除数数值需要额外+1,这是长颈鹿通过不断测试得出的结论,至于为什么,芯海的官方文档中没有写,例程代码中甚至也是错误的用法,除以除数(N+1)才能得到一个正确的均值滤波后的值,我认为这个功能是为了弥补这款MCU没有DMA的缺点,设计出的一个“折中”方案,可减少中断触发次数,节省MCU本就不富裕的算力资源,
本例程开启一个ADC通道,使用了ADC自动累加“AutoAccumulation”功能。
/*****************************************************************************
[函数名称]MX_ADC_Init
[函数功能] ADC 初始化
[参 数]
[备 注]
*****************************************************************************/
ADC_HandleTypeDef hadc = {0}; //全局变量
void MX_ADC_Init()
{
MX_ADC_MspInit(); //ADC底层GPIO初始化
__HAL_RCC_ADC_CLK_ENABLE(); //ADC总线时钟:开启
hadc.Instance = ADC; //ADC实例选择:ADC(只有一个)
hadc.Init.SamplingTime = HAL_ADC_SAMPLE_8CYCLE; //采样时间:8周期
hadc.Init.ClkSel = HAL_ADC_CLOCK_PCLK_DIV4; //时钟分频:4分频
hadc.Init.ConvMode = HAL_ADC_MODE_CONTINUE; //转换模式:扫描模式
hadc.Init.ContinueChannelSel = HAL_ADC_CHANNEL_2; //扫描通道设置:2
hadc.Init.NbrOfConversion = 10-1; //转换次数:10次
hadc.Init.AutoAccumulation = HAL_ADC_AUTOACC_ENABLE; //自动累加:开启
hadc.Init.CircleMode = HAL_ADC_MULTICHANNEL_NONCIRCLE; //循环模式:单通道循环
hadc.Init.Vref = HAL_ADC_VREF_VCAP; //参考电压:VREF_VCAP,2.5V
hadc.Init.ExtTrigConv0 = HAL_ADC_EXTTRIG_SW_START; //中断触发:软件信号
HAL_ADC_Init(&hadc); //初始化ADC
//HAL_NVIC_SetPriority( ADC_IRQn, 2 ); //ADC中断优先级:2(0~3)
HAL_NVIC_EnableIRQ( ADC_IRQn ); //ADC中断使能:开启
HAL_ADC_Start_IT(&hadc); //启动adc
}
3、链接重构ADC中断相关函数:
配置外设的中断功能,都有相对固定的流程,设置优先级、开中断、链接中断回调函数。如果项目是用C/C++混合开发,在重构中断回调函数时,需要加上 extern “C”{ } 来区分C和C++的函数名称空间,否则编译器会找不到中断回调函数的入口。
void ADC_IRQHandler(void) //链接 ADC 中断回调函数
{
extern ADC_HandleTypeDef hadc;
HAL_ADC_IRQHandler(&hadc); //ADC中断回调函数
}
我们首先需要链接ADC中断回调函数,如上所示,“ADC_IRQHandler”是系统最底层的ADC中断回调函数,在系统启动文件中断向量表中为其分配了固定的入口地址,只要触发ADC中断,就会自动进入这个函数,但是这个函数很基础,只是提供了一个虚拟的入口,没有具体的实现,区分回调种类的具体实现在“HAL_ADC_IRQHandler”函数中。
“HAL_ADC_IRQHandler”函数是HAL库封装好的ADC中断处理函数,在这个函数中,实现了单通道转换完成回调、全通道转换完成回调以及范围溢出回调函数等功能,功能更加丰富,开发者只需要根据应用场景调用对应功能的入口函数,就可以实现ADC的中断回调函数实现。
|
HAL_ADC_ContConvCpltCallback(); |
ADC全部通道转换完成回调函数 |
|
HAL_ADC_LevelOutOfRangeCallback(); |
ADC超阈值回调函数 |
|
HAL_ADC_ChannelxCallback(); |
ADC单通道转换完成回调函数 |
本例程中使用的是HAL_ADC_ContConvCpltCallback()功能回调函数,使用这个函数,可以在ADC通道累加完规定的所有数据之后,进入中断,在中断函数中采集的ADC值进行处理。
/*****************************************************************************
[函数名称]HAL_ADC_ChannelxCallback
[函数功能]ADC单通道转换完成 中断回调函数
[参 数]
[备 注]
*****************************************************************************/
extern "C" {
// 全部转换结束后回调,等待ADC值全部转换完成之后触发一个中断
void HAL_ADC_ContConvCpltCallback(ADC_HandleTypeDef *hADC)
{
HAL_ADC_Stop_IT(hADC);
vUpdateData(); //调用ADC处理函数
}
}
4、将ADC转换为实际电压值:
ADC对应的电压转换函数如下:
void vUpdateData( void )
{
uint16 m_au16ADAverage = 0;
const f32 c_f32Uref = 2500.0f; //参考电压对应2.5V,2500mV
m_au16ADAverage = HAL_ADC_GetAccValue(&hadc) / 10; //获取累加后的ADC值,并求平均
//12位精度ADC,对应分辨率位4096,f32Voltage即为ADC转换后的实际电压
f32 f32Voltage = m_au16ADAverage * c_f32Uref / 4096.0f;
m_bUpdateData = TRUE;
}
四、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~
更多推荐




所有评论(0)