STM32开发,野火ADC—独立模式-单通道-DMA例程BUG

STM32开发,野火ADC—独立模式-单通道-DMA例程BUG

  • 1 概述
    • 1.1 资源概述
    • 1.2 实现功能
  • 2 程序实现
    • 2.1主程序
    • 2.2 ADC程序
    • 2.3 ADC头文件
  • 3 程序调试
  • 4 查找原因

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 
#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。
STM32开发,野火ADC—独立模式-单通道-DMA例程BUG_第1张图片

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) 这些位由软件设置和清除 
 0080116103211:保留
 这是错误的设置,导致外设的数据宽度设置了两次,而存储器保持默认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) 这些位由软件设置和清除 
 0080116103211:保留
 这是正确的设置,存储器设置位01,即为16位,最高4位不会丢失

原因分析和实际情况一致。
查看野火给的资料,发现里边的这个地方是错的,和程序保持了一致。这个程序应该是被误写(使用自动补全),然后编译后实际验证没有认真检查导致。
STM32开发,野火ADC—独立模式-单通道-DMA例程BUG_第2张图片
整个例程就错了一个字母,而我找这个字母花费了两天的时间。

你可能感兴趣的:(STM32开发学习笔记)