STM Урок 20. HAL. ADC. Injected Channel. Interrupt



Урок 20

HAL. ADC. Injected Channel. Interrupt

 

Сегодня мы продолжим работать с инжекторным каналом АЦП.

Только сегодня мы попробуем поработать с прерываниями такого канала, тем самым мы немного автоматизируем процесс, не будем использовать задержки, что придаст нашему проекту большую профессиональность.

Проект мы также создадим из предыдущего — из ADC_INJECTED, и назовём его ADC_INJECTED_INT, всего лишь прицепив суффикс _INT.

Запустим проект в Cube MX.

Первым делом мы, конечно, включим эти самые прерывания

 

image00

 

Перейдём в данном диалоге в закладку «Parameter Settings» и параметр «End Of Confersion Selectiom» переключим в следующее состояние

 

image01

 

Применим настройки, сгенерируем проект, запустим его в Keil, настроим программатор на авторезет, добавим файл LCD.c в проект, соберём проект и начнем писать код.

Скопируем из бесконечного цикла строку

 HAL_ADCEx_InjectedStart(&hadc1);

и вставим её до бесконечного цикла, слегка исправив, добавив префикс _IT

 LCD_Clear();
 HAL_ADCEx_InjectedStart_IT(&hadc1);
  /* USER CODE END 2 */
 

В бесконечном цикле всё закомментируем, кроме задержки.

 

 

Добавим также обработчик прерывания для инжектированного канала АЦП. Он почти ничем не отличается от обработчика прерываний регулярного

 

void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc1)
{

}

 

Обработчик от регулярного канала пока оставим на будущее.

В функции-обработчике прерываний мы будем забирать результат, так как самое время это здесь именно и делать. Данный обработчик вызывается по окончанию преобразований напряжений.

Только преобразовывать в тип переменной с плавающей точкой здесь не будем для экономии времени на обработку. Будем использовать целочисленный массив. Он у нас уже был, но на прошлом занятии мы его удалили. Теперь придется его добавить заново

 

/* Private variables ———————————————————*/
volatile uint16_t ADC_Data[4];
 

Теперь можно будем добавить код в обработчик прерывания, используя данный массив

 

void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc1)
{
  ADC_Data[0]=HAL_ADCEx_InjectedGetValue(hadc1,ADC_INJECTED_RANK_1);
  ADC_Data[1]=HAL_ADCEx_InjectedGetValue(hadc1,ADC_INJECTED_RANK_2);
  ADC_Data[2]=HAL_ADCEx_InjectedGetValue(hadc1,ADC_INJECTED_RANK_3);
  ADC_Data[3]=HAL_ADCEx_InjectedGetValue(hadc1,ADC_INJECTED_RANK_4);

}

 

И в конце нам нужно заново запустить преобразование. Можно вызов функции скопировать из нашего кода из функции main(), только амперсанд нам не потребуется, так как у нас уже указатель

 

  ADC_Data[3]=HAL_ADCEx_InjectedGetValue(hadc1,ADC_INJECTED_RANK_4);
  cnt1++;
  HAL_ADCEx_InjectedStart_IT(hadc1);

}
 

Добавим переменную i в функцию main()

 

 char str1[9];
 uint8_t i;
  /* USER CODE END 1 */
 

Теперь добавим в бесконечный цикл конечный цикл, в котором мы уложим все значения, считанны в обработчике прерывания, на место — в массив переменных с плавающей точкой

 

while (1)
{
  for(i=0;i<4;i++) u[i]=((float)ADC_Data[i])*3/4096;

 

 

Раскомментируем код, который у нас закоментирован в бесконечном цикле и исправим его под создавшиеся новые условия, отображая постепенно считанные и обработанные данные на символьном дисплее

 

  for(i=0;i<4;i++) u[i]=((float)ADC_Data[i])*3/4096;
  sprintf(str,»%.2fv»,u[0]);//преобразуем результат в строку
  sprintf(str1,»%.2fv»,u[1]);//преобразуем результат в строку
  strcat(str,str1);
  sprintf(str1,»%.2fv»,u[2]);//преобразуем результат в строку
  strcat(str,str1);
  sprintf(str1,»%.2fv»,u[3]);//преобразуем результат в строку
  strcat(str,str1);
  LCD_SetPos(0,0);//покажем результат на ЖКИ-дисплее
  LCD_String(str);

  HAL_Delay(200);
  /* USER CODE END WHILE */

 

Соберем наш код и проверим его на практике

 

image02

 

Мы видим, что всё отлично работает. Убедиться в этом можно, покрутив резисторы в делителях напряжений.

Также мы можем ещё добавить счётчики, как мы делали в позапрошлом уроке, и посчитать, с какой частотой у нас работает наша шина АЦП. Даже, в принципе, можно найти тот проект и скопировать код оттуда, так просто получится быстрее

 

volatile uint16_t ADC_Data[4];
volatile uint32_t cnt1,cnt2;
/* USER CODE END PV */

 

Также добавим в файл stm32f4xx_it.c

 

/* USER CODE BEGIN 0 */
extern volatile uint32_t cnt2;
/* USER CODE END 0 */
………………….

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
 cnt2++;
  /* USER CODE END SysTick_IRQn 0 */
 

На всякий случае инициализируем счётчики в функции main()

 

  /* USER CODE BEGIN 2 */
 cnt1=0;cnt2=0;
 

Считать будем в обработчике прерываний от АЦП

 

  ADC_Data[3]=HAL_ADCEx_InjectedGetValue(hadc1,ADC_INJECTED_RANK_4);
  cnt1++;
  HAL_ADCEx_InjectedStart_IT(hadc1);
}

 

И останется нам отобразить это где-то на дисплее. Но раз уж мы заняли 1ю строчку, то отобразим всё это в другом месте

 

  LCD_String(str);
  sprintf(str,»%10u»,cnt1);//преобразуем результат в строку
  LCD_SetPos(6,1);//покажем результат на ЖКИ-дисплее
  LCD_String(str);
  sprintf(str,»%10u»,cnt2);//преобразуем результат в строку
  LCD_SetPos(6,2);//покажем результат на ЖКИ-дисплее
  LCD_String(str);

  HAL_Delay(200);
  /* USER CODE END WHILE */
 

Соберём проект, прошьём контроллер и посмотрим результат. Дожидаемся, когда на дисплее в нижней строке будет применрно будет 10000

 

image03

 

И сделав нехитрый подсчёт мы видим, что АЦП у нас работает где-то со скоростью 220 кГц.

Это очень неплохо, что одновременно 4 канала успевают преобразовывать данные. поступающие на входы с такой частотой.

Попробуем также порегулировать резисторы, чтобы убедиться, что у нас ничего не висит.

 

 

Предыдущий урок Программирование МК STM32 Следующий урок

 

Исходный код

 

Отладочную плату и дисплей можно приобрести здесь:

STM32F4-DISCOVERY

Дисплей LCD 20×4

 

 

Смотреть ВИДЕОУРОК

 

STM32 HAL. ADC. Injected Channel. Interrupt

 

Один комментарий на “STM Урок 20. HAL. ADC. Injected Channel. Interrupt
  1. Андрей:

    Добрый день, в моем эксперименте по данному уроку при точно таких же вводных (PCKL 84МГц, ADCCLK=PCKL/2, Ts=3 cycles) мой АЦП (ADC1) считает с частотой примерно 98 кГц. Единственное отличие я измеряю не подстроечные резисторы, а внутренний датчик температуры у мк на 18 канале, который подвожу также к 4 рангам инжектированной группы. У меня плата NUCLEO-F446RE. Почему у Вас 220кГц, а у меня 98кГц?
    И при Ts=3 cycles АЦП измеряет не корректно входные сигналы, нужно ставить Sampling Time хотя бы 28 cycles, но тогда и частота АЦП понижается до 80кГц.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*