| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-07-28 | gingko | 初次建立 | ===== STM32CubeMX教程十二——复用ADC实验 ===== 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR {{ :icore4tx:icore4tx_cube_12_1.png?direct |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各 种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 {{ :icore4tx:icore4tx_cube_12_2.png?direct |}} 3.配置RCC,使用外部时钟源 {{ :icore4tx:icore4tx_cube_12_3.png?direct |}} 4.时基源选择SysTick {{ :icore4tx:icore4tx_cube_12_4.png?direct |}} 5.将PA10,PI1,PI2,PI3设置为GPIO_Output {{ :icore4tx:icore4tx_cube_12_5.png?direct |}} 6.引脚模式配置 {{ :icore4tx:icore4tx_cube_12_6.png?direct |}} {{ :icore4tx:icore4tx_cube_12_7.png?direct |}} 7.设置串口 {{ :icore4tx:icore4tx_cube_12_8.png?direct |}} 8.在NVIC Settings一栏使能接收中断 {{ :icore4tx:icore4tx_cube_12_9.png?direct |}} 9.配置ADC {{ :icore4tx:icore4tx_cube_12_10.png?direct |}} {{ :icore4tx:icore4tx_cube_12_11.png?direct |}} 10.时钟源设置,选择外部高速时钟源,配置为最大主频 {{ :icore4tx:icore4tx_cube_12_12.png?direct |}} {{ :icore4tx:icore4tx_cube_12_13.png?direct |}} 11.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27 {{ :icore4tx:icore4tx_cube_12_14.png?direct |}} 12.点击Code Generator,进行进一步配置 {{ :icore4tx:icore4tx_cube_12_15.png?direct |}} * **Copy all used libraries into the project folder** * **将HAL库的所有.C和.H都复制到所建工程中** * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便 * 缺点:体积大,编译时间很长 * **Copy only the necessary library files** * **只复制所需要的.C和.H(推荐)** * 优点:体积相对小,编译时间短,并且工程可复制拷贝 * 缺点:新增外设时需要重新用STM32CubeMX导入 * **Add necessary library files as reference in the toolchain project configuration file** * **不复制文件,直接从软件包存放位置导入.C和.H** * 优点:体积小,比较节约硬盘空间 * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径 * 自行选择方式即可 13.然后点击GENERATE CODE 创建工程 {{ :icore4tx:icore4tx_cube_12_16.png?direct |}} 创建成功,打开工程。 \\ \\ ===== 实验十二:复用ADC实验——板载电源监控 ===== ==== 一、实验目的与意义 ==== - 了解STM32 ADC结构。 - 了解STM32 ADC特征。 - 掌握ADC的使用方法。 - 掌握74HC4051逻辑芯片的使用方法。 - 掌握STM32 HAL库中ADC属性的配置方法。 - 掌握KEILMDK 集成开发环境使用方法。 ==== 二、实验设备及平台 ==== - iCore4T 双核心板。[[https://item.taobao.com/item.htm?spm=a1z10.1-c.w137644-251734891.3.5923532fDrMDOe&id=610595120319|点击购买]] - JLINK(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]] - Micro USB线缆。 - Keil MDK 开发平台。 - STM32CubeMX开发平台。 - 装有WIN XP(及更高版本)系统的计算机。 ==== 三、实验原理 ==== === 1.ADC介绍 === * ADC是A/D转换部件,单片机不能直接处理模拟量,所以需要ADC将模拟量转换为数字量后,在进行处理。在使用单片机进行模拟数据处理的过程中,ADC至关重要。ADC以下几种类型: * **积分型**:积分型AD工作原理是将输入电压转换成时间(脉冲宽度信号)或频率(脉冲频率),然后由定时器/计数器获得数字值。其优点是用简单电路就能获得高分辨率,抗干扰能力强,但缺点是由于转换精度依赖于积分时间,因此转换速率极低。初期的单片AD转换器大多采用积分型,现在逐次比较型已逐步成为主流。 * **逐次比较型**:逐次比较型AD由一个比较器和DA转换器通过逐次比较逻辑构成,从MSB开始,顺序地对每一位将输入电压与内置DA转换器输出进行比较,经n次比较而输出数字值。其电路规模属于中等。其优点是速度较高、功耗低,在低分辨率(<12位)时价格便宜,但高精度(>12位)时价格很高。 * ** 并行比较型/串并行比较型**:并行比较型AD采用多个比较器,仅作一次比较而实行转换,又称FLash(快速)型。由于转换速率极高,n位的转换需要2n-1个比较器,因此电路规模也极大,价格也高,只适用于视频AD转换器等速度特别高的领域。串并行比较型AD结构上介于并行型和逐次比较型之间,最典型的是由2个n/2位的并行型AD转换器配合DA转换器组成,用两次比较实行转换,所以称为 Half flash(半快速)型。还有分成三步或多步实现AD转换的叫做分级(Multistep/Subrangling)型AD,而从转换时序角度又可称为流水线(Pipelined)型AD,现代的分级型AD中还加入了对多次转换结果作数字运算而修正特性等功能。这类AD速度比逐次比较型高,电路 规模比并行型小。 * **Σ-Δ(Sigma delta)调制型**:Σ-Δ型AD由积分器、比较器、1位DA转换器和数字滤波器等组成。原理上近似于积分型,将输入电压转换成时间(脉冲宽度)信号,用数字滤波器处理后得到数字值。电路的数字部分基本上容易单片化,因此容易做到高分辨率。主要用于音频和测量。 * **电容阵列逐次比较型**:电容阵列逐次比较型AD在内置DA转换器中采用电容矩阵方式,也可称为电荷再分配型。一般的电阻阵列DA转换器中多数电阻的值必须一致,在单芯片上生成高 精度的电阻并不容易。如果用电容阵列取代电阻阵列,可以用低廉成本制成高精度单片AD转换器。最近的逐次比较型AD转换器大多为电容阵列式的。 * **压频变换型**:压频变换型(Voltage-Frequency Converter)是通过间接转换方式实现模数转换的。其原理是首先将输入的模拟信号转换成频率,然 后用计数器将频率转换成数字量。从理论上讲这种AD的分辨率几乎可以无限增加,只要采样的时间能够满足输出频率分辨率要求的累积脉冲个数的宽度。其优点是分辨率高、功耗低、价格低,但是需要外部计数电路共同完成AD转换。 === 2.ADC主要参数介绍 === ADC主要参数有以下几点: * **分辨率**:数字量变化一个最小量时模拟量的变化量,定义为满刻度与2n的比值。分辨率又称精度,通常以数字信号的位数来表示。 * **转换速率**:完成一次A/D转换所需要时间的倒数,值越大表示转换得越快。积分型AD的转换时间是毫秒级属低速AD,逐次比 较型AD是微秒级属中速AD,全并行/串并行型AD可达到纳秒级。 * **量化误差**:由于AD的有限分辨率而引起的误差,即有限分辨率AD的阶梯状转移特性曲线与无限分辨率AD(理想AD)的转移特 性曲线(直线)之间的最大偏差。通常是1 个或半个最小数字量的模拟变化量,表示为1LSB、1/2LSB。 * **偏移误差**:输入信号为零时输出信号不为零的值,可外接电位器调至最小。 * **满刻度误差**:满度输出时对应的输入信号与理想输入信号值之差。 * **线性度**:实际转换器的转移函数与理想直线的最大偏移。 === 3.STM32H750 ADC介绍 === * STM32H750xx系列有3个ADC,都可以独立工作,其中ADC1和ADC2还可以组成双重模式(提高采样率)。STM32H750的ADC分辨率高达16位,每个ADC具有多达20个的采集通道,这些通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在32位数据寄存器中。 === 4.74HC4051介绍 === * 74HC4051是一款八选一模拟开关电路,内置3个地址选择端(A0~A2),低有效的使能输入端(E ̅),8路独立的输入/输出端(Y0~Y7)及公共输入/输出端(Z)。 * 电路内部有8个双向模拟开关,每个开关的一端连接到独立的输入/输出(Y0~Y7)端,另一端连接到公共的输入/输出(Z)端。 * 当E ̅为低电平时,通过A0~A2选择一个通路的开关处于低阻导通状态。当E ̅为高电平时,A0~A2设置无效,所有开关处于高阻关断状态。如果需要切换开关状态,就必须使用使能输入端(E ̅)。 * VDD和VSS是连接到数字控制输入端(A0~A2和E ̅)的电源电压。(VDD-VSS)的范围是3~9V。模拟输入输出(Y0~Y7和Z)能够在最高VDD,最低VEE之间变化。VDD-VEE不会超过9V。对于用做数字多路选择开关,VEE和VSS是连在一起的(通常接地)。 * 74HC4051主要应用于模拟多路选择开关、数字多路选择开关及信号选通。 逻辑图: {{ :icore4tx:icore4tx_arm_hal_12_1.png?direct&650 |}} 功能说明: {{ :icore4tx:icore4tx_arm_hal_12_2.png?direct&650 |}} 原理图: {{ :icore4tx:icore4tx_arm_hal_12_3.png?direct&500 |}} {{ :icore4tx:icore4tx_arm_hal_12_4.png?direct&550 |}} {{ :icore4tx:icore4tx_arm_hal_12_5.png?direct&650 |}} ==== 四、实验程序 ==== === 1.主函数 === int main(void) { HAL_Init(); SystemClock_Config(); i2c.initialize(); axp152.initialize(); axp152.set_dcdc1(3500);//[ARM & FPGA] axp152.set_dcdc2(1200);//[FPGA INT] axp152.set_dcdc3(3300);//[DCOUT3] axp152.set_dcdc4(3300);//[DCOUT4] axp152.set_aldo1(3300);//[BK3] axp152.set_aldo2(3300);//[ALDOOUT2] axp152.set_dldo1(3300);//[BK0] axp152.set_dldo2(3300);//[BK1] HAL_Delay(200); MX_GPIO_Init(); MX_USART2_UART_Init(); MX_ADC1_Init(); MX_ADC3_Init(); usart2.initialize(115200); while (1) { if(systick._500ms_flag == 1){ systick._500ms_flag = 0; LED_ON; my_adc.read(0); my_adc.read_mux(); usart2.printf("\x0c");//清屏 usart2.printf("\033[1;32;40m");//设置终端字体为绿色 usart2.printf("Hello,I am iCore4TX!\r\n\r\n"); //打印ADC的值 usart2.printf("[V ] %4.2fV\r\n",my_adc.value[0] * 6); usart2.printf("[I ] %3.0fmA\r\n",my_adc.value[5] / 2* 1000.); usart2.printf("[3.3V ] %4.2fV\r\n",my_adc.value[7] * 2); usart2.printf("[2.5V ] %4.2fV\r\n",my_adc.value[4] * 2); usart2.printf("[1.2V ] %4.2fV\r\n",my_adc.value[6]); usart2.printf("[BK3 ] %4.2fV\r\n",my_adc.value[2] * 2); usart2.printf("[BK4 ] %4.2fV\r\n",my_adc.value[1] * 2); usart2.printf("[BK5 ] %4.2fV\r\n",my_adc.value[3] * 2); usart2.printf("[BK7 ] %4.2fV\r\n",my_adc.value[8] * 2); LED_OFF; } } } === 2.74HC4051通道配置 === //定义74HC4051片选管脚 #define SEL_A_ON HAL_GPIO_WritePin(GPIOI, SEL_A_Pin, GPIO_PIN_SET) #define SEL_A_OFF HAL_GPIO_WritePin(GPIOI, SEL_A_Pin, GPIO_PIN_RESET) #define SEL_B_ON HAL_GPIO_WritePin(GPIOI, SEL_B_Pin, GPIO_PIN_SET) #define SEL_B_OFF HAL_GPIO_WritePin(GPIOI, SEL_B_Pin, GPIO_PIN_RESET) #define SEL_C_ON HAL_GPIO_WritePin(GPIOI, SEL_C_Pin, GPIO_PIN_SET) #define SEL_C_OFF HAL_GPIO_WritePin(GPIOI, SEL_C_Pin, GPIO_PIN_RESET) //选择测量BK4时片选脚状态 #define CHANNEL_0_ON SEL_C_OFF;\ SEL_B_OFF;\ SEL_A_OFF //选择测量BK3时片选脚状态 #define CHANNEL_1_ON SEL_C_OFF;\ SEL_B_OFF;\ SEL_A_ON //选择测量BK5时片选脚状态 #define CHANNEL_2_ON SEL_C_OFF;\ SEL_B_ON;\ SEL_A_OFF //选择测量2.5V时片选脚状态 #define CHANNEL_3_ON SEL_C_OFF;\ SEL_B_ON;\ SEL_A_ON //选择测量输入电流时片选脚状态 #define CHANNEL_4_ON SEL_C_ON;\ SEL_B_OFF;\ SEL_A_OFF //选择测量1.2V时片选脚状态 #define CHANNEL_5_ON SEL_C_ON;\ SEL_B_OFF;\ SEL_A_ON //选择测量3.3V时片选脚状态 #define CHANNEL_6_ON SEL_C_ON;\ SEL_B_ON;\ SEL_A_OFF //选择测量BK7时片选脚状态 #define CHANNEL_7_ON SEL_C_ON;\ SEL_B_ON;\ === 3.ADC初始化函数 === void MX_ADC1_Init(void) { ADC_MultiModeTypeDef multimode = {0}; ADC_ChannelConfTypeDef sConfig = {0}; hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV16; //16分频 hadc1.Init.Resolution = ADC_RESOLUTION_16B; //ADC转换分辨率16位 hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; //非扫描模式 hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; //关闭 EOC 中断 hadc1.Init.LowPowerAutoWait = DISABLE; //自动低功耗关闭 hadc1.Init.ContinuousConvMode = DISABLE; //关闭连续转换 hadc1.Init.NbrOfConversion = 1; //1个转换在规则序列中 hadc1.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发 hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //禁止触发检测 hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR; //存到DR寄存器 hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED; //溢出保留上次转换数据 hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE; //位数不左移 hadc1.Init.OversamplingMode = DISABLE; //关闭过采样 if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } multimode.Mode = ADC_MODE_INDEPENDENT; //独立模式 if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_16; //通道16 sConfig.Rank = ADC_REGULAR_RANK_1; //第 1 个序列 sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; //采样时间 sConfig.SingleDiff = ADC_SINGLE_ENDED; //单端输入 sConfig.OffsetNumber = ADC_OFFSET_NONE; //不选择偏移序号 sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } void MX_ADC3_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; hadc3.Instance = ADC3; hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV16; hadc3.Init.Resolution = ADC_RESOLUTION_16B; hadc3.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc3.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc3.Init.LowPowerAutoWait = DISABLE; hadc3.Init.ContinuousConvMode = DISABLE; hadc3.Init.NbrOfConversion = 1; hadc3.Init.DiscontinuousConvMode = DISABLE; hadc3.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR; hadc3.Init.Overrun = ADC_OVR_DATA_PRESERVED; hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE; hadc3.Init.OversamplingMode = DISABLE; if (HAL_ADC_Init(&hadc3) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&hadc3, &sConfig) != HAL_OK) { Error_Handler(); } } === 4.ADC读取函数 === void sort(unsigned short int a[], int n) { int i, j, t; //元素从小到大排列 for (i = 0; i < n - 1; i++) { for (j = 0; j < n - i - 1; j++) { if (a[j] > a[j + 1]) { t = a[j]; a[j] = a[j + 1]; a[j + 1] = t; } } } } int read(int channel) { int i; unsigned long int temp = 0; unsigned short int data[50]; ADC_ChannelConfTypeDef channel_config; int channel_remap[2] = {ADC_CHANNEL_16,ADC_CHANNEL_1};//ADC1,16通道 channel_config.Channel = channel_remap[channel]; //通道选择 channel_config.Offset = 0; //偏移量为0 channel_config.Rank = ADC_REGULAR_RANK_1; //第一个序列 channel_config.SamplingTime = ADC_SAMPLETIME_1CYCLE_5; //采样时间 channel_config.SingleDiff = ADC_SINGLE_ENDED; //单端输入 channel_config.OffsetNumber = ADC_OFFSET_NONE; //不选择偏移序号 for(i = 0;i < 50;i ++){ if(channel == 0){ //如果通道为0,则使能ADC1 HAL_ADC_ConfigChannel(&hadc1,&channel_config); HAL_ADC_Start(&hadc1); while(!__HAL_ADC_GET_FLAG(&hadc1,ADC_FLAG_EOC)); data[i] = HAL_ADC_GetValue(&hadc1); }else if(channel == 1){ //如果通道为1,则使能ADC3 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,50); for(i = 20;i < 30;i++){ //取ADC排序后的中间10位数值 temp += data[i]; } temp = temp / 10; //取ADC平均值 if(channel == 0){ //读取ADC1的值 my_adc.value[channel] = temp * ADC_REF / 65536; } return temp; } static int read_mux(void) { //打开通道并读取ADC的值 CHANNEL_0_ON; my_adc.value[1] = my_adc.read(1) * ADC_REF / 65536; CHANNEL_1_ON; my_adc.value[2] = my_adc.read(1) * ADC_REF / 65536; CHANNEL_2_ON; my_adc.value[3] = my_adc.read(1) * ADC_REF / 65536; CHANNEL_3_ON; my_adc.value[4] = my_adc.read(1) * ADC_REF / 65536; CHANNEL_4_ON; my_adc.value[5] = my_adc.read(1) * ADC_REF / 65536; CHANNEL_5_ON; my_adc.value[6] = my_adc.read(1) * ADC_REF / 65536; CHANNEL_6_ON; my_adc.value[7] = my_adc.read(1) * ADC_REF / 65536; CHANNEL_7_ON; my_adc.value[8] = my_adc.read(1) * ADC_REF / 65536; return 0; } ==== 五、实验步骤 ==== - 把仿真器与iCore4TX的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore4TX通过Micro USB线与计算机相连,为iCore4TX供电; - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore4TX上; - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 六、实验现象 ==== 串口一直向终端输出电源的数据。 {{ :icore4tx:icore4tx_arm_hal_12_6.png?direct |}}