STM32F1与STM32CubeIDE快速入门-ADC通过DMA方式与PWM实现调光器

DMA 方法是以非常高的速率转换多个 ADC 通道的最有效方法,并且仍然将结果传输到内存而无需 CPU 干预,这是一种非常酷且省时的技术。
本次实例将实现ADC以DMA方式采样,并转换成PWM输出,从而达到调光效果。
在这里插入图片描述

1、ADC的DMA方式配置

创建工程及系统时钟配置请参考前面文章,在这里不再做介绍。

1)ADC基本参数配置
在这里插入图片描述
2)ADC的DMA配置
在这里插入图片描述
3)定时器TIM2配置
在这里插入图片描述
保存配置并生成代码。

2、代码生成与功能实现

STM32Cube IDE生成的主要代码如下:

1)ADC初始化

main.c文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/\*\*
\* @brief ADC1 Initialization Function
\* @param None
\* @retval None
\*/
static void MX\_ADC1\_Init(void) {

/\* USER CODE BEGIN ADC1\_Init 0 \*/

/\* USER CODE END ADC1\_Init 0 \*/

ADC_ChannelConfTypeDef sConfig = { 0 };

/\* USER CODE BEGIN ADC1\_Init 1 \*/

/\* USER CODE END ADC1\_Init 1 \*/
/\*\* Common config
\*/
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL\_ADC\_Init(&hadc1) != HAL_OK) {
Error\_Handler();
}
/\*\* Configure Regular Channel
\*/
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL\_ADC\_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error\_Handler();
}
/\* USER CODE BEGIN ADC1\_Init 2 \*/

/\* USER CODE END ADC1\_Init 2 \*/

}

stm32f1xx_hal_msp.c文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/\*\*
\* @brief ADC MSP Initialization
\* This function configures the hardware resources used in this example
\* @param hadc: ADC handle pointer
\* @retval None
\*/
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\_GPIOA\_CLK\_ENABLE();
/\*\*ADC1 GPIO Configuration
PA7 ------> ADC1\_IN7
\*/
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL\_GPIO\_Init(GPIOA, &GPIO_InitStruct);

/\* 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_NORMAL;
hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
if (HAL\_DMA\_Init(&hdma_adc1) != HAL_OK)
{
Error\_Handler();
}

\_\_HAL\_LINKDMA(hadc,DMA_Handle,hdma_adc1);

/\* USER CODE BEGIN ADC1\_MspInit 1 \*/

/\* USER CODE END ADC1\_MspInit 1 \*/
}

}


2)DMA初始化

main.c文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/\*\*
\* Enable DMA controller clock
\*/
static void MX\_DMA\_Init(void) {

/\* DMA controller clock enable \*/
\_\_HAL\_RCC\_DMA1\_CLK\_ENABLE();

/\* DMA interrupt init \*/
/\* DMA1\_Channel1\_IRQn interrupt configuration \*/
HAL\_NVIC\_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL\_NVIC\_EnableIRQ(DMA1_Channel1_IRQn);

}

3)定时器TIM2初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/\*\*
\* @brief TIM2 Initialization Function
\* @param None
\* @retval None
\*/
static void MX\_TIM2\_Init(void) {

/\* USER CODE BEGIN TIM2\_Init 0 \*/

/\* USER CODE END TIM2\_Init 0 \*/

TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
TIM_OC_InitTypeDef sConfigOC = { 0 };

/\* USER CODE BEGIN TIM2\_Init 1 \*/

/\* USER CODE END TIM2\_Init 1 \*/
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 65535;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL\_TIM\_Base\_Init(&htim2) != HAL_OK) {
Error\_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL\_TIM\_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error\_Handler();
}
if (HAL\_TIM\_PWM\_Init(&htim2) != HAL_OK) {
Error\_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL\_TIMEx\_MasterConfigSynchronization(&htim2, &sMasterConfig)
!= HAL_OK) {
Error\_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL\_TIM\_PWM\_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1)
!= HAL_OK) {
Error\_Handler();
}
/\* USER CODE BEGIN TIM2\_Init 2 \*/

/\* USER CODE END TIM2\_Init 2 \*/
HAL\_TIM\_MspPostInit(&htim2);

}


4)DMA中断及功能实现

stm32f1xx_it.c文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/\* Private variables ---------------------------------------------------------\*/
/\* USER CODE BEGIN PV \*/
extern uint16_t AD_RES;
/\* USER CODE END PV \*/

/\*\*
\* @brief This function handles DMA1 channel1 global interrupt.
\*/
void DMA1\_Channel1\_IRQHandler(void) {
/\* USER CODE BEGIN DMA1\_Channel1\_IRQn 0 \*/

/\* USER CODE END DMA1\_Channel1\_IRQn 0 \*/
HAL\_DMA\_IRQHandler(&hdma_adc1);
/\* USER CODE BEGIN DMA1\_Channel1\_IRQn 1 \*/

/\* USER CODE END DMA1\_Channel1\_IRQn 1 \*/
}

/\* USER CODE BEGIN 1 \*/
void HAL\_ADC\_ConvCpltCallback(ADC_HandleTypeDef \*hadc) {
// Conversion Complete & DMA Transfer Complete As Well
// So The AD\_RES Is Now Updated & Let's Move IT To The PWM CCR1
// Update The PWM Duty Cycle With Latest ADC Conversion Result
TIM2->CCR1 = (AD_RES << 4);
}

5)程序入口及功能实现

main.c文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/\* Private typedef -----------------------------------------------------------\*/
/\* USER CODE BEGIN PTD \*/
uint16_t AD_RES = 0;
/\* USER CODE END PTD \*/

/\*\*
\* @brief The application entry point.
\* @retval int
\*/
int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_DMA\_Init();
MX\_ADC1\_Init();
MX\_TIM2\_Init();
/\* USER CODE BEGIN 2 \*/
HAL\_TIM\_PWM\_Start(&htim2, TIM_CHANNEL_1);
// Calibrate The ADC On Power-Up For Better Accuracy
HAL\_ADCEx\_Calibration\_Start(&hadc1);
/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
// Start ADC Conversion
// Pass (The ADC Instance, Result Buffer Address, Buffer Length)
HAL\_ADC\_Start\_DMA(&hadc1, &AD_RES, 1);
HAL\_Delay(1);
}
/\* USER CODE END 3 \*/
}

文章来源: https://iotsmart.blog.csdn.net/article/details/123496094