Polaris 0 4 сентября, 2019 Опубликовано 4 сентября, 2019 · Жалоба Доброго всем времени суток. Столкнулся тут со странным поведением DMA у STM32F446 при использовании DMA2 для комутации GPIO. Необходимость возникла из-за того, что на новой плате не были предусмотрены инвертеры уровня UART, а ведомое устройство в режиме загрузчика их, как оказалось, требует. С STM32F302 проблем бы не было, там есть инверсия уровней, но вот в более быстром и сложном F446 такой инверсии нет, плату переделывать не хотелось, пришлось искать решение в виде софтового UART на DMA. В плане передачи проблем никаких нет, все работает, тайминги и уровни не вызывают никаких вопросов, а вот в плане приема бьюсь уже неделю. Вот так выглядит инициализация DMA: static uint32_t Buff[128]; DMA_DeInit(DMA2_Stream1); DMA_StructInit(&DMA_InitStructure); DMA_InitStructure.DMA_BufferSize = 0; DMA_InitStructure.DMA_Channel = DMA_Channel_7; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&Buff; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOA->IDR); DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_Init(DMA2_Stream1, &DMA_InitStructure); Вот так инициализирую таймер, который будет задавать тактовую частоту (57600): TIM_DeInit(TIM8); TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = TIMER_PERIOD - 1; TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)(SystemCoreClock / 1 / TIMER_FREQ) - 1; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); TIM_GenerateEvent(TIM8, TIM_EventSource_Update); TIM_Cmd(TIM8, ENABLE); TIM_DMACmd(TIM8, TIM_DMA_Update, ENABLE); Частоты однозначно верные, потому что тот же таймер используется для передачи, а там все ок, как в плане приема на стороне прибора, так и на осциллографе. И вот так происходит прием: uint8_t i; uint8_t val = 0; /**< Empty buffer */ memset(Buff, 0, 10); DMA_Cmd(DMA2_Stream1, DISABLE); DMA_ClearFlag(DMA2_Stream1, (DMA_FLAG_TCIF1 | DMA_FLAG_HTIF1 | DMA_FLAG_TEIF1)); /**< Activate edge interrupt */ WaitingRxUART = true; EXTI_ClearITPendingBit(EXTI_Line1); NVIC_EnableIRQ(EXTI1_IRQn); /**< Wait till DMA transmission is ready */ while ((DMA2_Stream1->CR & DMA_SxCR_EN) || (WaitingRxUART == true)); /**< Stop edge interrupt */ NVIC_DisableIRQ(EXTI1_IRQn); DMA_Cmd(DMA2_Stream1, DISABLE); /**< Decode data from DMA buffer */ /**< We are processing 8 data bits, don't know yet why it should be started with Buff[2]? */ for (i = 2; i < 10; i++) { val >>= 1; if ((Buff[i] & (1 << 1)) == 0) val |= 0x80; } При комутации приемопередатчика возникает короткий импульс, который нужно фильтровать, это происходит в прерывании: /**< External interrupt for detecting rising edge of UART packet */ void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) != RESET) { /**< Clear the UART EXTI line pending bit */ EXTI_ClearITPendingBit(EXTI_Line1); /**< Wait 1 us and check input */ TIM10->CNT = 0; TIM10->SR = 0; while ((TIM10->SR & TIM_IT_Update) == 0); if ((GPIOA->IDR & (1 << 1)) == 0) return; /**< Real start bit received, no spike */ /**< Stop edge interrupt */ NVIC_DisableIRQ(EXTI1_IRQn); WaitingRxUART = false; /**< Start DMA GPIO capture */ /**< Receive 10 bits (8 data bits * 2 stop bits) */ TIM8->CR1 &= ~TIM_CR1_CEN; TIM_SetCounter(TIM8, TIM8->ARR / 2); TIM_ClearITPendingBit(TIM8, TIM_IT_Update); TIM8->CR1 |= TIM_CR1_CEN; DMA_SetCurrDataCounter(DMA2_Stream1, 10); DMA_Cmd(DMA2_Stream1, ENABLE); } } Все вроде бы хорошо работает, прием идет, выбросы отфильтровываются, но есть одно но - периодически, причем достаточно часто, происходит сбой, вместо, к примеру 0xAA принимается 0xAB, я посмотрел, что оказывается в буфере, куда собираются значения IDR, и обнаружил там наряду с нормальными значениями состояния входов GPIO пару нулевых значений, причем они идут не первыми и не последними, то есть, буффер выглядит так: 0x9B6C, 0x00, 0x00, 0x9B6C, 0x9B6E, ... Такого не может быть, потому что просто не может быть. Такое впечатление, что канал DMA с чем-то коллидирует и пишет в буфер нули. Может быть, кто-то сталкивался с подобным? Буду рад услышать какие-то советы, потому что голова уже сломалась. На осциллографе данные выглядят нормально (за исключением выбросов), но они точно фильтруются. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Polaris 0 6 сентября, 2019 Опубликовано 6 сентября, 2019 · Жалоба Проблема решена, вопрос был в логике ожидания получения байта: while ((DMA2_Stream1->CR & DMA_SxCR_EN) || (WaitingRxUART == true)) Изменение порядка условий все решило. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться