Reystlin 0 21 июля, 2022 Опубликовано 21 июля, 2022 (изменено) · Жалоба Доброго времени суток коллеги! Наблюдаю странное поведение при отправке пакета через USART с использованием DMA. У DMA Настроено прерывание на завершение отправки пакета, в теле этого прерывания происходит опускание ножки PA11. По осциллографу наблюдаю отправку 10 байт а опускание ноги после отправки 8 байта. При изменении длины пакета ситуация со срабатыванием прерывания и опусканием ноги на 2 байта раньше- сохраняется. чип STM32F407, USART1 DMA2 Синий луч PA11 Желтый TX Зеленый RX uint8_t rx_buf[256]; uint8_t tx_buf[256]; void Init_USART1() { // GPIO RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; GPIO_InitTypeDef GPIO_InitStruct_ModBUS = {0}; GPIO_InitStruct_ModBUS.Speed = GPIO_SPEED_FAST; GPIO_InitStruct_ModBUS.Alternate = GPIO_AF7_USART1; // PA9 - DI - TX GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_9; GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_AF_PP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS); // PA10 - RO - RX GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_10; GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_AF_OD; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS); // PA11 - DE/RE - CTS GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_11; GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS); GPIOA->ODR&=~(1<<11); // Приём // USART1 RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // APB2 = 84MHz uint32_t baud_rate = 38400; uint16_t uartdiv = 168000000 / baud_rate; USART1->BRR = (( ( ( uartdiv / 16 ) << USART_BRR_DIV_Mantissa_Pos ) | ( ( uartdiv % 16 ) << USART_BRR_DIV_Fraction_Pos ) ))-2; USART1->CR1 = 0 | USART_CR1_OVER8 // Размер кадра 8 бит | USART_CR1_UE // Включаем USART1 //| USART_CR1_M // Формат кадра Start bit, 8 bits, n Stop bits //| USART_CR1_PCE // Parity mode control disable //| USART_CR1_PS // Parity mode Even //| USART_CR1_PEIE // Разрешение прерывания PEIE ошибка четности //| USART_CR1_TXEIE // Разрешение прерывания TXEIE опустошение регистра передатчика //| USART_CR1_TCIE // Разрешение прерывания TCIE завершение передачи //| USART_CR1_RXNEIE // Разрешение прерывания RXNEIE завершение приема | USART_CR1_IDLEIE // Разрешение прерывания IDLEIE обнаружение сигнала IDLE на линии | USART_CR1_TE // Разрешение передачи | USART_CR1_RE // Разрешение приема //| USART_CR1_RWU // Reciver wakeup //| USART_CR1_SBK // Send break ; USART1->CR2 = 0 // USART_CR2_LINEN // Включение режима LIN | USART_CR2_STOP_1 // 2 Стоп бита //| USART_CR2_STOP_0 //| USART_CR2_CLKEN // Включить вывод CK //| USART_CR2_CPOL // Определяет уровень сигнала на линии CK в отсутствии симпульсов //| USART_CR2_CPHA // Определяет по которому фронту импульсов на линии CK происходи захват //| USART_CR2_LBCL // Нужно ли тактировать через линию CK последний передаваемый бит //| USART_CR2_LBDIE // Разрешение прерывания по обнаружению сигнала break //| USART_CR2_LBDL // Длительность ожидаемого сигнала break ; USART1->CR3 = 0 // USART_CR3_CTSIE // Включение прерывания по линии CTS //USART_CR3_CTSE // Включение контроля линии CTS(Данные начнут передаваться когда CTS = 0) //| USART_CR3_RTSE // Включение контроля линии RTS(Dscnfdkztncz КЕЫ = 0 когда приемник пуст и готов) | USART_CR3_DMAT // Включить режим DMA для передатчика | USART_CR3_DMAR // Включить режим DMA для приемника //| USART_CR3_SCEN // Включить режим Smartcard //| USART_CR3_NACK // Поылать ли NACK в режиме Smartcard //| USART_CR3_HDSEL // Однопроводный полудуплексный режим //| USART_CR3_IRLP // Включение режима low-power для IrDA //| USART_CR3_IREN // Включение режиме IrDA //| USART_CR3_EIE // Включение прерывания по ошибкам передачи ; // DMA2 - Stream7 - Transmit RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; DMA2_Stream7->PAR = (uint32_t)&(USART1->DR); // Адрес назначения DMA2_Stream7->M0AR = (uint32_t)(tx_buf); // Адрес источника DMA2_Stream7->FCR = 0; // Настройка FIFO буфера DMA2_Stream7->CR = DMA_SxCR_CHSEL_2; // Выбираем 4 канал DMA2_Stream7->CR |= DMA_SxCR_MINC; // Инкремент адреса памяти DMA2_Stream7->CR |= DMA_SxCR_DIR_0; // Направление - из памяти в перефереию DMA2_Stream7->CR |= DMA_SxCR_TCIE; // Прерывание по окончанию пересылки пакета NVIC_EnableIRQ(USART1_IRQn); NVIC_SetPriority(USART1_IRQn, 0x04); NVIC_SetPriority(DMA2_Stream7_IRQn, 0x04); NVIC_EnableIRQ(DMA2_Stream7_IRQn); // DMA2 - Stream 5 - Recive DMA2_Stream5->PAR = (uint32_t)&(USART1->DR); // Адрес источника DMA2_Stream5->M0AR = (uint32_t)(rx_buf); // Адрес назначения DMA2_Stream5->FCR = 0; // Настройка FIFO буфера DMA2_Stream5->NDTR = 255; DMA2_Stream5->CR = DMA_SxCR_CHSEL_2; // Выбираем 4 канал DMA2_Stream5->CR |= DMA_SxCR_MINC; // Инкремент адреса памяти DMA2_Stream5->CR |= DMA_SxCR_PFCTRL; // DMA - Ведомый //DMA2_Stream5->CR |= DMA_SxCR_TCIE; DMA2_Stream5->CR |= DMA_SxCR_EN; } void DMA2_Stream7_IRQHandler(void) { if (DMA2->HISR & DMA_HISR_TCIF7) { DMA2->HIFCR = DMA_HIFCR_CTCIF7; // сброс флага события TCIF GPIOA->ODR&=~(1<<11); // Приём } }; // Принимает строку. void USART1_put(uint8_t size, char *text) { for(uint8_t i =0;i<size;i++) tx_buf[i]=text[i]; GPIOA->ODR|=(1<<11); // Передача DMA2_Stream7->CR &= ~DMA_SxCR_EN; // Отключаем поток DMA DMA2_Stream7->NDTR = size; USART1->SR = ~(USART_SR_TC); DMA2_Stream7->CR |= DMA_SxCR_EN; }; void USART1_IRQHandler(void) { if((USART1->SR & USART_SR_IDLE)!= 0) { // Окончили прием ответа от МК } } Чтобы работало отправку делаю вот так void Send_ModBUS_Preset_Single_Register(uint8_t SlaveID, uint16_t reg, uint16_t data) { char msg[8]; msg[0] = SlaveID; msg[1] = Preset_Single_Register; msg[2] = (uint8_t)(reg>>8); msg[3] = (uint8_t)(reg&0xFF); msg[4] = (uint8_t)(data>>8); msg[5] = (uint8_t)(data&0xFF); uint16_t crc = mmodbus_crc16((uint8_t*)msg,6); msg[6]=(uint8_t)(crc&0xFF); msg[7]=(uint8_t)(crc>>8)&0xFF; USART1_put(10, msg); } Изменено 21 июля, 2022 пользователем Reystlin Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Eddy_Em 2 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба Все нормально: ведь USART тоже буферизуется, так что, окончание пересылки через DMA вообще не означает, что USART тоже все передал! Но это и не важно: все равно ведь уже можно запускать следующую транзакцию... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tgruzd 11 21 июля, 2022 Опубликовано 21 июля, 2022 (изменено) · Жалоба Ничего странного в этом поведении нет. Дма запихивает последний байт в регистр уарта и с его точки зрения - "трансмишн комплит". Почему именно два байта: регистр DR - это на самом деле два регистра, буферный и сдвиговый. Изменено 21 июля, 2022 пользователем tgruzd Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба какое тогда прерывание верно ловить чтобы поймать нужное событие? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tgruzd 11 21 июля, 2022 Опубликовано 21 июля, 2022 (изменено) · Жалоба Зависит от того, какое событие вам нужно) Возможно, TC уарта Изменено 21 июля, 2022 пользователем tgruzd Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 21 июля, 2022 Опубликовано 21 июля, 2022 · Жалоба void DMA2_Stream7_IRQHandler(void) { if (DMA2->HISR & DMA_HISR_TCIF7) { DMA2->HIFCR = DMA_HIFCR_CTCIF7; // сброс флага события TCIF USART1->SR = ~(USART_SR_TC); USART1->CR1 |= USART_CR1_TCIE; // Разрешение прерывания TCIE завершение передачи } }; void USART1_IRQHandler(void) { if((USART1->SR & USART_SR_TC)!= 0) { // Окончили отправку пакета USART1->CR1 &= ~USART_CR1_TCIE; USART1->SR = ~(USART_SR_TC); GPIOA->ODR&=~(1<<11); // Приём } else if((USART1->SR & USART_SR_IDLE)!= 0) { // Окончили прием ответа от МК USART1->SR = ~(USART_SR_IDLE); GPIOA->ODR|=(1<<11); // Прием окончен recive_fn(rx_buf); } } Сделал вот так, получил что нужно, но кажется, что это костыль, возможно есть более элегантное решение Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба Элегантного решения вряд ли найдется, ибо события UART-модуля отвязаны от событий DMA. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_G 11 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба Вообще-то ПОЛНОЕ завершение передачи идет с флагом TXE - Transmit Data Register Empty, который выставляется после флага TC и опустошения буфера передатчика Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба 2 минуты назад, V_G сказал: Вообще-то ПОЛНОЕ завершение передачи идет с флагом TXE - Transmit Data Register Empty, который выставляется после флага TC и опустошения буфера передатчика Нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
V_G 11 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба В 22.07.2022 в 16:38, Arlleex сказал: Нет. Пардон, действительно перепутал флаги TC и TXE. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба 11 часов назад, Reystlin сказал: Сделал вот так, получил что нужно, но кажется, что это костыль, возможно есть более элегантное решение Проверку флагов в UART-обработчике еще бы правильно сделать, на всякий случай. Мало ли проект разрастется и флагов прибавится... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба On 7/22/2022 at 12:06 PM, Arlleex said: Проверку флагов в UART-обработчике еще бы правильно сделать, на всякий случай. Мало ли проект разрастется и флагов прибавится... Как бы вы порекомендовали сделать правильнее? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба Наряду с проверкой флага в SR проверять еще и факт разрешения самого прерывания void USART1_IRQHandler(void) { const u32 sr = USART1->SR, cr = USART1->CR1; if(sr & USART_SR_TC && cr & USART_CR1_TCIE) { ... } ... } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 143 22 июля, 2022 Опубликовано 22 июля, 2022 · Жалоба 13 часов назад, Reystlin сказал: возможно есть более элегантное решение Ну, можно сбрасывать TC и разрешать его прерывание перед началом отправки, тогда обработчик DMA_TCIF не понадобится (его действия можно перенести в обработчик TC). Но это если вы уверены, что ПДП натравливается на весь пакет целиком. У меня такое бывает редко, ибо практически всегда использую кольцевые буфера, а там пакет может начинаться в конце буфера, а заканчиваться в начале. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба Таким образом? void Init_USART1() { // GPIO RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; GPIO_InitTypeDef GPIO_InitStruct_ModBUS = {0}; GPIO_InitStruct_ModBUS.Speed = GPIO_SPEED_FAST; GPIO_InitStruct_ModBUS.Alternate = GPIO_AF7_USART1; // PA9 - DI - TX GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_9; GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_AF_PP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS); // PA10 - RO - RX GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_10; GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_AF_OD; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS); // PA11 - DE/RE - CTS GPIO_InitStruct_ModBUS.Pin = GPIO_PIN_11; GPIO_InitStruct_ModBUS.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct_ModBUS); GPIOA->ODR&=~(1<<11); // Приём // USART1 RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // APB2 = 84MHz uint32_t baud_rate = 38400; uint16_t uartdiv = 168000000 / baud_rate; USART1->BRR = (( ( ( uartdiv / 16 ) << USART_BRR_DIV_Mantissa_Pos ) | ( ( uartdiv % 16 ) << USART_BRR_DIV_Fraction_Pos ) ))-2; USART1->CR1 = 0 | USART_CR1_OVER8 // Размер кадра 8 бит | USART_CR1_UE // Включаем USART1 //| USART_CR1_M // Формат кадра Start bit, 8 bits, n Stop bits //| USART_CR1_PCE // Parity mode control disable //| USART_CR1_PS // Parity mode Even //| USART_CR1_PEIE // Разрешение прерывания PEIE ошибка четности //| USART_CR1_TXEIE // Разрешение прерывания TXEIE опустошение регистра передатчика //| USART_CR1_TCIE // Разрешение прерывания TCIE завершение передачи //| USART_CR1_RXNEIE // Разрешение прерывания RXNEIE завершение приема | USART_CR1_IDLEIE // Разрешение прерывания IDLEIE обнаружение сигнала IDLE на линии | USART_CR1_TE // Разрешение передачи | USART_CR1_RE // Разрешение приема //| USART_CR1_RWU // Reciver wakeup //| USART_CR1_SBK // Send break ; USART1->CR2 = 0 // USART_CR2_LINEN // Включение режима LIN | USART_CR2_STOP_1 // 2 Стоп бита //| USART_CR2_STOP_0 //| USART_CR2_CLKEN // Включить вывод CK //| USART_CR2_CPOL // Определяет уровень сигнала на линии CK в отсутствии симпульсов //| USART_CR2_CPHA // Определяет по которому фронту импульсов на линии CK происходи захват //| USART_CR2_LBCL // Нужно ли тактировать через линию CK последний передаваемый бит //| USART_CR2_LBDIE // Разрешение прерывания по обнаружению сигнала break //| USART_CR2_LBDL // Длительность ожидаемого сигнала break ; USART1->CR3 = 0 // USART_CR3_CTSIE // Включение прерывания по линии CTS //USART_CR3_CTSE // Включение контроля линии CTS(Данные начнут передаваться когда CTS = 0) //| USART_CR3_RTSE // Включение контроля линии RTS(Dscnfdkztncz КЕЫ = 0 когда приемник пуст и готов) | USART_CR3_DMAT // Включить режим DMA для передатчика | USART_CR3_DMAR // Включить режим DMA для приемника //| USART_CR3_SCEN // Включить режим Smartcard //| USART_CR3_NACK // Поылать ли NACK в режиме Smartcard //| USART_CR3_HDSEL // Однопроводный полудуплексный режим //| USART_CR3_IRLP // Включение режима low-power для IrDA //| USART_CR3_IREN // Включение режиме IrDA //| USART_CR3_EIE // Включение прерывания по ошибкам передачи ; // DMA2 - Stream7 - Transmit RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; DMA2_Stream7->PAR = (uint32_t)&(USART1->DR); // Адрес назначения DMA2_Stream7->M0AR = (uint32_t)(tx_buf); // Адрес источника DMA2_Stream7->FCR = 0; // Настройка FIFO буфера DMA2_Stream7->CR = DMA_SxCR_CHSEL_2; // Выбираем 4 канал DMA2_Stream7->CR |= DMA_SxCR_MINC; // Инкремент адреса памяти DMA2_Stream7->CR |= DMA_SxCR_DIR_0; // Направление - из памяти в перефереию DMA2_Stream7->CR |= DMA_SxCR_TCIE; // Прерывание по окончанию пересылки пакета NVIC_EnableIRQ(USART1_IRQn); NVIC_SetPriority(USART1_IRQn, 0x04); NVIC_SetPriority(DMA2_Stream7_IRQn, 0x04); NVIC_EnableIRQ(DMA2_Stream7_IRQn); // DMA2 - Stream 5 - Recive DMA2_Stream5->PAR = (uint32_t)&(USART1->DR); // Адрес источника DMA2_Stream5->M0AR = (uint32_t)(rx_buf); // Адрес назначения DMA2_Stream5->FCR = 0; // Настройка FIFO буфера DMA2_Stream5->NDTR = 255; DMA2_Stream5->CR = DMA_SxCR_CHSEL_2; // Выбираем 4 канал DMA2_Stream5->CR |= DMA_SxCR_MINC; // Инкремент адреса памяти DMA2_Stream5->CR |= DMA_SxCR_PFCTRL; // DMA - Ведомый //DMA2_Stream5->CR |= DMA_SxCR_TCIE; DMA2_Stream5->CR |= DMA_SxCR_EN; } void DMA2_Stream7_IRQHandler(void) { if (DMA2->HISR & DMA_HISR_TCIF7) { DMA2->HIFCR = DMA_HIFCR_CTCIF7; // сброс флага события TCIF // USART1->SR = ~(USART_SR_TC); // USART1->CR1 |= USART_CR1_TCIE; // Разрешение прерывания TCIE завершение передачи } }; void (*recive_fn)(uint8_t*); // Принимает строку. void USART1_put(uint8_t size, char *text, void (*recive)(uint8_t*)) { recive_fn = recive; for(uint8_t i =0;i<size;i++) tx_buf[i]=text[i]; GPIOA->ODR|=(1<<11); // Передача DMA2_Stream7->CR &= ~DMA_SxCR_EN; // Отключаем поток DMA DMA2_Stream7->NDTR = size; USART1->SR = ~(USART_SR_TC); USART1->CR1 |= USART_CR1_TCIE; // Разрешение прерывания TCIE завершение передачи DMA2_Stream7->CR |= DMA_SxCR_EN; }; void USART1_IRQHandler(void) { const uint32_t sr = USART1->SR, cr = USART1->CR1; if((sr & USART_SR_TC) && (cr & USART_CR1_TCIE)) { // Окончили отправку пакета USART1->CR1 &= ~USART_CR1_TCIE; USART1->SR &= ~(USART_SR_TC); GPIOA->ODR&=~(1<<11); // Приём } else if((sr & USART_SR_IDLE) && (cr & USART_CR1_IDLEIE)) { // Окончили прием ответа от МК USART1->SR &= ~(USART_SR_IDLE); GPIOA->ODR|=(1<<11); // Прием окончен recive_fn(rx_buf); } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться