Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F2xx - USART+DMA
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
athlon64
Проект на STM32F217, FreeRTOS
Реализовал работу с портом (передача с DMA, приём по прерываниям). В таком режиме всё работает прекрасно. Не стал делать приём с DMA сразу, т.к. думал что аппаратно определить конец кадра в STM32 нельзя.
Но недавно заметил что в регистре SR есть флажок IDLE, который по идее для этого и предназначен. Проверил, флажок действительно выставляется в конце каждого фрейма и прерывание генерируется.
Но при реализации приёма с DMA возникли проблемы. Принимается первый пакет, обрабатывается и отсылается ответ корректно. Далее приём обламывается, т.к. регистр NDTR не переустанавливается при возобновлении приёма. А по этому регистру считается размер принятого фрейма.
Добавил DMA2_Stream1->CR = 0; перед началом нового приёма, т.к. настроечные регистры DMA недоступны для изменения пока активен стрим. Но сразу после обнуления CR попадаю в HardFault_Handler().

Инициализация порта:
Код
void COM0_init(unsigned int BaudRate)
{
  // Тут инициализация пинов и BaudRate //

  // 1 Stop bit, LIN OFF
  USART6->CR2 = 0x00000000;
  // UART enable, TX,RX enable
  USART6->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_IDLEIE;
  USART6->CR3 = USART_CR3_DMAT | USART_CR3_DMAR;   // Включаем DMA на передачу и на приём UART6
  USART6->CR1 |= USART_CR1_UE;
  NVIC->ISER[2] = (1 << (USART6_IRQn & 0x1F)); // Разрешаем прерывания от USART6
  NVIC->ISER[1] = (1 << (DMA2_Stream1_IRQn & 0x1F)); // Разрешаем прерывания от DMA

  ReceiveFromCOM0_DMA();
}


Обработчики прерываний:
Код
extern xSemaphoreHandle semCOM1pak;

void USART6_IRQHandler(void)
{
  signed long Test;
  int status = USART6->SR;

  if ((status & USART_SR_TC) && (USART6->CR1 & USART_CR1_TCIE)) // Передача окончена (последний байт полностью передан в порт)
  {
    USART6->SR &= ~USART_SR_TC;   // Снимаем флаг
    USART6->CR1 &= ~USART_CR1_TCIE;
    ReceiveFromCOM0_DMA();
  }
  else if (status & USART_SR_IDLE)
  {
    USART6->DR; // Снимаем флаг

    Test = DMA2_Stream1->NDTR;
    
    GPIOE->ODR ^= (1<<6);
    
    PtrInCOM0 = COM_InBuffSize - DMA2_Stream1->NDTR;
    xSemaphoreGiveFromISR(semCOM1pak, &Test);
    vPortYieldFromISR();
  }
}

// Прерывание от DMA2_Stream1 при приёме из UART6
void DMA2_Stream1_IRQHandler(void)
{
  if (DMA2->LISR & DMA2_LO_TCIF1)   // Работа DMA при приёме окончена
    DMA2->LIFCR |= DMA2_LO_TCIF1;   // Снимаем флаг
  
  if (DMA2->LISR & DMA2_LO_TEIF1)   // Ошибка при приёме
    DMA2->LIFCR |= DMA2_LO_TEIF1;   // Снимаем флаг
  
  ReceiveFromCOM0_DMA();
}


Процедуры приёма и отправки:
Код
void ReceiveFromCOM0_DMA(void)
{
  DMA2_Stream1->CR = 0;
  
  DMA2_Stream1->PAR = (uint32_t) &(USART6->DR); // Указатель на регистр-приёмник
  DMA2_Stream1->M0AR = (uint32_t) COM0_InBuff;         // Указатель на буфер источник
  DMA2_Stream1->NDTR = COM_InBuffSize;                    // Размер передаваемого буфера
  
  // 5 канал (CHSEL=101), Memsize=8bit, Mem increment ON, Per2Mem mode, TCIE ON, TEIE ON,  
  DMA2_Stream1->CR = DMA_SxCR_CHSEL_0 | DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_TCIE | DMA_SxCR_TEIE;
  // Переключаем RS485 на приём
  Tx485_OFF();
  // Начинаем передачу
  DMA2_Stream1->CR |= DMA_SxCR_EN;
}

// Отправить пакет в COM0 с DMA2 (Stream 6/7, канал 5).
void SendToCOM0_DMA(char *Buff, unsigned short Size)
{
  DMA2_Stream1->CR = 0;
  DMA2_Stream6->CR = 0;
  DMA2->LIFCR |= (DMA2_HI_TCIF6 | DMA2_HI_TEIF6);   // Снимаем флаг

  DMA2_Stream6->PAR = (uint32_t) &(USART6->DR); // Указатель на регистр-приёмник
  DMA2_Stream6->M0AR = (uint32_t) Buff;         // Указатель на буфер источник
  DMA2_Stream6->NDTR = Size;                    // Размер передаваемого буфера
  
  // 5 канал (CHSEL=101), Memsize=8bit, Mem increment ON, Mem2Per mode, TCIE ON, TEIE ON,  
  DMA2_Stream6->CR = DMA_SxCR_CHSEL_0 | DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_DIR_0;
  // Переключаем RS485 на передачу
  Tx485_ON();
  // Начинаем передачу
  DMA2_Stream6->CR |= DMA_SxCR_EN;
  
  USART6->CR1 |= USART_CR1_TCIE;
}
athlon64
DMA2_StreamY->CR = 0 заменил на DMA2_StreamY->CR &= ~DMA_SxCR_EN и перед новым чтением с DMA добавил сброс флагов стрима. Вроде зашевелилось.
Рабочий вариант выкладываю, может кому-то пригодится
AHTOXA
Спасибо, навернное пригодится.
Кстати, вот эта вот хитрая конструкция:
Код
  apbclock = 60000000;

  // Считаем делитель для заданного BaudRate
  IntValue = ((apbclock * 25 / 4) / BaudRate);
  Mantissa = (unsigned short int) (IntValue / 100);
  Fraction = (unsigned short int) (IntValue - Mantissa*100);
  Fraction = (Fraction*4)/25;
  USART6->BRR = (Mantissa << 4) | Fraction;

полностью эквивалентна вот такой простой записи:
Код
USART6->BRR = apbclock / BaudRate;

sm.gif
MrYuran
Цитата(AHTOXA @ Apr 27 2012, 09:04) *

Все-таки, наверно лучше с поправкой на целочисленное деление:
Код
USART6->BRR = (apbclock + BaudRate/2) / BaudRate;
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.