STM32 сохранение данных АЦП с помощью DMA.
DMA(Direct Memory Access – прямой доступ к памяти) – позволяет передавать данные без участия ядра. То есть, если использовать DMA, то по завершении преобразования АЦП, нам не надо забирать результат преобразования и сохранять его в памяти, за нас это сделает DMA. Для этого при конфигурации DMA достаточно указать адрес откуда брать данные и куда сохранять, а также ещё несколько простых настроек, которые мы рассмотрим ниже.
Передача данных осуществляется по специально отведённым каналам, у STM32F103 их целых 12 штук, они распределены между двумя контроллерами, 7 каналов у первого и 5 у второго. Картинки можно увеличить кликнув по ним.
Как видим, каждое периферийное устройство закреплено за определённым каналом DMA.
Каждому запросу можно назначить один из приоритетов:
В случае одновременного возникновения двух запросов, с одинаковыми приоритетами, первым выполнится тот, у которого номер канала меньше. В такой ситуации 2-й канал будет иметь преимущество перед 4-м.
Каждый канал DMA может формировать запрос на прерывание по одному из событий:
Как писалось выше, при конфигурации DMA надо указать адреса отправителя и получателя - эти адреса являются базовыми, также можно инкрементировать значение адреса отправителя/получателя после каждой посылки, это может пригодиться, когда полученные данные надо сохранить в массив.
При инициализации необходимо указать размерность данных, которые будем отправлять и получать с помощью DMA, она может быть равна 8, 16 или 32 бита. Если размерность данных отправителя и получателя не совпадают, контроллер DMA выравняет их, в RM0008 есть целая таблица, в которой можно посмотреть как происходит выравнивание.
Максимальный размер буфера DMA составляет 65535 значений.
Давайте теперь рассмотрим как сохранить в память данные, полученные с ADC1.
В существующей инициализации АЦП достаточно в регистре CR2 разрешить отправку по DMA.
Теперь можно переходить к конфигурации DMA.
Циклический режим работает следующим образом, после запуска DMA значение в регистре DMA_CNTDRх будет автоматически декрементироваться(уменьшаться) после каждой посылки, пока оно не станет равным нулю, затем значение в регистре автоматически обновится на DMA_BUFF_SIZE и всё начнётся сначала. С помощью этого режима очень просто организовать кольцевой буфер, что мы и сделали.Теперь в массиве buff всегда будут свежие значения, полученные от АЦП. Осталось только вовремя остановить эту карусель и вывести полученные значения на экран.
Для чего? У меня, например, по такому принципу работает прототип осциллографа, посмотреть на него можно тут.
Передача данных осуществляется по специально отведённым каналам, у STM32F103 их целых 12 штук, они распределены между двумя контроллерами, 7 каналов у первого и 5 у второго. Картинки можно увеличить кликнув по ним.
Как видим, каждое периферийное устройство закреплено за определённым каналом DMA.
Каждому запросу можно назначить один из приоритетов:
- очень высокий
- высокий
- средний
- низкий
В случае одновременного возникновения двух запросов, с одинаковыми приоритетами, первым выполнится тот, у которого номер канала меньше. В такой ситуации 2-й канал будет иметь преимущество перед 4-м.
Каждый канал DMA может формировать запрос на прерывание по одному из событий:
- отправлена половина буфера
- отправка завершена
- ошибка при передаче
Как писалось выше, при конфигурации DMA надо указать адреса отправителя и получателя - эти адреса являются базовыми, также можно инкрементировать значение адреса отправителя/получателя после каждой посылки, это может пригодиться, когда полученные данные надо сохранить в массив.
При инициализации необходимо указать размерность данных, которые будем отправлять и получать с помощью DMA, она может быть равна 8, 16 или 32 бита. Если размерность данных отправителя и получателя не совпадают, контроллер DMA выравняет их, в RM0008 есть целая таблица, в которой можно посмотреть как происходит выравнивание.
Максимальный размер буфера DMA составляет 65535 значений.
Давайте теперь рассмотрим как сохранить в память данные, полученные с ADC1.
В существующей инициализации АЦП достаточно в регистре CR2 разрешить отправку по DMA.
ADC1->CR2 |= ADC_CR2_DMA;
Теперь можно переходить к конфигурации DMA.
//Адрес регистра результата преобразования АЦП
#define ADC1_DR_Address ((u32)0x40012400+0x4c)
#define DMA_BUFF_SIZE 10//Размер буфера
uint16_t buff[DMA_BUFF_SIZE];//Буфер
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Разрешаем тактирование первого DMA модуля
DMA1_Channel1->CPAR = ADC1_DR_Address; //Указываем адрес периферии - регистр результата преобразования АЦП для регулярных каналов
DMA1_Channel1->CMAR = (uint32_t)buff; //Задаем адрес памяти - базовый адрес массива в RAM
DMA1_Channel1->CCR &= ~DMA_CCR1_DIR; //Указываем направление передачи данных, из периферии в память
DMA1_Channel1->CNDTR = DMA_BUFF_SIZE; //Количество пересылаемых значений
DMA1_Channel1->CCR &= ~DMA_CCR1_PINC; //Адрес периферии не инкрементируем после каждой пересылки
DMA1_Channel1->CCR |= DMA_CCR1_MINC; //Адрес памяти инкрементируем после каждой пересылки.
DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0; //Размерность данных периферии - 16 бит
DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0; //Размерность данных памяти - 16 бит
DMA1_Channel1->CCR |= DMA_CCR1_PL; //Приоритет - очень высокий
DMA1_Channel1->CCR |= DMA_CCR1_CIRC; //Разрешаем работу DMA в циклическом режиме
DMA1_Channel1->CCR |= DMA_CCR1_EN; //Разрешаем работу 1-го канала DMA
Циклический режим работает следующим образом, после запуска DMA значение в регистре DMA_CNTDRх будет автоматически декрементироваться(уменьшаться) после каждой посылки, пока оно не станет равным нулю, затем значение в регистре автоматически обновится на DMA_BUFF_SIZE и всё начнётся сначала. С помощью этого режима очень просто организовать кольцевой буфер, что мы и сделали.Теперь в массиве buff всегда будут свежие значения, полученные от АЦП. Осталось только вовремя остановить эту карусель и вывести полученные значения на экран.
Для чего? У меня, например, по такому принципу работает прототип осциллографа, посмотреть на него можно тут.
Похожие статьи