278 lines
8.4 KiB
C
278 lines
8.4 KiB
C
/********************************** (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);
|
|
}
|