/********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : Main program body. ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /* *@Note This example demonstrates receiving indeterminate length data via USART's IDLE interrupt and DMA. USART1_Tx(PA9), USART1_Rx(PA10). */ #include "debug.h" #include "string.h" void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); void DMA1_Channel5_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); // ring buffer size #define RING_BUFFER_LEN (1024u) // The length of a single buffer used by DMA #define RX_BUFFER_LEN (128u) #define USART_RX_CH DMA1_Channel5 struct { volatile uint8_t DMA_USE_BUFFER; uint8_t Rx_Buffer[2][RX_BUFFER_LEN]; } USART_DMA_CTRL = { .DMA_USE_BUFFER = 0, .Rx_Buffer = {0}, }; struct { uint8_t buffer[RING_BUFFER_LEN]; volatile uint16_t RecvPos; // volatile uint16_t SendPos; // volatile uint16_t RemainCount; } ring_buffer = {{0}, 0, 0, 0}; /********************************************************************* * @fn USARTx_CFG * * @brief Initializes the USART1 peripheral. * * @return none */ void USARTx_CFG(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStructure = {0}; USART_InitTypeDef USART_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB| RCC_APB2Periph_USART1, ENABLE); /* USART1 TX-->A.9 RX-->A.10 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = baudrate; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_Cmd(USART1, ENABLE); } /********************************************************************* * @fn DMA_INIT * * @brief Configures the DMA for USART1. * * @return none */ void DMA_INIT(void) { DMA_InitTypeDef DMA_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(USART_RX_CH); DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DATAR); DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART_DMA_CTRL.Rx_Buffer[0]; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = RX_BUFFER_LEN; DMA_Init(USART_RX_CH, &DMA_InitStructure); DMA_ITConfig(USART_RX_CH, DMA_IT_TC, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_Cmd(USART_RX_CH, ENABLE); USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); } /********************************************************************* * @fn ring_buffer_push_huge * * @brief Put a large amount of data into the ring buffer. * * @return none */ void ring_buffer_push_huge(uint8_t *buffer, uint16_t len) { const uint16_t bufferRemainCount = RING_BUFFER_LEN - ring_buffer.RemainCount; if (bufferRemainCount < len) { len = bufferRemainCount; } const uint16_t bufferSize = RING_BUFFER_LEN - ring_buffer.RecvPos; if (bufferSize >= len) { memcpy(&(ring_buffer.buffer[ring_buffer.RecvPos]), buffer, len); ring_buffer.RecvPos += len; } else { uint16_t otherSize = len - bufferSize; memcpy(&(ring_buffer.buffer[ring_buffer.RecvPos]), buffer, bufferSize); memcpy(ring_buffer.buffer, &(buffer[bufferSize]), otherSize); ring_buffer.RecvPos = otherSize; } ring_buffer.RemainCount += len; } /********************************************************************* * @fn ring_buffer_pop * * @brief Get a data from the ring buffer. * * @return the Data */ uint8_t ring_buffer_pop() { uint8_t data = ring_buffer.buffer[ring_buffer.SendPos]; ring_buffer.SendPos++; if (ring_buffer.SendPos >= RING_BUFFER_LEN) { ring_buffer.SendPos = 0; } ring_buffer.RemainCount--; return data; } /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Delay_Init(); USART_Printf_Init(115200); USARTx_CFG(115200); DMA_INIT(); printf("SystemClk:%d\r\n", SystemCoreClock); printf("This is an example\r\n"); while (1) { if (ring_buffer.RemainCount > 0) { printf("recv %d >>>\n", ring_buffer.RemainCount); while (ring_buffer.RemainCount > 0) { printf("%c", ring_buffer_pop()); } printf("\n<<<\n"); } Delay_Ms(1000); } } /********************************************************************* * @fn USART1_IRQHandler * * @brief This function handles USART1 global interrupt request. * * @return none */ void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) { // IDLE uint16_t rxlen = (RX_BUFFER_LEN - USART_RX_CH->CNTR); uint8_t oldbuffer = USART_DMA_CTRL.DMA_USE_BUFFER; USART_DMA_CTRL.DMA_USE_BUFFER = !oldbuffer; DMA_Cmd(USART_RX_CH, DISABLE); DMA_SetCurrDataCounter(USART_RX_CH, RX_BUFFER_LEN); // Switch buffer USART_RX_CH->MADDR = (uint32_t)(USART_DMA_CTRL.Rx_Buffer[USART_DMA_CTRL.DMA_USE_BUFFER]); DMA_Cmd(USART_RX_CH, ENABLE); USART_ReceiveData(USART1); // clear IDLE flag ring_buffer_push_huge(USART_DMA_CTRL.Rx_Buffer[oldbuffer], rxlen); } } /********************************************************************* * @fn DMA1_Channel5_IRQHandler * * @brief This function handles DMA1 Channel 5 global interrupt request. * * @return none */ void DMA1_Channel5_IRQHandler(void) { uint16_t rxlen = RX_BUFFER_LEN; uint8_t oldbuffer = USART_DMA_CTRL.DMA_USE_BUFFER; // FULL USART_DMA_CTRL.DMA_USE_BUFFER = !oldbuffer; DMA_Cmd(USART_RX_CH, DISABLE); DMA_SetCurrDataCounter(USART_RX_CH, RX_BUFFER_LEN); // Switch buffer USART_RX_CH->MADDR = (uint32_t)(USART_DMA_CTRL.Rx_Buffer[USART_DMA_CTRL.DMA_USE_BUFFER]); DMA_Cmd(USART_RX_CH, ENABLE); ring_buffer_push_huge(USART_DMA_CTRL.Rx_Buffer[oldbuffer], rxlen); DMA_ClearITPendingBit(DMA1_IT_TC5); }