tonyk_av 43 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба Quote но кажется, что это костыль, возможно есть более элегантное решение Для F4 это правильное решение. Красивое решение называется MAX13487. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба 2 часа назад, Reystlin сказал: Таким образом? Настоятельно рекомендую GPIO-хи настраивать после настройки UART-модуля, а DMA-поток - перед настройкой UART. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 23 июля, 2022 Опубликовано 23 июля, 2022 (изменено) · Жалоба Добавил блокировку возможности отправки сообщения во время получения ответа первый пакет отправляю и получаю нормально после второй отправки на короткое время проваливает PA11 и снова запускается отправка, не дожидаясь приема данных не могу понять в чем косяк почему-то после повторной отправки сразу же срабатывает USART_SR_IDLE не дожидаясь приема... 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 - Ведомый } enum USART_RS485_Status{ USART_IDLE = 0x00, USART_SEND = 0x01, USART_READ = 0x02 }; void (*recive_fn)(uint8_t*); enum USART_RS485_Status status = USART_IDLE; // Принимает строку. enum USART_RS485_Status USART1_put(uint8_t size, char *text, void (*recive)(uint8_t*)) { if(status!=USART_IDLE) return status; 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; DMA2_Stream7->CR |= DMA_SxCR_EN; status = USART_SEND; return 0; }; 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) { 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); USART1->SR &= ~(USART_SR_IDLE); USART1->CR1 |= USART_CR1_IDLEIE; DMA2_Stream5->CR |= DMA_SxCR_EN; GPIOA->ODR&=~(1<<11); // Приём status = USART_READ; } else if((sr & USART_SR_IDLE) && (cr & USART_CR1_IDLEIE)) { // Окончили прием ответа от МК USART1->SR &= ~(USART_SR_IDLE); USART1->CR1 &= ~(USART_CR1_IDLEIE); DMA2_Stream5->CR &= ~(DMA_SxCR_EN); GPIOA->ODR|=(1<<11); // Прием окончен recive_fn(rx_buf); status = USART_IDLE; } } Изменено 23 июля, 2022 пользователем Reystlin Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 43 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба Quote Добавил блокировку возможности отправки сообщения во время получения ответа Зачем? Разреши приём при передаче. Сработал IDLE, значит, передача закончилась и можно переключаться на приём. Попутно проверишь, что принятое совпало с отправленным. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба работаю над реализацией ModBUS поверх RS485, полудуплексная передача по одному каналу. Задумка вот в чем: есть очередь сообщений, кладем в неё сообщения в любых местах программы, основном цикле смотри если есть сообщение в очереди то пытаемся отправить, если передатчик не занят то отправляем(status==USART_IDLE) если занят то продолжаем основной цикл. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 43 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба On 7/23/2022 at 9:54 PM, Reystlin said: Задумка вот в чем: есть очередь сообщений, кладем в неё сообщения в любых местах программы Очень плохая задумка отправлять Модбас-сообщения через очередь. Размер RTU 256 байт, TCP 260. Даже на "жирных" в части ОЗУ МК это очень расточительно. Делай как в ПЛК. Есть регистры, есть объекты синхронизации на запись-чтение регистров, есть задача, обслуживающая Модбас. У задачи есть пул _запросов_, который она заполняет, читая очередь команд. Команды содержат код запроса, его параметры, состояние и уникальный идентификатор запроса. Тогда любая задача может просто добавить запрос в пул запросов, получить его идентификатор, а дальше попросить задачу-обработчик Модбаса непрерывно исполнять запрос, исполнить один, и так далее. Обрати внимание, что доступ к пространству регистров только через объекты синхронизации, поэтому, возможно, для получения минимальных задержек понадобится сделать двойную буферизацию доступа. Ещё не забывай, что каждый запрос на работу с очередью сообщений это вызов ОС, что долго/дорого, особенно с учётом копирований в/из очереди. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба On 7/23/2022 at 8:30 PM, tonyk_av said: Очень плохая задумка отправлять Модбас-сообщения через очередь. Размер RTU 256 байт, TCP 260. Даже на "жирных" в части ОЗУ МК это очень расточительно. Делай как в ПЛК. Есть регистры, есть объекты синхронизации на запись-чтение регистров, есть задача, обслуживающая Модбас. У задачи есть пул _запросов_, который она заполняет, читая очередь команд. Команды содержат код запроса, его параметры, состояние и уникальный идентификатор запроса. Тогда любая задача может просто добавить запрос в пул запросов, получить его идентификатор, а дальше попросить задачу-обработчик Модбаса непрерывно исполнять запрос, исполнить один, и так далее. Обрати внимание, что доступ к пространству регистров только через объекты синхронизации, поэтому, возможно, для получения минимальных задержек понадобится сделать двойную буферизацию доступа. Ещё не забывай, что каждый запрос на работу с очередью сообщений это вызов ОС, что долго/дорого, особенно с учётом копирований в/из очереди. Если я правильно понял, это примерно похоже на то что я и собирался реализовать тем временем нашел проблему, оказывается в IDLE прерывании нужно DR прочитать для его сброса. 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); USART1->SR &= ~(USART_SR_IDLE); USART1->CR1 |= USART_CR1_IDLEIE; DMA2_Stream5->CR |= DMA_SxCR_EN; GPIOA->ODR&=~(1<<11); // Приём status = USART_READ; } else if((sr & USART_SR_IDLE) && (cr & USART_CR1_IDLEIE)) { // Окончили прием ответа от МК USART1->SR &= ~(USART_SR_IDLE); USART1->CR1 &= ~(USART_CR1_IDLEIE); uint8_t dr = USART1->DR; DMA2_Stream5->CR &= ~(DMA_SxCR_EN); GPIOA->ODR|=(1<<11); // Прием окончен recive_fn(rx_buf); status = USART_IDLE; } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба 2 часа назад, Reystlin сказал: USART1->SR &= ~(USART_SR_TC); USART1->SR &= ~(USART_SR_IDLE); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба ну USART1->SR &= ~(USART_SR_IDLE); согласен, лишнее. а с USART1->SR &= ~(USART_SR_TC); что не так? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба 9 минут назад, Reystlin сказал: ...согласен, лишнее... От '&' здесь эффект куда негативнее, чем от лишней строчки, на которую Вы указали. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба как правильно организовать сброс бита в статус регистре тогда? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 23 июля, 2022 Опубликовано 23 июля, 2022 · Жалоба & убрать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 24 июля, 2022 Опубликовано 24 июля, 2022 · Жалоба Там все '|=' и '&=' надо убирать. Во всём коде. Откуда все они это берут??? Ведь все как под копирку лепят эти '|=' и '&=' ??? И никто даже не заглядывает в реф.мануалы. И даже на мгновение не включают голову... В 23.07.2022 в 20:57, Reystlin сказал: const uint32_t sr = USART1->SR, cr = USART1->CR1; А const тут - на кой? И почему: В 23.07.2022 в 18:41, Reystlin сказал: enum USART_RS485_Status status = USART_IDLE; без volatile? И почему: В 23.07.2022 в 18:41, Reystlin сказал: DMA2_Stream7->CR |= DMA_SxCR_EN; status = USART_SEND; установка значения status идёт после старта работы UART, а не до? Также почему recive_fn - тоже без volatile? В 23.07.2022 в 18:41, Reystlin сказал: for(uint8_t i =0;i<size;i++) tx_buf[i]=text[i]; Есть такая функция полезная как memcpy(). Попробуйте её - понравится. В 23.07.2022 в 18:41, Reystlin сказал: DMA2_Stream5->CR = DMA_SxCR_CHSEL_2; // Выбираем 4 канал DMA2_Stream5->CR |= DMA_SxCR_MINC; // Инкремент адреса памяти DMA2_Stream5->CR |= DMA_SxCR_PFCTRL; // DMA - Ведомый Это делается одной операцией, а не 5-ю чтениями/записями. Если конечно не стоит задача сляпать как можно более тормозной и громоздкий код. Также вангую, что не отрабатываются необходимые задержки между предыдущим приёмом и передачей (хотя из этого "кода" этого и не видно). И не понятен смысл манипуляций с ОК-режимом PA.11. И не понятно - зачем читается USART.DR после завершения приёма? PS: Всё не смотрел - только мельком, по диагонали. А то после таких "кодов" изжога начинается... Вобщем - всё переделывать. К нормальному виду. Открыв мануал и учебник по си. В таком виде оно неработопособно. Иначе потом будет ещё одна тема "Почему перестаёт работать при включении оптимизации?" как у другого "деятеля" тут. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 24 июля, 2022 Опубликовано 24 июля, 2022 · Жалоба 17 минут назад, jcxz сказал: А const тут - на кой? Чем он не угодил? Человеку подрезает возможность что-то изменить, машине дает волю оптимизировать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 24 июля, 2022 Опубликовано 24 июля, 2022 · Жалоба В 24.07.2022 в 12:10, Arlleex сказал: Чем он не угодил? Человеку подрезает возможность что-то изменить, машине дает волю оптимизировать. Оптимизатору оно до лампочки: он и так прекрасно видит, что нигде после они не используются на запись. А если уже говорить о человеческой оптимизации, то в первую очередь выкинуть cr из условий в ISR. Ибо не нужен там. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться