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

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

386

устройства АЦП в режиме DMA (в противном случае новое преобразование не будет запускаться).

12.2.6.1. Многократное преобразование одного канала в режиме DMA

Чтобы выполнить заданное количество преобразований одного и того же канала (или одной и той же последовательности каналов) в режиме DMA, вам необходимо сделать следующее:

Установить в поле hadc.Init.ContinuousConvMode значение ENABLE.

Выделить буфер достаточного размера.

Передать HAL_ADC_Start_DMA() количество желаемых преобразований.

12.2.6.2.Многократные и не непрерывные преобразования в режиме

DMA

Чтобы выполнить многократные преобразования в режиме DMA, вам необходимо выполнить следующие шаги:

Установить в поле hadc.Init.DMAContinuousRequests значение ENABLE.

Вызвать HAL_ADC_Start_DMA() для запуска преобразований в режиме DMA.

Если вместо этого в поле hadc.Init.DMAContinuousRequests установить значение DISABLE, вам необходимо вызывать HAL_ADC_Stop_DMA() в конце каждой последовательности преобразования, а после повторно вызывать HAL_ADC_Start_DMA(). В противном случае преобразование не начнется.

12.2.6.3. Непрерывные преобразования в режиме DMA

Установить в поле hadc.Init.ContinuousConvMode значение ENABLE.

Установить в поле hadc.Init.DMAContinuousRequests значение ENABLE, в противном

случае АЦП не перезапускает DMA после завершения первого сканирования последовательности.

Сконфигурировать поток/канал DMA в режиме DMA_CIRCULAR.

12.2.7. Обработка ошибок

Периферийные устройства АЦП могут уведомлять разработчиков в случае потери результата преобразования. Данное условие ошибки возникает, когда происходит непрерывное преобразование или при преобразовании в режиме сканирования, и регистр данных АЦП перезаписывается последующей транзакцией до его чтения. Когда это происходит, устанавливается специальный бит в регистре ADC_SR и генерируется прерывание АЦП.

Мы можем перехватить ошибку переполнения (overrun error), реализовав следующий обратный вызов:

void HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc);

При возникновении ошибки переполнения передачи DMA отключаются, и запросы к DMA больше не принимаются. В этом случае, если сделан запрос к DMA, выполняемое регулярное преобразование прерывается и дальнейшие запуски регулярного преобразования игнорируются. После этого необходимо сбросить флаг OVR и бит DMAEN

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

387

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

HAL_ADC_Start_DMA()).

Мы можем смоделировать ошибку переполнения, включив режим непрерывного преобразования в предыдущем примере и установив значение ENABLE в поле hadc.Init.DMAContinuousRequests11: если прерывание от АЦП разрешено и из него вызывается HAL_ADC_IRQHandler(), тогда вы сможете перехватить ошибку переполнения.

Ошибка переполнения связана не только с неправильной конфигурацией интерфейса АЦП. Она может быть сгенерирована, даже когда АЦП работает в циклическом режиме DMA. Для заказного устройства, основанного на микроконтроллере STM32F4, которое я сделал некоторое время назад, при интенсивном использовании DMA несколькими периферийными устройствами, я обнаружил, что ошибка переполнения может возникнуть, когда DMA выполняет другие параллельные транзакции. Несмотря на то, что арбитраж шины должен избегать условий гонки (race conditions), особенно когда приоритеты установлены правильно, я столкнулся с данной ошибкой в некоторых трудно воспроизводимых ситуациях. Правильно обработав ошибку переполнения, когда это произошло, я смог перезапустить преобразования. Само собой разумеется, что прежде, чем я понял источник неожиданных остановок преобразования в режиме DMA, я потратил несколько дней, пытаясь решить проблему.

12.2.8. Преобразования, управляемые таймером

Периферийное устройство АЦП может быть сконфигурировано для управления от таймера через линию запуска TRGO. Таймер, используемый для выполнения данной операции, специально спроектирован во время разработки микросхемы. Например, в микроконтроллере STM32F401RE периферийное устройство ADC1 может быть синхронизировано таймером TIM2. Эта функция чрезвычайно полезна для преобразования АЦП с заданной частотой дискретизации. Например, мы можем оцифровать звуковой сигнал, генерируемый микрофоном на частоте 20 кГц. Данные результата могут быть сохранены в постоянной памяти.

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

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

Сконфигурируйте линию запуска TRGO таймера, чтобы она запускала преобразование каждый раз, когда генерируется событие обновления (TIM_TRGO_UPDATE)12.

11В некоторых микроконтроллерах STM32 также необходимо явно включить обнаружение переполнения, установив для параметра hadc.Init.Overrun значение ADC_OVR_DATA_OVERWRITTEN. Обращайтесь к исходному коду HAL для рассматриваемого семейства микроконтроллера.

12Обратите внимание, что важно сконфигурировать режим вывода TRGO таймера с помощью процедуры HAL_TIMEx_MasterConfigSynchronization(), даже если таймер не работает в режиме ведущего. Это является источником путаницы для начинающих пользователей, и я должен признать, что это немного нелогично.

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

388

Сконфигурируйте АЦП так, чтобы выбранная линия таймера TRGO запускала преобразования, и убедитесь, что режим непрерывного преобразования отключен (потому что линия TRGO запускает преобразование). Кроме того, установите для

поля hadc.Init.DMAContinuousRequests значение ENABLE и DMA в циклическом ре-

жиме, если вы хотите выполнять N преобразований в течение неопределенного времени, или установите для поля hadc.Init.DMAContinuousRequests значение DISABLE, если вы хотите остановить преобразование после выполнения N-го их количества.

Обязательно установите для поля hadc.Init.ContinuousConvMode значение DISABLE, в противном случае АЦП выполняет преобразования самостоятельно, не ожидая запуска от таймера.

Запустите таймер.

Запустите АЦП в режиме прерываний или в режиме DMA.

Вследующем примере показано, как запускать преобразование каждую 1 с в микроконтроллере STM32F401RE при помощи таймера TIM2.

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

17int main(void) {

18char msg[20];

19uint16_t rawValues[3];

20float temp;

21

22HAL_Init();

23Nucleo_BSP_Init();

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

26MX_TIM2_Init();

27MX_ADC1_Init();

29HAL_TIM_Base_Start(&htim2);

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

32while(1) {

33while(!convCompleted);

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

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

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

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

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

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

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

44}

45convCompleted = 0;

46}

47}

48

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

389

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

50void MX_ADC1_Init(void) {

51ADC_ChannelConfTypeDef sConfig;

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

54

__HAL_RCC_ADC1_CLK_ENABLE();

55

 

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

57*/

58hadc1.Instance = ADC1;

59hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;

60hadc1.Init.Resolution = ADC_RESOLUTION_12B;

61hadc1.Init.ScanConvMode = DISABLE;

62hadc1.Init.ContinuousConvMode = DISABLE;

63hadc1.Init.DiscontinuousConvMode = DISABLE;

64hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG2_T2_TRGO;

65hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;

66hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

67hadc1.Init.NbrOfConversion = 3;

68hadc1.Init.DMAContinuousRequests = ENABLE;

69hadc1.Init.EOCSelection = 0;

70HAL_ADC_Init(&hadc1);

71

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

73sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;

74sConfig.Rank = 1;

75sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;

76HAL_ADC_ConfigChannel(&hadc1, &sConfig);

77}

78

79void MX_TIM2_Init(void) {

80TIM_ClockConfigTypeDef sClockSourceConfig;

81TIM_MasterConfigTypeDef sMasterConfig;

82

83 __HAL_RCC_TIM2_CLK_ENABLE(); 84

85htim2.Instance = TIM2;

86htim2.Init.Prescaler = 41999; // 84 МГц / 42000 = 2000 Гц

87htim2.Init.Period = 1999;

88htim2.Init.CounterMode = TIM_COUNTERMODE_UP;

89htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

90HAL_TIM_Base_Init(&htim2);

91

92sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

93HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);

94

95sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;

96sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

97HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);

98}