STM32+DMA多路+ADCHAL库函数+cubemax配置+部分解释

1.DMA有很多类型:BDMA、MDMA、DMA。这里配置的是DAM和BDMA。

——用到的外设:ADC多路通道BDMA。

——ADC1的11路,ADC2的7路,ADC3的10路,总共使用ADC28路。

——ADC1和ADC2使用DMA传输,ADC3使用BDMA传输。

——芯片:STM32H723ZGT6型号。

2.STM32Cubemax配置

——新建工程——选择对应芯片类型

——选择对应芯片——STM32H723ZGT6型号——

——选yes——

——配置管脚——28路管脚——ADC1/ADC2/ADC3对应管脚——

——配置对应ADC选项——以ADC1为例子,配置11路ADC的DMA采样——其他ADC也相应配置。

——ADC3和ADC1有点不同,需要注意——

——配置其他选项——RCC和NVIC。

——配置时钟——目前配置的是13.5MHZ的ADC时钟。

——生成文件——

——最后生成文件就可以编辑程序了——

3.编辑程序需要添加的程序

——在adc.c文件下添加代码

/* USER CODE BEGIN 1 */
void adc_init(void)//在adc.c文件下插入写到/* USER CODE BEGIN 1 */下和/* USER CODE END 1 */中间
{
    MX_ADC1_Init();	//初始化调用放这里, 确保在MX_DMA_Init()初始化后面
    MX_ADC2_Init();	//初始化调用放这里, 确保在MX_DMA_Init()初始化后面
    MX_ADC3_Init();	//在MX_BDMA_Init()初始化后面
    
    //HAL_Delay(100);	//有地方说这里可以等等电压稳定后再校准

    if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
    {
        Error_Handler();
    }
    
    if (HAL_ADCEx_Calibration_Start(&hadc2, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_ADCEx_Calibration_Start(&hadc3, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
    {
        Error_Handler();
    }

    if (HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc1_dmabuff, adc1_buff_Size) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_ADC_Start_DMA(&hadc2, (uint32_t *)adc2_dmabuff, adc2_buff_Size) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_ADC_Start_DMA(&hadc3, (uint32_t *)adc3_dmabuff, adc3_buff_Size) != HAL_OK)
    {
        Error_Handler();
    }
}


void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
  if(hadc->Instance == ADC1) {
//      flag0++;
      SCB_InvalidateDCache_by_Addr((uint32_t *) &adc1_dmabuff[0], adc1_buff_Size);
  }else if(hadc->Instance == ADC2) {
//      flag1++;
      SCB_InvalidateDCache_by_Addr((uint32_t *) &adc2_dmabuff[0], adc2_buff_Size);
  }
  else if(hadc->Instance == ADC3) {
//      flag2++;
      SCB_InvalidateDCache_by_Addr((uint32_t *) &adc3_dmabuff[0], adc3_buff_Size);
  }
}

//ADC的DMA的完成中断回调
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    uint8_t j;
    j=1;
    ADC.flag_adc1=j;
    ADC.flag_adc2=j;
    ADC.flag_adc3=j;
	if(hadc->Instance == ADC1) {
        SCB_InvalidateDCache_by_Addr((uint32_t *) &adc1_dmabuff[adc1_buff_Size/2], adc1_buff_Size);
//        HAL_ADC_Stop_DMA(&hadc1);//add//sxq3.13
   }  else if(hadc->Instance == ADC2) {
       SCB_InvalidateDCache_by_Addr((uint32_t *) &adc2_dmabuff[adc2_buff_Size/2], adc2_buff_Size);
//        HAL_ADC_Stop_DMA(&hadc2);//add//sxq3.13
   }else if(hadc->Instance == ADC3) {
       SCB_InvalidateDCache_by_Addr((uint32_t *) &adc3_dmabuff[adc3_buff_Size/2], adc3_buff_Size);
//        HAL_ADC_Stop_DMA(&hadc3);//add//sxq3.13
   }
}

void Scan_ADC(void)
{
    uint32_t j;
    SCB_DisableDCache();
	if(ADC.flag_adc1==1){    //判断是不是ADC1进入的中断回调
		for(j=0;j<adc1_buff_Size;j++){ 
			ADC1_VALUE[j]=adc1_dmabuff[j];
            ADC.ADC1_V[j]=(float)(ADC1_VALUE[j]*3.3/65536.0);
		}
        ADC.flag_adc1=0;
	}
    if(ADC.flag_adc2==1)
    {
       for(j=0;j<adc2_buff_Size;j++){
            ADC2_VALUE[j]=adc2_dmabuff[j];
            ADC.ADC2_V[j]=(float)(ADC2_VALUE[j]*3.3/65536.0);
		} 
        ADC.flag_adc2=0;
    }
    if(ADC.flag_adc3==1)
    {
      for(j=0;j<adc3_buff_Size;j++){
            ADC3_VALUE[j]=adc3_dmabuff[j];
            ADC.ADC3_V[j]=(float)(ADC3_VALUE[j]*3.3/4096.0);
		} 
        ADC.flag_adc3=0;      
    } 
    SCB_EnableDCache();
}
/* USER CODE END 1 */

——在adc.c文件中需要注意的地方需要添加代码——在void MX_ADC3_Init(void)结尾的用户可以添加的代码区域添加上。

 /* USER CODE BEGIN ADC3_Init 2 */
  	//ADD FOR adc3 the //ADC3只能使用BDMA ,数据访问的区域有限定,在0x38000000之后;	
	/** Initializes and configures the Region and the memory to be protected */
  MPU_Region_InitTypeDef MPU_InitStruct = {0};;
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER1;
  MPU_InitStruct.BaseAddress = 0x38000000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
  MPU_InitStruct.SubRegionDisable = 0x0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;

  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
 
  HAL_MPU_ConfigRegion(&MPU_InitStruct);	

  /* USER CODE END ADC3_Init 2 */

——还有一个值得注意的地方——在adc.h中需要添加代码——这一步是因为BDMA的特殊,在D3区域,看参考手册可以知道D3区域地址在0x38000000。需要改变一下它的地址


/* USER CODE BEGIN Includes */
typedef struct {//配置使用的ADC数据存储区域结构体
    
     volatile uint32_t flag_adc1; 
     volatile uint32_t flag_adc2;
     volatile uint32_t flag_adc3;
     float ADC1_V[adc1_buff_Size];
     float ADC2_V[adc2_buff_Size];
     float ADC3_V[adc3_buff_Size];
    
}ADC_VALUE;
ADC_VALUE ADC;
extern ADC_VALUE ADC;

volatile uint16_t ADC1_VALUE[adc1_buff_Size];
volatile uint16_t ADC2_VALUE[adc2_buff_Size];
volatile uint16_t ADC3_VALUE[adc3_buff_Size];
//32字节对齐(地址+大小)
//adc1_data指定到 AXI SRAM 的0x24000000
//adc3_data指定到 SRAM4 的0x38000000
ALIGN_32BYTES (uint16_t adc1_dmabuff[adc1_buff_Size]) __attribute__((section(".ARM.__at_0x24000080")));
ALIGN_32BYTES (uint16_t adc2_dmabuff[adc2_buff_Size]) __attribute__((section(".ARM.__at_0x24000060")));
ALIGN_32BYTES (uint16_t adc3_dmabuff[adc3_buff_Size]) __attribute__((section(".ARM.__at_0x38000000")));
/* USER CODE END Includes */

extern ADC_HandleTypeDef hadc1;

extern ADC_HandleTypeDef hadc2;

extern ADC_HandleTypeDef hadc3;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_ADC1_Init(void);
void MX_ADC2_Init(void);
void MX_ADC3_Init(void);

/* USER CODE BEGIN Prototypes */
void adc_init(void);//头文件声明
void Scan_ADC(void);//头文件声明

——在主函数里头添加代码——

——最后编译一下,有些数据错误警告处理一下,主要流程就是这样,然后观察数组变化,

如果源头0x38000000数组里头变了,而使用的中间传递数组没变,

有可能是使用的Dcache的原因,找到程序中使能Dceche注释掉,即SCB_EnbleDCache();又或者其他你自己编写使能的函数注释掉,看能不能解决。如果没有,

看对应ADC的数据寄存器有没有变化,如果改变了,说明ADC调试通了,主要逻辑层面或者内存数据处理的问题。

如果寄存器没有变化,可能你ADC都没调通,需要重新调配。

以上仅仅属于本人学习心得,可供学习参考,禁止商用~

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
下面是一个使用STM32F103标准库函数的串口+DMA发送不定长数据的示例代码: ```c #include "stm32f10x.h" // 定义串口DMA发送缓存区大小 #define TX_BUFFER_SIZE 256 // 定义串口DMA发送缓存区 uint8_t tx_buffer[TX_BUFFER_SIZE]; // 初始化函数 void init(void) { // 使能DMA1时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 使能串口1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 配置USART1的GPIO引脚 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); // 配置DMA1通道4 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tx_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 0; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); // 使能DMA1通道4传输完成中断 DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); // 使能DMA1通道4 DMA_Cmd(DMA1_Channel4, ENABLE); } // 串口DMA发送函数 void uart_send_dma(uint8_t *data, uint16_t len) { // 等待DMA1通道4传输完成 while (DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET); // 清除DMA1通道4传输完成标志位 DMA_ClearFlag(DMA1_FLAG_TC4); // 复位DMA1通道4 DMA_Cmd(DMA1_Channel4, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel4, len); DMA_Cmd(DMA1_Channel4, ENABLE); // 将数据写入发送缓存区 for (int i = 0; i < len; i++) { tx_buffer[i] = data[i]; } } int main(void) { init(); // 发送数据 uint8_t data[] = {0x01, 0x02, 0x03, 0x04}; uart_send_dma(data, sizeof(data)); while (1); return 0; } ``` 需要注意的是,本示例使用了USART1的TX引脚,并且只实现了发送功能。如果需要实现接收功能,需要使用USART1的RX引脚,并且需要在USART_InitStructure中设置USART_Mode为USART_Mode_Rx | USART_Mode_Tx。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值