Реализовал работу с портом (передача с 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();
}
{
// Тут инициализация пинов и 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 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;
}
{
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;
}