четверг, 31 января 2013 г.

STM32 ссылки DMA, ADC

http://mycontroller.ru/stm32-dma-initsializatsiya/
http://emproj.com/1_5_To_90
http://electronics-archive.ru/stm32-dma-%D0%B2%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5.html

http://eddy-em.livejournal.com/tag/stm32


Пришло время рассказать об одной потрясающей штуке под названием DMA – то есть прямой доступ к памяти (Direct Memory Access). Поясню что же это такое.
В двух словах – DMA позволяет перемещать данные без(!) участия центрального процессора. То есть процессор работает себе преспокойненько, не отвлекается ни на что, а DMA в этот момент может пересылать огромные массивы данных, например, в UART. Думаю сразу понятно насколько это полезно, ведь процессору теперь не надо отвлекаться от основной полезной работы.
А в контроллерах STM32F10x даже не один, а два контроллера DMA! И, соответственно, у каждого несколько каналов (у DMA1 – 7, а у DMA2 – 5). Вот как выглядит разделение каналов между периферийными устройствами:

Список каналов DMA1
Список каналов DMA2
Я когда то упорно пытался подключить USART не к тому каналу DMA к которому нужно, и совершенно не втыкал почему не работает. С тех пор очень люблю эти картинки )
Давайте-ка теперь рассмотрим процесс настройки DMA. Открываем файлы stm32f10x_dma.h и stm32f10x_dma.c из SPL. В первом находим структуру:
typedef struct
{
  uint32_t DMA_PeripheralBaseAddr; 
  uint32_t DMA_MemoryBaseAddr;     
  uint32_t DMA_DIR;
  uint32_t DMA_BufferSize;
  uint32_t DMA_PeripheralInc;
  uint32_t DMA_MemoryInc;          
  uint32_t DMA_PeripheralDataSize; 
  uint32_t DMA_MemoryDataSize;     
  uint32_t DMA_Mode;               
  uint32_t DMA_Priority;           
  uint32_t DMA_M2M;                
}DMA_InitTypeDef;
В самом файле все довольно подробно откомментировано, правда, на английском. Итак:
uint32_t DMA_PeripheralBaseAddr – сюда мы должны записать адрес периферийного устройства, которое будет участвовать в обмене информацией
uint32_t DMA_MemoryBaseAddr – а сюда записываем адрес области памяти, где лежат данные (ну или куда их надо положить, в зависимости от направления обмена)
uint32_t DMA_DIR – тут устанавливаем, является ли периферия источником или местом назначения
uint32_t DMA_BufferSize – размер буфера для хранения данных
uint32_t DMA_PeripheralInc,
uint32_t DMA_MemoryInc – тут задаем надо ли инкрементировать указатели на данные в периферии и в памяти соответственно
Остановимся на этом поподробнее. Допустим, мы забираем данные из регистра данных АЦП и пишем их в массив в памяти. Так как мы хотим записывать результат преобразования в разные элементы массива, то необходимо инкрементировать указатель, следовательно DMA_MemoryInc = Enable. В то же время регистр данных АЦП у нас один и он никуда не перемещается, а что это значит? Правильно, uint32_t DMA_PeripheralInc = Disable )
Идем дальше:
DMA_PeripheralDataSize – размер единицы данных в периферии, аналогично – DMA_MemoryDataSize
Возможные значения этих полей:
DMA_PeripheralDataSize_Byte
DMA_PeripheralDataSize_HalfWord
DMA_PeripheralDataSize_Word
DMA_MemoryDataSize_Byte
DMA_MemoryDataSize_HalfWord
DMA_MemoryDataSize_Word
DMA_Mode – режим работы канала DMA
DMA_Priority – приоритет для канала DMA
DMA_M2M – используем ли передачу память->память
Заполнив эти поля нужным образом можно быстро и легко настроить DMA.
Пришло время немного поиграться ) Будем писать пример. Давайте объявим массив с данными в памяти и перекинем их с помощью прямого доступа к памяти в регистр данных USART, откуда они и вылетят во внешний мир.
/*******************************************************************/
#define BAUDRATE  9600
#define DMA_BUFFER_SIZE  16
 
/*******************************************************************/
GPIO_InitTypeDef port;
ADC_InitTypeDef adc;
USART_InitTypeDef usart;
uint16_t inputData;
uint16_t outputData;
DMA_InitTypeDef dma;
uint8_t dataBuffer[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
 
/*******************************************************************/
dataBuffer – массив с данными, которые надо отправить по USART’у. Не самый информативный набор данных в этом примере, но для тестирования покатит )
Инициализация:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 
 
DMA_StructInit(&dma);
dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
dma.DMA_MemoryBaseAddr = (uint32_t)&dataBuffer[0];
dma.DMA_DIR = DMA_DIR_PeripheralDST;
dma.DMA_BufferSize = DMA_BUFFER_SIZE;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_Init(DMA1_Channel4, &dma); 
 
GPIO_StructInit(&port);
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_9;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
 
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_10;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
 
USART_StructInit(&usart);
usart.USART_BaudRate = BAUDRATE;
USART_Init(USART1, &usart);
Рассматриваем отдельно то, что касается DMA:
dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
это адрес регистра данных USART
dma.DMA_MemoryBaseAddr = (uint32_t)&dataBuffer[0];
аналогично ) адрес нулевого элемента массива
dma.DMA_DIR = DMA_DIR_PeripheralDST;
шлем в периферию, а не из нее
dma.DMA_BufferSize = DMA_BUFFER_SIZE;
буфер – 16 байт (#define DMA_BUFFER_SIZE 16)
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
в периферии не инкрементируем, в памяти – инкрементируем, прям как в примере про АЦП чуть выше
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
шлем байтами
DMA_Init(DMA1_Channel4, &dma);
Идем в таблицу с каналами – USART1_TX – 4 канал, все верно =)
int main()
{
    __enable_irq();
    initAll();
    USART_Cmd(USART1, ENABLE);
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    DMA_Cmd(DMA1_Channel4, ENABLE);
    while(1)
    {
  __NOP();
    }
}
Тут все понятно:
USART_Cmd(USART1, ENABLE);
DMA_Cmd(DMA1_Channel4, ENABLE);
Включаем USART и DMA
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
Активируем передачу в последовательный порт по запросу DMA
while(1)
{
    __NOP();
}
Тут могла быть ваша программа ;) Процессор свободен.
Запускаем программу в отладчике:
DMA пример
Данные пришли туда, куда и требовалось =)
На этом, собственно, все, с прямым доступом к памяти мы разобрались!

Ссылка на источник: http://microtechnics.ru/stm32-uchebnyj-kurs-dma/

Комментариев нет:

Отправить комментарий