这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
adc实验_电源监控 [2019/12/21 10:59] zhangzheng |
adc实验_电源监控 [2022/03/22 10:18] (当前版本) sean |
||
---|---|---|---|
行 1: | 行 1: | ||
- | [[http://www.cnblogs.com/xiaomagee/p/7372419.html]] | + | | **银杏科技有限公司旗下技术文档发布平台** |||| |
+ | |技术支持电话|**0379-69926675-801**||| | ||
+ | |技术支持邮件|Gingko@vip.163.com||| | ||
+ | ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ||
+ | | V1.0 | 2020-07-04 | gingko | 初次建立 | | ||
+ | |||
+ | ===== 实验九:ADC实验——电源监控 ===== | ||
+ | |||
+ | ==== 一、 实验目的与意义 ==== | ||
+ | |||
+ | - 了解STM32 ADC结构。 | ||
+ | - 了解STM32 ADC特征。 | ||
+ | - 掌握ADC使用方法。 | ||
+ | - 掌握STM32 HAL库中ADC属性的配置方法。 | ||
+ | - 掌握KEIL MDK 集成开发环境使用方法。 | ||
+ | ==== 二、 实验设备及平台 ==== | ||
+ | |||
+ | - iCore4 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.15.5923532fsFrHiE&id=551864196684|点击购买]]。 | ||
+ | - JLINK(或相同功能)仿真器[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]。 | ||
+ | - Micro USB线缆。 | ||
+ | - Keil MDK 开发平台。 | ||
+ | - STM32CubeMX开发平台。 | ||
+ | - 装有WIN XP(及更高版本)系统的计算机。 | ||
+ | ==== 三、 实验原理 ==== | ||
+ | |||
+ | === 1、STM32F767 ADC简介 === | ||
+ | |||
+ | * STM32F767xx系列有3个ADC,这些ADC可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F767的ADC是12位逐次逼近型的模拟数字转换器。它有19个通道,可测量16个外部源、2个内部源和Vbat通道的信号。这些通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。 | ||
+ | * STM32F767IGT6包含有3个ADC。STM32F767的ADC最大的转换速率为2.4Mhz,也就是转换时间为0.41us(在ADCCLK=36M,采样周期为3个ADC时钟下得到),不要让ADC的时钟超过36M,否则将导致结果准确度下降。 | ||
+ | * STM32F767将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换,在注入通道被转换完成之后,规则通道才得以继续转换。 | ||
+ | === 2、ADC主要特性 === | ||
+ | |||
+ | * 可配置12位、10位、8位或6位分辨率 | ||
+ | * 在转换结束、注入转换结束以及发生模拟看门狗或溢出事件时产生中断 | ||
+ | * 单次和连续转换模式 | ||
+ | * 扫描模式,自动转化通道0到通道n数据 | ||
+ | * 数据对齐以保持内置数据一致性 | ||
+ | * 可独立设置各通道采样时间 | ||
+ | * 外部触发器选项,可为常规转换和注入转换配置极性 | ||
+ | * 不连续采样模式 | ||
+ | * 双重/三重模式(具有2个或更多ADC的器件提供) | ||
+ | * 双重/三重ADC模式下可配置的DMA数据存储 | ||
+ | * 双重/三重交替模式下可配置的转换间延迟 | ||
+ | * ADC转换类型(参见数据手册) | ||
+ | * ADC电源要求:供电在2.4V到3.6V下可全速运行,供电低至1.8V时为慢速运行 | ||
+ | * ADC输入范围:VREF–<VIN<VREF+ | ||
+ | * 常规通道转换期间可产生DMA请求 | ||
+ | === 3、ADC框图 === | ||
+ | {{ :icore4:icore4_arm_hal_9_1.png?direct |}} | ||
+ | === 4、ADC寄存器介绍 === | ||
+ | (1)ADC控制寄存器(ADC_CR1和ADC_CR2) | ||
+ | {{ :icore4:icore4_arm_hal_9_2.png?direct |}} | ||
+ | * ADC_CR1的SCAN位,该位用于设置扫描模式,由软件设置和清除,如果设置为1,则使用扫描模式,如果为0,则关闭扫描模式。在扫描模式下,由ADC_SQRx或ADC_JSQRx寄存器选中的通道被转换。如果设置了EOCIE或JEOCIE,只在最后一个通道转换完毕后才会产生EOC或JEOC中断。ADC_CR1[25:24]用于设置ADC的分辨率,详细的对应关系如图所示: | ||
+ | {{ :icore4:icore4_arm_hal_9_3.png?direct |}} | ||
+ | * ADC_CR2寄存器的各位描述如图所示: | ||
+ | {{ :icore4:icore4_arm_hal_9_4.png?direct |}} | ||
+ | * ADON位用于开关AD转换器,而CONT位用于设置是否进行连续转换,SWSTART 位用于开始规则通道的转换。 | ||
+ | (2)ADC通用控制寄存器(ADC_CCR) | ||
+ | {{ :icore4:icore4_arm_hal_9_5.png?direct |}} | ||
+ | * TSVREFE位是内部温度传感器和Vrefint通道使能位,这里我们直接设置为0。ADCPRE[1:0]用于设置ADC输入时钟分频,00~11分别对应2/4/6/8分频,MULTI[4:0]用于多重ADC模式选择。 | ||
+ | (3)ADC采样时间寄存器(ADC_SMPR1和ADC_SMPR2),这两个寄存器用于设置通道0~18的采样时间,每个通道占用3个位。 ADC_SMPR1的各位描述如图所示: | ||
+ | {{ :icore4:icore4_arm_hal_9_6.png?direct |}} | ||
+ | * ADC_SMPR2的各位描述如下图所示: | ||
+ | {{ :icore4:icore4_arm_hal_9_7.png?direct |}} | ||
+ | * 对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低ADC的转换速率。ADC的转换时间可以由以下公式计算: | ||
+ | * **Tcovn=采样时间+12个周期** | ||
+ | * 其中:Tcovn为总转换时间,采样时间是根据每个通道的SMP位的设置来决定的。例如,当ADCCLK=27Mhz的时候,并设置3个周期的采样时间,则得到:Tcovn=3+12=15个周期=0.55us。 | ||
+ | (4)ADC状态寄存器(ADC_SR),该寄存器保存了ADC转换时的各种状态。该寄存器的各位描述如图所示: | ||
+ | {{ :icore4:icore4_arm_hal_9_8.png?direct |}} | ||
+ | * 这里仅介绍将要用到的是EOC位,通过判断该位来决定是否此次规则通道的AD转换已经完成,如果该位位1,则表示转换完成了,就可以从ADC_DR中读取转换结果,否则等待转换完成。 | ||
+ | * 本实验中iCore4的所有电源经过电阻分压或者直接接入STM32的ADC的输出通道内,输入电流经过高端电流检测芯片ZXCT1009F输入到ADC的输入通道内,从而实现电源监控功能。 | ||
+ | ==== 四、 实验程序 ==== | ||
+ | |||
+ | === 1、 主函数 === | ||
+ | <code c> | ||
+ | int main(void) | ||
+ | { | ||
+ | int i; | ||
+ | /* MCU 配置*/ | ||
+ | /* 重置所有外围设备,初始化Flash接口和Systick */ | ||
+ | HAL_Init(); | ||
+ | /* 配置系统时钟 */ | ||
+ | SystemClock_Config(); | ||
+ | /* 初始化所有已配置的外围就设备 */ | ||
+ | MX_GPIO_Init(); | ||
+ | MX_ADC3_Init(); | ||
+ | MX_USART6_UART_Init(); | ||
+ | |||
+ | usart6.initialize(115200); | ||
+ | LED_GREEN_ON; | ||
+ | usart6.printf("\x0c"); //清屏 | ||
+ | usart6.printf("\033[1;32;40m"); //设置终端字体为绿色 | ||
+ | usart6.printf(" Hello, I am iCore4!\r\n"); | ||
+ | /* 无限循环 */ | ||
+ | while (1) | ||
+ | { | ||
+ | LED_GREEN_ON; | ||
+ | HAL_Delay(500); | ||
+ | LED_GREEN_OFF; | ||
+ | //读取ADC | ||
+ | for(i = 0;i < 5;i ++){ | ||
+ | my_adc.read(i); | ||
+ | } | ||
+ | //打印系统供电电压 | ||
+ | usart6.printf(" "); | ||
+ | usart6.printf("[I] %3.0fmA , ",my_adc.value[0] / 2* 1000.); | ||
+ | usart6.printf("[V] %4.2fV, ",my_adc.value[1] * 6.); | ||
+ | usart6.printf("[3.3V] %4.2fV, ",my_adc.value[2] * 2.); | ||
+ | usart6.printf("[2.5V] %4.2fV,",my_adc.value[3] * 2.); | ||
+ | usart6.printf("[1.2V] %4.2fV\r",my_adc.value[4]); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | === 2、 初始化ADC === | ||
+ | * 在HAL库中,初始化ADC是通过函数HAL_ADC_Init来实现的,该函数声明为: | ||
+ | <code c> | ||
+ | typedef struct | ||
+ | { | ||
+ | ADC_TypeDef *Instance; //ADC1/ADC2/ADC3 | ||
+ | ADC_InitTypeDef Init; //初始化结构体变量 | ||
+ | __IO uint32_t NbrOfCurrentConversionRank;//当前转换序列 | ||
+ | DMA_HandleTypeDef *DMA_Handle; //DMA方式使用 | ||
+ | HAL_LockTypeDef Lock; //ADC锁定对象 | ||
+ | __IO uint32_t State; //ADC通讯状态 | ||
+ | __IO uint32_t ErrorCode; //ADC错误代码 | ||
+ | }ADC_HandleTypeDef; | ||
+ | </code> | ||
+ | * 该结构体定义和其他外设比较类似,我们着重看第二个成员变量Init含义,它是结构体ADC_InitTypeDef 类型,结构体ADC_InitTypeDef 定义为: | ||
+ | <code c> | ||
+ | typedef struct | ||
+ | { | ||
+ | uint32_t ClockPrescaler; //时钟分频系数2/4/6/8分频 | ||
+ | uint32_t Resolution; //分辨率12/10/8/6 位 | ||
+ | uint32_t DataAlign; //对齐方式,左对齐还是右对齐 | ||
+ | uint32_t ScanConvMode; //扫描模式 | ||
+ | uint32_t EOCSelection; //EOC标志是否设置 | ||
+ | uint32_t ContinuousConvMode; //开启连续转换模式或者单次转换模式 | ||
+ | uint32_t NbrOfConversion; //规则序列中有多少个转换 | ||
+ | uint32_t DiscontinuousConvMode; //不连续采样模式 | ||
+ | uint32_t NbrOfDiscConversion; //不连续采样通道数 | ||
+ | uint32_t ExternalTrigConv; //外部触发方式 | ||
+ | uint32_t ExternalTrigConvEdge; //外部触发边沿 | ||
+ | uint32_t DMAContinuousRequests; //开启 DMA 请求连续模式或者单独模式 | ||
+ | }ADC_InitTypeDef; | ||
+ | </code> | ||
+ | === 3、 ADC3初始化函数 === | ||
+ | <code c> | ||
+ | void MX_ADC3_Init(void) | ||
+ | { | ||
+ | ADC_ChannelConfTypeDef sConfig; | ||
+ | /**配置ADC的全局功能(时钟,分辨率,数据对齐和转换次数)*/ | ||
+ | hadc3.Instance = ADC3; | ||
+ | hadc3.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;//4分频 | ||
+ | hadc3.Init.Resolution = ADC_RESOLUTION_12B;// 12 位模式 | ||
+ | hadc3.Init.ScanConvMode = DISABLE; //非扫描模式 | ||
+ | hadc3.Init.ContinuousConvMode = DISABLE; //关闭连续转换 | ||
+ | hadc3.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式 | ||
+ | hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; | ||
+ | //使用软件触发 | ||
+ | hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发 | ||
+ | hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐 | ||
+ | hadc3.Init.NbrOfConversion = 1; //1 个转换在规则序列中 | ||
+ | hadc3.Init.DMAContinuousRequests = DISABLE; //关闭DMA请求 | ||
+ | hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV; //EOC中断 | ||
+ | if (HAL_ADC_Init(&hadc3) != HAL_OK) //初始化ADC3 | ||
+ | { | ||
+ | _Error_Handler(__FILE__, __LINE__); | ||
+ | } | ||
+ | /*为选定的ADC常规通道配置其在定序器中的相应等级及其采样时间 */ | ||
+ | sConfig.Channel = ADC_CHANNEL_7; //通道 | ||
+ | sConfig.Rank = 1; //第1个序列,序列1 | ||
+ | sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES; //采样时间 | ||
+ | if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK) //通道配置 | ||
+ | { | ||
+ | _Error_Handler(__FILE__, __LINE__); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | * 此部分代码中MX_ADC3_Init函数调用函数HAL_ADC_Init初始化 ADC13相关参数。 | ||
+ | === 4、 读取通道值 === | ||
+ | <code c> | ||
+ | int read_adc(int channel) | ||
+ | { | ||
+ | int i,k; | ||
+ | unsigned long int temp[20] = {0}; | ||
+ | unsigned long int value; | ||
+ | unsigned short int data[100]; | ||
+ | ADC_ChannelConfTypeDef channel_config; | ||
+ | unsigned char channel_remap[5] = {ADC_CHANNEL_7,ADC_CHANNEL_12,ADC_CHANNEL_4,ADC_CHANNEL_5,ADC_CHANNEL_6}; | ||
+ | |||
+ | channel_config.Channel = channel_remap[channel]; | ||
+ | channel_config.Offset = 0; | ||
+ | channel_config.Rank = 1; | ||
+ | channel_config.SamplingTime = ADC_SAMPLETIME_480CYCLES; | ||
+ | |||
+ | for(k = 0;k < 20;k++){ | ||
+ | for(i = 0;i < 100;i ++){ | ||
+ | HAL_ADC_ConfigChannel(&hadc3,&channel_config); | ||
+ | HAL_ADC_Start(&hadc3); | ||
+ | while(!__HAL_ADC_GET_FLAG(&hadc3,ADC_FLAG_EOC)); | ||
+ | data[i] = HAL_ADC_GetValue(&hadc3); | ||
+ | } | ||
+ | sort(data,100); | ||
+ | for(i = 40;i < 60;i++){ | ||
+ | temp[k] += data[i]; | ||
+ | } | ||
+ | temp[k] = temp[k] / 20; | ||
+ | } | ||
+ | value = 0; | ||
+ | for(k = 0;k < 20;k++){//获取平均值,用来提高准确度 | ||
+ | value += temp[k]; | ||
+ | } | ||
+ | value /= 20; | ||
+ | my_adc.value[channel] = value * ADC_REF / 4096; | ||
+ | return value; | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | ==== 五、 实验步骤 ==== | ||
+ | - 把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连); | ||
+ | - 把iCore4通过Micro USB线与计算机相连,为iCore4供电; | ||
+ | - 打开Keil MDK 开发环境,并打开本实验工程; | ||
+ | - 烧写程序到iCore4上; | ||
+ | - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 | ||
+ | ==== 六、 实验现象 ==== | ||
+ | |||
+ | * 串口一直向终端输出输入电源的数据。 | ||
+ | {{ :icore4:icore4_arm_hal_9_9.png?direct |}} |