Arlleex 190 24 июля, 2022 Опубликовано 24 июля, 2022 · Жалоба 32 минуты назад, jcxz сказал: Оптимизатору оно до лампочки: он и так прекрасно видит, что нигде после они не используются на запись. За последние лет 10 я видел миллион мнений на этот счет, с примерами и контрпримерами. Пусть даже компиляторы умные, но код пишет человек, и он в силу своей человечности может случайно что-то присвоить такой переменной, например, при копипасте куска кода. А потом ловить баги на ровном месте. Цитата ...в первую очередь выкинуть cr из условий в ISR. Ибо не нужен там. А Вы уверены, что завтра ТС не потребуется добавить в ISR обработку, например, ошибок принимаемых символов (да или просто сам прием символов по RXNE) и Ваш совет не сломает ему код? Я, например, вижу, что ТС как бы и не в курсе о потенциальнх багах с обработкой флагов UART-а, поэтому и посоветовал сразу безопасное решение. Хотя вот конкретно сейчас да, проверять cr не нужно, ибо проблемы могут быть при обработке TXE. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 24 июля, 2022 Опубликовано 24 июля, 2022 · Жалоба В 24.07.2022 в 12:32, Arlleex сказал: добавить в ISR обработку, например, ошибок принимаемых символов (да или просто сам прием символов по RXNE) и Ваш совет не сломает ему код? Каким образом обработка ошибок UART связана с чтением CR? В STM32 для этого вроде достаточно SR и DR. А насчёт приёма символов в ISR: так он и тут зачем-то читает DR в ISR. И это при том, что UART у него при этом находится в режиме работы с DMA! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 24 июля, 2022 Опубликовано 24 июля, 2022 · Жалоба 19 часов назад, jcxz сказал: Каким образом обработка ошибок UART связана с чтением CR? В STM32 для этого вроде достаточно SR и DR. С этими флагами я погорячился, признаю. Но, тем не менее, это точно касается флага TXE, который нельзя сбросить кроме как записью в DR. Соответственно, недостаточно лишь его проверки в SR ISR. Иначе при возникновении другого события, например, RXNE или ошибок приема, и при пустом регистре передатчика if(sr & USART_SR_TXE) запустит ветку на исполнение, что может быть совершенно не правильным для драйвера. Цитата А насчёт приёма символов в ISR: так он и тут зачем-то читает DR в ISR. И это при том, что UART у него при этом находится в режиме работы с DMA! Да, казалось бы, есть ошибка. Но нет. К сожалению, этот момент довольно хреново описан в RM, т.к. сброс флагов IDLE и всех ошибок (FE, ORE и т.д.) осуществляется строго определенной последовательностью: чтение SR, затем чтение DR. Я по-моему даже, когда разбирался с DMA + UART в F4, проверял этот момент, и действительно там флаги не чистились при других последовательностях. К тому же, когда ISR срабатывает по IDLE, DMA уже все вычитал в память и новых пересылок из UART-периферии не осуществляет (если, правда, прием снова не начался). Глянул в свой драйвер (правда, там я обрабатываю только ошибки) - тоже читаю DR после SR void cUARTDMARx::svcOnTmrEvt() { if(uartCtrl.hw->CR1 & USART_CR1_RE) { const u32 bufNum = this->bufNum, ndtr = dmaCtrl.hw.str->NDTR; if(uartCtrl.hw->SR & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE)) uartCtrl.hw->DR, isTrash = true; if(!isOvf && !isTrash) { const u32 wpos = this->wpos, i = bufNum * FIFO_RXBUF_LEN, j = i + FIFO_RXBUF_LEN - ndtr; if((s32)(j - wpos) > 0 || wpos - i > FIFO_RXBUF_LEN) this->wpos = j == FIFO_RXBUF_SIZE ? 0 : j, ledOn(); } } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 24 июля, 2022 Опубликовано 24 июля, 2022 · Жалоба Почитал я повнимательнее, ради спортивного интереса, раздел USART в STM32F405. Вон чо пишут в разделе коопераций с DMA Цитата When the number of data transfers programmed in the DMA Controller is reached, the DMA controller generates an interrupt on the DMA channel interrupt vector. The DMAR bit should be cleared by software in the USART_CR3 register during the interrupt subroutine. Видимо, обратное тоже здесь применимо: когда выполняется обработчик ISR USART-модуля, DMAR (или DMAT) должен быть очищен. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 26 июля, 2022 Опубликовано 26 июля, 2022 · Жалоба как тогда правильно устанавливать и сбрасывать биты в регистрах без чтения их значения? с GPIO понятно, там BRR есть. а остальные как? к примеру вот тут DMA2_Stream5->CR &= ~(DMA_SxCR_EN); чтобы правильное значение в регистре получить нужно считать его значение и умножить на инвертированную маску необходимого бита для опустошения.... какие ещё есть способы? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 26 июля, 2022 Опубликовано 26 июля, 2022 · Жалоба В 26.07.2022 в 23:23, Reystlin сказал: чтобы правильное значение в регистре получить нужно считать его значение Зачем? Всё элементарно: DMA2_Stream5->CR = 0; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 26 июля, 2022 Опубликовано 26 июля, 2022 · Жалоба При этом сбросятся все установленные настройки в нем... и при каждом включении нужно будет все биты настроек выставлять, либо какую-то инлайновую функцию включения/отключения делать Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 26 июля, 2022 Опубликовано 26 июля, 2022 · Жалоба В 26.07.2022 в 23:34, Reystlin сказал: При этом сбросятся все установленные настройки в нем... А зачем они при выключенном stream-е? В 26.07.2022 в 23:34, Reystlin сказал: и при каждом включении нужно будет все биты настроек выставлять И что? Будет DMA2_Stream5->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_PFCTRL | DMA_SxCR_EN; - это всего 2 ассемблерные команды. Ваш паровоз: 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; это - как минимум 11 команд. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 26 июля, 2022 Опубликовано 26 июля, 2022 · Жалоба При выключенном, конечно, не нужны. но его же нужно будет включить. этот "паравоз" я привел вот к такому виду: DMA2_Stream5->CR = 0 | DMA_SxCR_CHSEL_2 // Выбираем 4 канал | DMA_SxCR_MINC // Инкремент адреса памяти | DMA_SxCR_PFCTRL // DMA - Ведомый При каждом включении необходимо будет вставить этот кусок кода, затрудняя возможность внесения изменений в инициализацию, через пол года можно забыть в каких местах в коде включение работы DMA происходит, а если этот код другому человеку поддеживать придется? стараюсь всегда всю конфигурацию вынести в одно место а не размазывать по коду без крайней необходимости С точки зрения эффективности со стороны работы процессора да - ваша рекомендация хороша, не спорю. с точки зрения поддержки кода не совсем... кроме инлайн функции не вижу другого выхода из ситуации, чтобы оптимизировать код со стороны работы процессора и не потерять его гибкость изменения и поддержки. В любом случае, спасибо за вашу помощь и советы Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 143 26 июля, 2022 Опубликовано 26 июля, 2022 · Жалоба 1 час назад, Reystlin сказал: При каждом включении необходимо будет вставить этот кусок кода Занесите это значение в константу, потом присваивайте ее значение. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 26 июля, 2022 Опубликовано 26 июля, 2022 · Жалоба On 7/27/2022 at 1:23 AM, Сергей Борщ said: Занесите это значение в константу, потом присваивайте ее значение. Хороший вариант, спасибо, об константе не подумал) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 45 27 июля, 2022 Опубликовано 27 июля, 2022 · Жалоба On 7/27/2022 at 1:23 AM, Reystlin said: с GPIO понятно, там BRR есть. а остальные как? Внимательно читать RM. Много периферии, у которой флаги сбрасываются при чтении. Есть периферия, тот же UART в разных МК, у которых есть регистры на запись для сброса бит. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 2 августа, 2022 Опубликовано 2 августа, 2022 · Жалоба Продолжаю изыскания по организации общения через ModBUS поверх RS485 на текущий момент вот такой код: #include "USART1.h" #include <stdio.h> #include <string.h> uint8_t rx_buf[256]; uint8_t tx_buf[256]; #define TIM7_En() TIM7->CR1 = TIM_CR1_OPM | TIM_CR1_CEN; // Таймер задержки отправки следующего пакета #define DMA2_Stream5_En() DMA2_Stream5->CR = DMA_SxCR_EN | DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_PFCTRL // DMA на прием #define DMA2_Stream7_En() DMA2_Stream7->CR = DMA_SxCR_EN | DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE // DMA на отправку void Init_USART1() { // 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 = 0 | DMA_SxCR_CHSEL_2 // Выбираем 4 канал | DMA_SxCR_MINC // Инкремент адреса памяти | DMA_SxCR_DIR_0 // Направление - из памяти в перефереию | 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 = 0 | DMA_SxCR_CHSEL_2 // Выбираем 4 канал | DMA_SxCR_MINC // Инкремент адреса памяти | DMA_SxCR_PFCTRL // DMA - Ведомый ;*/ //TIM7 - Таймер для Timeout и задержек RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; //TIM7->CR1 = TIM_CR1_OPM; // Однократный режим TIM7->PSC=84-1; TIM7->ARR=500-1; TIM7->EGR |= TIM_EGR_UG; TIM7->SR=0; TIM7->DIER = TIM_DIER_UIE; // Разрешение прерывания NVIC_EnableIRQ(TIM7_IRQn); // 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->BSRR = GPIO_BSRR_BS11; // Передача } void (*recive_fn)(uint8_t*); volatile 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) { status = USART_SEND; recive_fn = recive; memcpy(tx_buf, text, size); GPIOA->BSRR = GPIO_BSRR_BS11; // Передача DMA2_Stream7->NDTR = size; DMA2_Stream7_En(); } return status; }; 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->SR = ~(USART_SR_TC); DMA2_Stream7->CR = 0; DMA2_Stream5_En(); GPIOA->BSRR = GPIO_BSRR_BR11; // Прием status = USART_READ; // Настройка таймера на Timeout TIM7->ARR = 5000-1; // Задержка отправки второго пакета после приема TIM7_En(); // Включаем счетчик для задержки отправки второго пакета } else if((sr & USART_SR_IDLE) && (cr & USART_CR1_IDLEIE)) { // Окончили прием ответа (void)USART1->DR; USART1->SR = ~(USART_SR_IDLE); DMA2_Stream5->CR = 0; GPIOA->BSRR = GPIO_BSRR_BR11; // Прием окончен status = USART_WAIT; TIM7->ARR = 500-1; // Задержка отправки второго пакета после приема TIM7_En(); // Включаем счетчик для задержки отправки второго пакета recive_fn(rx_buf); } } void TIM7_IRQHandler(void) { TIM7->SR = ~(TIM_SR_UIF); if(status == USART_READ) { // Timeout status = USART_IDLE; } else { // Окончили получение и задержку status = USART_IDLE; } } Slave устройство не подключено. Желтый TX Зеленёый RX Синий PA11 https://disk.yandex.ru/i/vkatL_l1n5cT2A не могу понять в чем дело почему на PA11 меняется сигнал во время отправки одного целого пакета Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 2 августа, 2022 Опубликовано 2 августа, 2022 · Жалоба Вряд ли кто-то кроме Вас скажет, почему у Вас дергается нога PA11. Из приведенных исходников этого не видно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 2 августа, 2022 Опубликовано 2 августа, 2022 · Жалоба Нашел ошибку. вот здесь GPIOA->BSRR = GPIO_BSRR_BR11; // Прием окончен надо было GPIOA->BSRR = GPIO_BSRR_BS11; // Прием окончен Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться