Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2753
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Аналого-цифровое преобразование

382

В следующем примере просто показано, как выполнить преобразование в режиме прерываний. Код инициализации для АЦП такой же, как и в предыдущем примере.

int main(void) { HAL_Init(); Nucleo_BSP_Init();

/* Инициализация всей сконфигурированной периферии */

MX_ADC1_Init();

HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(ADC_IRQn);

HAL_ADC_Start_IT(&hadc1);

while (1);

}

void ADC_IRQHandler(void) { HAL_ADC_IRQHandler(&hadc1);

}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { char msg[20];

uint16_t rawValue; float temp;

rawValue = HAL_ADC_GetValue(&hadc1); temp = ((float)rawValue) / 4095 * 3300; temp = ((temp - 760.0) / 2.5) + 25;

sprintf(msg, "rawValue: %hu\r\n", rawValue); HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);

sprintf(msg, "Temperature: %f\r\n", temp);

HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);

}

12.2.6. Аналого-цифровые преобразования в режиме DMA

Наиболее интересным режимом управления периферийными устройствами АЦП является режим DMA. Этот режим позволяет выполнять преобразования без вмешательства ЦПУ и, используя DMA в циклическом режиме, мы можем легко сконфигурировать АЦП так, чтобы он выполнял непрерывные преобразования. Более того, как мы расскажем далее, этот режим идеально подходит для преобразования с использованием таймера, позволяющим производить выборку входного сигнала с фиксированной частотой дискретизации. Также периферийные устройства АЦП необходимо использовать в режиме DMA, когда мы хотим выполнить преобразование нескольких каналов в режиме скани-

рования.

Аналого-цифровое преобразование

383

Для выполнения аналого-цифрового преобразования в режиме DMA обычно выполняются следующие шаги:

Настройка периферийных устройств АЦП в соответствии с требуемым режимом преобразования (однократное сканирование, непрерывное сканирование и т. д.).

Настройка пары канал/поток DMA, соответствующей используемому контроллеру АЦП.

Связывание дескриптора DMA DMA_HandleTypeDef с дескриптором АЦП ADC_Hand-

leTypeDef, используя макрос __HAL_LINKDMA().

Включение DMA и разрешение IRQ, связанных с используемым потоком DMA.

Запустить АЦП в режиме DMA с помощью HAL_ADC_Start_DMA(), передав указатель на массив, используемый для хранения полученных данных из АЦП.

Быть готовым захватить событие EOC, определив обратный вызов HAL_ADC_Con-

vCpltCallback()10.

В следующем примере, предназначенном для работы на микроконтроллере

STM32F401RE, показано, как выполнить сканирование с однократным преобразованием в

режиме DMA. Первая часть, которую мы собираемся проанализировать, относится к конфигурации периферийного устройства АЦП и контроллера DMA.

Имя файла: src/main-ex2.c

44 }

45

46/* Функция инициализации ADC1 */

47void MX_ADC1_Init(void) {

48ADC_ChannelConfTypeDef sConfig;

50/* Разрешение тактирования периферийного устройства АЦП */

51

__HAL_RCC_ADC1_CLK_ENABLE();

52

 

53/**Конфигурирование глобальных функций АЦП

54*/

55hadc1.Instance = ADC1;

56hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;

57hadc1.Init.Resolution = ADC_RESOLUTION_12B;

58hadc1.Init.ScanConvMode = ENABLE;

59hadc1.Init.ContinuousConvMode = DISABLE;

60hadc1.Init.DiscontinuousConvMode = DISABLE;

61hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

62hadc1.Init.NbrOfConversion = 3;

63hadc1.Init.DMAContinuousRequests = DISABLE;

64hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;

65HAL_ADC_Init(&hadc1);

66

67/**Конфигурирование выбранных регулярных каналов АЦП */

68sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;

69sConfig.Rank = 1;

70sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;

10 Модуль HAL_ADC также предоставляет обратный вызов HAL_ADC_ConvHalfCpltCallback(), вызываемый, когда завершена половина сканирования последовательности преобразования.

Аналого-цифровое преобразование

384

71 HAL_ADC_ConfigChannel(&hadc1, &sConfig); 72

73sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;

74sConfig.Rank = 2;

75HAL_ADC_ConfigChannel(&hadc1, &sConfig);

77sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;

78sConfig.Rank = 3;

79HAL_ADC_ConfigChannel(&hadc1, &sConfig);

80}

82void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) {

83if(hadc->Instance==ADC1) {

84/* Разрешение тактирования периферийного устройства */

85

__HAL_RCC_ADC1_CLK_ENABLE();

86

 

87/* Инициализвация периферийного устройства DMA */

88hdma_adc1.Instance = DMA2_Stream0;

89hdma_adc1.Init.Channel = DMA_CHANNEL_0;

90hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;

91hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;

92hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;

93hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

94hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

95hdma_adc1.Init.Mode = DMA_NORMAL;

96hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;

97hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

98HAL_DMA_Init(&hdma_adc1);

99

100__HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

101}

102}

103

104 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {

MX_ADC1_Init() конфигурирует АЦП так, чтобы он выполнял однократное сканирование трех входов. ADCCLK установлен на максимальное значение деления (строка 56), а режим сканирования включен (строка 58). Как видите, мы конфигурируем АЦП так, чтобы он всегда выполнял преобразование от внутреннего датчика температуры: это бесполезная возможность, но, к сожалению, на платах Nucleo нет встроенных аналоговых периферийных устройств.

Функция HAL_ADC_MspInit() автоматически вызывается HAL, когда в строке 65 вызывается процедура HAL_ADC_Init(). Она просто конфигурирует поток 0/канал 0 DMA2 так, чтобы выполнялись передачи типа периферия-в-память, когда АЦП завершает преобразование. Ясно, что последовательность преобразования определяется рангом, назначенным каналу. Поскольку регистр данных АЦП размером 16 бит, мы конфигурируем DMA таким образом, чтобы выполнялась передача полуслова. Наконец, HAL автоматически вызывает функцию HAL_ADC_ConvCpltCallback(), когда заканчивается преобразование сканирования (вызов данной функции инициируется функцией HAL_DMA_IRQHandler(),

Аналого-цифровое преобразование

385

вызываемой из обработчика DMA2_Stream0_IRQHandler(), который здесь не показан). Обратный вызов устанавливает глобальную переменную, используемую для оповещения об окончании преобразования.

Имя файла: src/main-ex2.c

7extern UART_HandleTypeDef huart2;

8ADC_HandleTypeDef hadc1;

9DMA_HandleTypeDef hdma_adc1;

10volatile uint8_t convCompleted = 0;

12/* Прототипы функций ---------------------------------------------------------*/

13static void MX_ADC1_Init(void);

15int main(void) {

16char msg[20];

17uint16_t rawValues[3];

18float temp;

20HAL_Init();

21Nucleo_BSP_Init();

23/* Инициализация всей сконфигурированной периферии */

24

MX_ADC1_Init();

25

 

26

HAL_ADC_Start_DMA(&hadc1, (uint32_t*)rawValues, 3);

27

 

28

while(!convCompleted);

29

 

30

HAL_ADC_Stop_DMA(&hadc1);

31

 

32for(uint8_t i = 0; i < hadc1.Init.NbrOfConversion; i++) {

33temp = ((float)rawValues[i]) / 4095 * 3300;

34temp = ((temp - 760.0) / 2.5) + 25;

35

36sprintf(msg, "rawValue %d: %hu\r\n", i, rawValues[i]);

37HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);

39sprintf(msg, "Temperature %d: %f\r\n",i, temp);

40HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);

41}

Вышеуказанные строки кода показывают функцию main(). АЦП запускается в режиме DMA в строке 26, передавая указатель на массив rawValues и номер преобразования: он должен соответствовать полю hadc1.Init.NbrOfConversion в строке 62. Наконец, когда переменная convCompleted устанавливается в 1, содержимое массива rawValues преобразовывается, а результат отправляется по интерфейсу UART2. Обратите внимание, что в строке 30 вызывается HAL_ADC_Stop_DMA(): данная операция выполняется не для того, чтобы остановить преобразование (которое автоматически останавливается после трех выборок), а чтобы выполнить правильный алгоритм использования периферийного