ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
>[success] **技术支持说明:** >**1**.一般以自主学习为主 > **2**.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/nb-iot) > **3**.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> ## **ADC理论基础** * **模拟信号**:模拟信号是指用连续变化的物理量所表达的信息,特点是信号值的变化是连续的,如下图所示。 ![](https://img.kancloud.cn/a7/64/a76403f5bbbe0fc46e82220c61b69ada_900x572.png =300x) ### * **数字信号**:数字信号是指用一系列断续变化的脉冲数字量所表达的信息,特点是信号值会特然变化,如下图所示。 ![](https://img.kancloud.cn/5d/3d/5d3d4ee4d36e4ee98f1399bae4fcd866_570x362.png =300x) ### * **ADC**:即模数转换,指模拟信号转换为数字信号的过程。举个例子,例如下图中的黑色线为一个模拟信号,可以通过特定的规则或者算法将其转换为阶梯状的数字信号。 ![](https://img.kancloud.cn/c1/c1/c1c1aeb3b42ae9bb643bcafa93911372_562x326.png =400x) <br/> &emsp;&emsp;本节课将会以STM32F030的PA0这个IO口作为模拟信号的输入口,然后把这个模拟信号转换为数字信号。 <br/> ## **ADC 的 HCL API 设计** ADC的HAL API比较简单,只需要初始化和读取数据的API即可,如图所示。 ![](https://img.kancloud.cn/95/b3/95b367856bdb569f8d75c49afea3017f_364x180.png) <br/> ## **编写代码** 笔者在本节课配套的源代码中新建了 hal\_adc.h 和 hal\_adc.c文件,如图所示。 ![](https://img.kancloud.cn/3d/5b/3d5b575a52e50d56979ba747c510783d_266x592.png) ### 打开本节课配套的工程,笔者把hal\_adc.c以及必要的标准库文件添加进工程了,如图所示。 ![](https://img.kancloud.cn/4c/18/4c18cd4d0d5a30526d231f23384abe46_375x661.png =300x) <br/> hal_adc.h文件的代码如下: ``` #ifndef __HAL_ADC_H__ #define __HAL_ADC_H__ /* *初始化ADC */ void halAdcInit(void); /* *读取ADC值 */ unsigned int halAdcRead(void); #endif /* __HAL_ADC_H__ */ ``` <br/> hal_adc.c文件代码如下: ``` #include "hal_adc.h" #include "stm32f0xx_adc.h" /* *初始化ADC */ void halAdcInit() { GPIO_InitTypeDef gpioCfg;//定义GPIO配置 ADC_InitTypeDef adcCfg;//定义ADC配置 /* 配置PA0 */ gpioCfg.GPIO_Pin = GPIO_Pin_0;//指定引脚为Pin 0 gpioCfg.GPIO_Mode = GPIO_Mode_AN; gpioCfg.GPIO_PuPd = GPIO_PuPd_NOPULL; /* 配置ADC */ adcCfg.ADC_Resolution = ADC_Resolution_12b;//ADC值的分辨率为12位 adcCfg.ADC_ContinuousConvMode = DISABLE;//不要进行持续的转换。在halAdcRead函数每次读取ADC值时,会重新启动转换过程 adcCfg.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; adcCfg.ADC_DataAlign = ADC_DataAlign_Right; adcCfg.ADC_ScanDirection = ADC_ScanDirection_Backward; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4); ADC_DeInit(ADC1); GPIO_Init(GPIOA, &gpioCfg);//初始化PA0 ADC_Init(ADC1, &adcCfg);//初始化ADC1 /* Convert the ADC1 Vref with 55.5 Cycles as sampling time */ ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_55_5Cycles); ADC_GetCalibrationFactor(ADC1); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); ADC_StartOfConversion(ADC1); } /* *读取ADC值 * *@return ADC值 */ unsigned int halAdcRead() { ADC_StartOfConversion(ADC1); while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET); return ADC_GetConversionValue(ADC1); } ``` <br/> ## **使用ADC HAL API** 编写好ADC HAL API后,ADC的使用非常简单。在配套工程的main.c文件中添加如下代码: ### ``` /* * 通过串口接收到信息时的回调函数 * @param byte - 接收到的数据 */ static void onUartIRQ(unsigned char byte) {} int main(void) { halSystemInit();//系统初始化 halUartInit(115200);//串口通信初始化,并设置波特率为115200 halUartSetIRQCallback(onUartIRQ);//注册串口通信回调函数,当通过串口接收到信息时自动调用此函数 halAdcInit();//ADC初始化 /* 每隔1秒读取一次ADC值并通过串口发送出去 */ while (1) { halSystemDelayUs(1000 * 1000);//延时1000*1000微秒,即1秒 char debug[32]; sprintf(debug, "ADC=%d\n", halAdcRead());//读取ADC值,并格式化保存到debug变量中 halUartWrite((uint8_t *)debug, strlen(debug));//把debug通过串口发送出去 } } ``` ### 上述代码使用了格式化函数sprintf和字符串长度计算函数strlen,这两个函数在以下头文件中: ### ``` #include <stdio.h> #include <string.h> ``` <br/> **代码测试** 1.编译链接工程代码,把生成的Hex文件烧录到开发板中。 ### 2.把开发板的拨码开关的第1~4位打到右边,第5、6位打到左边,如图所示。 ![](https://img.kancloud.cn/58/9a/589a66f16f8ca111d36b54fac452805c_1076x1045.png =250x) 接着,使用配套的Micro USB线连接开发板到电脑上。 ### 3.然后打开串口调试助手,并按照《串口通信实验》章节的说明做好相关配置。 ### 4.读者可以看到由PA0这个IO口输入的模拟信号经过ADC后的值,如图所示。 ![](https://img.kancloud.cn/1f/a2/1fa24b52c1ab54f10e08701cd18d797b_725x723.png =500x) <br/> **进一步解释** 在《GPIO实验:按键》章节曾经讲解过,STM32F030的PA0是与一个名为“User”的按键连接的,如图所示。 ![](https://img.kancloud.cn/d6/5b/d65b0eade5a60e99322ca3538044ccf8_1786x988.png =500x) ### * 按键没有被按下时,PA0为浮空输入,即PA0的输入电平是不确定的。 * 按键被按下后,PA0连接3v3的电源,因此PA0的输入电平是3v3。 也就是说,PA0将会输入0~3.3v的模拟信号。main函数中的halAdcRead()函数作用是读取当前的模拟信号值,然后把这个值映射到[0,4095]这个范围中,例如: * 如果读取到的模拟信号值为3.3v左右,那么halAdcRead()函数的返回值为4095左右; * 如果读取到的模拟信号值为0v左右,那么halAdcRead()函数的返回值为0左右; * 如果读取到的模拟信号值为1.65v左右,那么halAdcRead()函数的返回值为2047左右。 ### [0,4095]这个取值范围是由ADC初始化函数halAdcInit()中的以下配置决定的: ``` adcCfg.ADC_Resolution = ADC_Resolution_12b;//ADC值的分辨率为12位 ``` 读者也可以把分辨率设置为8位,此时的取值范围即为[0,255],对应的配置代码如下: ``` adcCfg.ADC_Resolution = ADC_Resolution_8b;//ADC值的分辨率为8位 ``` <br/> **进一步扩展** 在ADC初始化函数halAdcInit()中,笔者使用了PA0作为模拟信号的输入接口,读者也可以更改其中的配置来使用其它GPIO作为输入接口。 <br/> <br/> ## **商务合作** 如有以下需求,可扫码添加管理员好友,注明“**商务合作**” * 项目定制开发,技术范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等; * 入驻平台,成为讲师; * 接项目赚外快; * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](https://img.kancloud.cn/b7/5a/b75ac8c3da945f20568771f5935c388f_430x430.png =150x) (非商务合作**勿扰**,此处**非**技术支持)