为RTThreaadV4.0.2+STM32的ADC驱动添加DMA方式

为RTThreaadV4.0.2+STM32的ADC驱动添加DMA方式

说明

目前RTThread的adc设备驱动采用轮询法,比较浪费cpu资源。话说我从开始使用STM32到现在,就没用过ADC轮询方式
这是我的某个项目中对RTThread的ADC设备驱动添加DMA方式

代码分析

1.相关宏定义
自行选择方便的地方定义,我是直接跟在board.h中ADC定义的后面
board.h中

	#define BSP_USING_ADC1
	#define BSP_USING_ADC_DMA
	/*#define BSP_USING_ADC2*/
	/*#define BSP_USING_ADC3*/
	#ifdef BSP_USING_ADC_DMA
	//每个通道缓存数据个数(因为我的项目中采用去除最大最小,移位求平均,所以取值2^n+2)
	#define BSP_DMA_BUF_COUNT       34    
	//总共通道数
	#define BSP_ADC_TOTAL_CHANNELS  15
	//所有使用的通道编号
	#define BSP_ADC_CHN_DEF         {0,1,2,3,4,5,6,7,10,11,12,13,14,15,17}
	//采样周期
	#define BSP_ADC_SAMPLE_CYC_DEF  ADC_SAMPLETIME_71CYCLES_5
	#endif

adc_config.h中

#ifdef BSP_USING_ADC_DMA
    #define ADC1_CONFIG                                            \
    {                                                              \
       .Instance                   = ADC1,                         \
       .Init.DataAlign             = ADC_DATAALIGN_RIGHT,          \
       .Init.ScanConvMode          = ADC_SCAN_ENABLE,             \
       .Init.ContinuousConvMode    = ENABLE,                      \
       .Init.NbrOfConversion       = BSP_ADC_TOTAL_CHANNELS,      \
       .Init.DiscontinuousConvMode = DISABLE,                      \
       .Init.NbrOfDiscConversion   = 1,                            \
       .Init.ExternalTrigConv      = ADC_SOFTWARE_START,           \
    }  
#else
    #define ADC1_CONFIG                                                \
    {                                                              \
       .Instance                   = ADC1,                         \
       .Init.DataAlign             = ADC_DATAALIGN_RIGHT,          \
       .Init.ScanConvMode          = ADC_SCAN_DISABLE,             \
       .Init.ContinuousConvMode    = DISABLE,                      \
       .Init.NbrOfConversion       = 1,                            \
       .Init.DiscontinuousConvMode = DISABLE,                      \
       .Init.NbrOfDiscConversion   = 1,                            \
       .Init.ExternalTrigConv      = ADC_SOFTWARE_START,           \
    }  
    
#endif  //BSP_USING_ADC_DMA

adc.h中

typedef enum
{
    RT_ADC_CMD_ENABLE,
    RT_ADC_CMD_DISABLE,
    #ifdef BSP_USING_ADC_DMA
    RT_ADC_CMD_SET_DMA_BUF,
    #endif
} rt_adc_cmd_t;

2. 在stm32_adc_init()中,直接对adc进行初始化

#ifdef BSP_USING_ADC_DMA
            int j;
            ADC_ChannelConfTypeDef sConfig = {0};
            rt_uint32_t chn[BSP_ADC_TOTAL_CHANNELS]=BSP_ADC_CHN_DEF;
            
            sConfig.SamplingTime = BSP_ADC_SAMPLE_CYC_DEF;
            for(j=0; j<BSP_ADC_TOTAL_CHANNELS; j++)
            {
              sConfig.Channel = stm32_adc_get_channel(chn[j]);
              sConfig.Rank = j+1;
              if(HAL_ADC_ConfigChannel(&stm32_adc_obj[i].ADC_Handler, &sConfig) != HAL_OK)
              {
                rt_kprintf("channel error\n");
                Error_Handler();
              }
            }
#endif

3. 在adc接口ops结构体中,添加set_buf接口,用于设置一个接收DMA数据缓存buffer。

struct rt_adc_ops
{
    rt_err_t (*enabled)(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled);
    rt_err_t (*convert)(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value);
    #ifdef BSP_USING_ADC_DMA
    rt_err_t (*set_buf)(struct rt_adc_device *device, rt_uint16_t *dma_buf);
    #endif
};

_adc_control()中添加命令

	#ifdef BSP_USING_ADC_DMA
      case RT_ADC_CMD_SET_DMA_BUF:
        result = adc->ops->set_buf(adc, (rt_uint16_t *)args);
        break;
    #endif

4. 在adc设备对象中,添加buffer指针,通过ops接口set_buf将指针指向用户建立的buffer。

struct rt_adc_device
{
    struct rt_device parent;
    const struct rt_adc_ops *ops;
    #ifdef BSP_USING_ADC_DMA
    rt_uint16_t *dma_buf;
    #endif
};
rt_err_t stm32_set_dma_rxbuf(struct rt_adc_device *device, rt_uint16_t *dma_buf)
{
  if(dma_buf == RT_NULL) return -RT_ERROR;
  device->dma_buf = dma_buf;
  return RT_EOK;
}

static const struct rt_adc_ops stm_adc_ops =
{
    .enabled = stm32_adc_enabled,
    .convert = stm32_get_adc_value,
    .set_buf = stm32_set_dma_rxbuf, 
};

5.在stm32_adc_enabled使能函数中,添加DMA使能

#ifdef BSP_USING_ADC_DMA
      if(device->dma_buf != RT_NULL)
      {
        HAL_ADCEx_Calibration_Start(stm32_adc_handler);

        HAL_ADC_Start_DMA(stm32_adc_handler, (rt_uint32_t *)device->dma_buf, BSP_ADC_TOTAL_CHANNELS*BSP_DMA_BUF_COUNT);
      }
      else
      {
        rt_kprintf("ADC DMA buffer not set!\n");
      }
      #else
        __HAL_ADC_ENABLE(stm32_adc_handler);
      #endif

使用流程

所以,根据思路,流程有四步:
1.查找adc,rt_device_find
2.control接口设置buf,rt_device_control
3.control接口使能,rt_device_control
4.取buf数据计算。因为数据一直由DMA更新到buffer中,所以不需要使用read的ops来读数据。
注:没有为关闭adc做任何改动,因为一般实际项目中,都是一直采集adc,需要关闭的自行研究

最后

这样做有点脱离rtthread的设备驱动框架的感觉,跟使用裸机的时候差不多,只是通过设备驱动初始化和配置而已。

改进思路,
可以在adc enable中做个定时器或者线程,循环计算数据,然后通过rt_device_read来获取计算好的值或者原始值。计算函数可以通过control设置。

本意提供一种思路供大家参考,代码有问题别找我,因为我在项目中调试通过了的,但如果有更好的思路请告诉我,841389568@qq.com

对了,用Cube配置ADC时,同时要配置好DMA,然后放到board.c中。具体配置,改天更新出来。

//---------------------------20-7-24更新DMA的HAL配置--------------//

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hadc->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_ADC1_CLK_ENABLE();
  
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**ADC1 GPIO Configuration    
    PC0     ------> ADC1_IN10
    PC1     ------> ADC1_IN11
    PC2     ------> ADC1_IN12
    PC3     ------> ADC1_IN13
    PA0-WKUP     ------> ADC1_IN0
    PA1     ------> ADC1_IN1
    PA2     ------> ADC1_IN2
    PA3     ------> ADC1_IN3
    PA4     ------> ADC1_IN4
    PA5     ------> ADC1_IN5
    PA6     ------> ADC1_IN6
    PA7     ------> ADC1_IN7
    PC4     ------> ADC1_IN14
    PC5     ------> ADC1_IN15 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 
                          |GPIO_PIN_4|GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 
                          |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    #ifdef BSP_USING_ADC_DMA
    extern DMA_HandleTypeDef hdma_adc1;//在drv_adc.c里面定义了的变量

    __HAL_RCC_DMA1_CLK_ENABLE();
    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler();
    }
    __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
    #endif //BSP_USING_ADC_DMA
    
  /* USER CODE BEGIN ADC1_MspInit 1 */

  /* USER CODE END ADC1_MspInit 1 */
  }
}

代码下载

还有些小改动未在文中提及,让我赚两个积分吧,太缺积分,手动比心
https://download.youkuaiyun.com/download/qq_30659437/12598589

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梅兰里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值