1 概述
实验的代码已经上传,无需积分。
1.1 资源概述
开发板:正点原子STM32F103 Nano开发板
CUBEMX版本:1.3.0
MDK版本:5.27
主控芯片型号:STM32F103RBT6
1.2 实现功能
1,移植野火ADC使用DMA传输例程,实现读取B01的电压,并通过串口打印出来。野火的程序使用的RCT6芯片,引脚与RBT6相同,改动比较简单。
2 程序实现
2.1主程序
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
- #include "stm32f1xx.h"
- #include "./usart/bsp_debug_usart.h"
- #include "./led/bsp_led.h"
- #include <stdio.h>
- #include "./adc/bsp_adc.h"
- // ADC1转换的电压值通过MDA方式传到SRAM
- extern __IO uint32_t ADC_ConvertedValue;
- // 局部变量,用于保存转换计算后的电压值
- float ADC_Vol;
- static void Delay(__IO uint32_t nCount) //简单的延时函数
- {
- for(; nCount != 0; nCount--);
- }
- /**
- * @brief 主函数
- * @param 无
- * @retval 无
- */
- int main(void)
- {
- /* 配置系统时钟为72 MHz */
- SystemClock_Config();
- /* 初始化USART1 配置模式为 115200 8-N-1 */
- DEBUG_USART_Config();
- Rheostat_Init();
- while (1)
- {
- ADC_Vol =(float) ADC_ConvertedValue/4096*(float)3.3; // 读取转换的AD值
- printf("\r\n The current AD value = 0x%04X \r\n", ADC_ConvertedValue);
- printf("\r\n The current AD value = %f V \r\n",ADC_Vol);
- Delay(0x8fffff);
- }
- }
- /**
- * @brief System Clock Configuration
- * The system Clock is configured as follow :
- * System Clock source = PLL (HSE)
- * SYSCLK(Hz) = 72000000
- * HCLK(Hz) = 72000000
- * AHB Prescaler = 1
- * APB1 Prescaler = 2
- * APB2 Prescaler = 1
- * HSE Frequency(Hz) = 8000000
- * HSE PREDIV1 = 1
- * PLLMUL = 9
- * Flash Latency(WS) = 2
- * @param None
- * @retval None
- */
- void SystemClock_Config(void)
- {
- RCC_ClkInitTypeDef clkinitstruct = {0};
- RCC_OscInitTypeDef oscinitstruct = {0};
- /* Enable HSE Oscillator and activate PLL with HSE as source */
- oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
- oscinitstruct.HSEState = RCC_HSE_ON;
- oscinitstruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
- oscinitstruct.PLL.PLLState = RCC_PLL_ON;
- oscinitstruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
- oscinitstruct.PLL.PLLMUL = RCC_PLL_MUL9;
- if (HAL_RCC_OscConfig(&oscinitstruct)!= HAL_OK)
- {
- /* Initialization Error */
- while(1);
- }
- /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
- clocks dividers */
- clkinitstruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
- clkinitstruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- clkinitstruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
- clkinitstruct.APB2CLKDivider = RCC_HCLK_DIV1;
- clkinitstruct.APB1CLKDivider = RCC_HCLK_DIV2;
- if (HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2)!= HAL_OK)
- {
- /* Initialization Error */
- while(1);
- }
- }
复制代码
2.2 ADC程序
- #include "./adc/bsp_adc.h"
- __IO uint32_t ADC_ConvertedValue;
- DMA_HandleTypeDef hdma_adcx;
- ADC_HandleTypeDef ADC_Handle;
- ADC_ChannelConfTypeDef ADC_Config;
- static void Rheostat_ADC_GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- RHEOSTAT_ADC_CLK_ENABLE();
- // 使能 GPIO 时钟
- RHEOSTAT_ADC_GPIO_CLK_ENABLE();
- // 配置 IO
- GPIO_InitStructure.Pin = RHEOSTAT_ADC_GPIO_PIN;
- GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
- // GPIO_InitStructure.Pull = GPIO_NOPULL ; //不上拉不下拉
- HAL_GPIO_Init(RHEOSTAT_ADC_GPIO_PORT, &GPIO_InitStructure);
- }
- static void Rheostat_ADC_Mode_Config(void)
- {
- // ------------------DMA Init 结构体参数 初始化--------------------------
- // 开启DMA时钟
- RHEOSTAT_ADC_DMA_CLK_ENABLE();
- // 数据传输通道
- hdma_adcx.Instance = RHEOSTAT_ADC_DMA_STREAM;
- hdma_adcx.Init.Direction=DMA_PERIPH_TO_MEMORY;; //存储器到外设
- hdma_adcx.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
- hdma_adcx.Init.MemInc=DMA_MINC_DISABLE; //存储器增量模式
- hdma_adcx.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD;//外设数据长度:16位
- hdma_adcx.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位,错误已经修改
- hdma_adcx.Init.Mode= DMA_CIRCULAR; //外设普通模式
- hdma_adcx.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
- //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
- HAL_DMA_Init(&hdma_adcx);
- __HAL_LINKDMA( &ADC_Handle,DMA_Handle,hdma_adcx);
- //---------------------------------------------------------------------------
- RCC_PeriphCLKInitTypeDef ADC_CLKInit;
- // 开启ADC时钟
- ADC_CLKInit.PeriphClockSelection=RCC_PERIPHCLK_ADC; //ADC外设时钟
- ADC_CLKInit.AdcClockSelection=RCC_ADCPCLK2_DIV8; //分频因子6时钟为72M/8=9MHz
- HAL_RCCEx_PeriphCLKConfig(&ADC_CLKInit); //设置ADC时钟
- ADC_Handle.Instance=RHEOSTAT_ADC;
- ADC_Handle.Init.DataAlign=ADC_DATAALIGN_RIGHT; //右对齐
- ADC_Handle.Init.ScanConvMode=DISABLE; //非扫描模式
- ADC_Handle.Init.ContinuousConvMode=ENABLE; //连续转换
- ADC_Handle.Init.NbrOfConversion=1; //1个转换在规则序列中 也就是只转换规则序列1
- ADC_Handle.Init.DiscontinuousConvMode=DISABLE; //禁止不连续采样模式
- ADC_Handle.Init.NbrOfDiscConversion=0; //不连续采样通道数为0
- ADC_Handle.Init.ExternalTrigConv=ADC_SOFTWARE_START; //软件触发
- HAL_ADC_Init(&ADC_Handle); //初始化
- //---------------------------------------------------------------------------
- ADC_Config.Channel = RHEOSTAT_ADC_CHANNEL;
- ADC_Config.Rank = 1;
- // 采样时间间隔
- ADC_Config.SamplingTime = ADC_SAMPLETIME_55CYCLES_5 ;
- // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为3个时钟周期
- HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);
- HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t*)&ADC_ConvertedValue, 1);
- }
- void Rheostat_Init(void)
- {
- Rheostat_ADC_GPIO_Config();
- Rheostat_ADC_Mode_Config();
- }
复制代码
2.3 ADC头文件
调整端口定义与正点Nano保持一致,这里需要注意的是,GBIOB1对应的是ADC_IN9, 对应的DMA1_Channel1(通道1,而不是11)。
- #ifndef __BSP_ADC_H
- #define __BSP_ADC_H
- #include "stm32f1xx.h"
- // ADC GPIO 宏定义
- #define RHEOSTAT_ADC_GPIO_PORT GPIOB
- #define RHEOSTAT_ADC_GPIO_PIN GPIO_PIN_1
- #define RHEOSTAT_ADC_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
- // ADC 序号宏定义
- #define RHEOSTAT_ADC ADC1
- #define RHEOSTAT_ADC_CLK_ENABLE() __HAL_RCC_ADC1_CLK_ENABLE();
- #define RHEOSTAT_ADC_CHANNEL ADC_CHANNEL_9
- // ADC DMA 通道宏定义,这里我们使用DMA传输
- #define RHEOSTAT_ADC_DMA_CLK_ENABLE() __HAL_RCC_DMA1_CLK_ENABLE();
- #define RHEOSTAT_ADC_DMA_STREAM DMA1_Channel1
- void Rheostat_Init(void);
- #endif /* __BSP_ADC_H */
复制代码
3 程序调试
在程序调试过程中,串口打印一直无法输出正确的值,都是小于1的小数,而且调节滑动电阻时,数值会发生变化。由于是移植的例程,一直怀疑是我自己改程序导致的,但是经过换成别的开发板,而且ADC端口不改的情况下,还是不好用。如果开始了漫长的查找过程。路程如下:
1.使用CUBEMX生成一个ADC DMA传输的程序,正常运行没有错误;
2.将生成的程序,ADC以及DMA相关部分去掉,加入野火例程中的ADC相关文件;
3.将CUBEMX生成的ADC相关文件加到野火程序中,运行正常没有错误。
4.运行调试发现不好用,只显示小数;
5.将原CUBEMX生成的函数,一块一块加到野火程序中去,替代旧的函数。最后发现在配置半字时出现问题。
例程中错误的配置
- hdma_adcx.Init.MemDataAlignment=DMA_PDATAALIGN_HALFWORD; //存储器数据长度:16位
复制代码
而正确的配置为(P改为M)
- hdma_adcx.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
复制代码
修改后程序调试OK,下面为更改后和更改前的结果,在错误的程序中,第三位的B丢失了。正确的位B37,错误的为37。
4 查找原因
查看官方手册继续查找原因,将程序里边的定义一层层翻出来,结果如下。
错误赋值函数
- hdma_adcx.Init.MemDataAlignment=DMA_PDATAALIGN_HALFWORD; //存储器数据长度:16位
- #define DMA_PDATAALIGN_HALFWORD ((uint32_t)DMA_CCR_PSIZE_0) /*!< Peripheral data alignment: HalfWord */
- #define DMA_CCR_PSIZE_0 (0x1U << DMA_CCR_PSIZE_Pos) /*!< 0x00000100 */
- #define DMA_CCR_PSIZE_Pos (8U)
- STM32F1中文参考手册V10里边关于DMA配置寄存器的说明
- 位9:8
- PSIZE[1:0]:外设数据宽度 (Peripheral size) 这些位由软件设置和清除
- 00:8位
- 01:16位
- 10:32位
- 11:保留
- 这是错误的设置,导致外设的数据宽度设置了两次,而存储器保持默认00,即为8位,而ADC为12位右对齐,最高4位丢失。
复制代码
正确赋值的函数
- hdma_adcx.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
- #define DMA_MDATAALIGN_HALFWORD ((uint32_t)DMA_CCR_MSIZE_0) /*!< Memory data alignment: HalfWord */
- #define DMA_CCR_MSIZE_0 (0x1U << DMA_CCR_MSIZE_Pos) /*!< 0x00000400 */
- #define DMA_CCR_MSIZE_Pos (10U)
- STM32F1中文参考手册V10里边关于DMA配置寄存器的说明
- 位11:10
- MSIZE[1:0]:存储器数据宽度 (Peripheral size) 这些位由软件设置和清除
- 00:8位
- 01:16位
- 10:32位
- 11:保留
- 这是正确的设置,存储器设置位01,即为16位,最高4位不会丢失
复制代码
原因分析和实际情况一致。
查看野火给的资料,发现里边的这个地方是错的,和程序保持了一致。这个程序应该是被误写(使用自动补全),然后编译后实际验证没有认真检查导致。
|