HC32F4A0_FreeRTOS_LwIP移植 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
本篇使用HC32F4A0的串口1和LwIP协议栈实现串口服务器的功能。已经实现了TCP回显服务器,那么在这个基础上添加串口相关代码,使数据能双向透传,实现简易串口服务器的功能。
工作流程分为两块,一个是串口到网络,另一个是网络到串口。这里使用串口1中断接收数据,通过netconn_write API传输给TCP客户端;TCP客户端发送数据到服务器,接收到数据后,通过串口发送出去。理清思路,下面开始实现。
首先实现网络到串口。先创建一个TCP服务器( sys_thread_new( "Uart_Server", xTaskUARTServer, NULL, 512, 4 ) ),建立新的连接后,进入网络数据接收环节,当服务器有数据进来时,我们调用写好的串口中断发送函数,将数据发送出去,我们在串口端就可以接收到相关数据。以下是服务器代码,串口发送函数,将在后面的串口内容中说明。
/* ---创建Tcp串口服务器--- */
sys_thread_new( "Uart_Server", xTaskUARTServer, NULL, 512, 4 );
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] xTaskUARTServer
* [url=home.php?mod=space&uid=159083]@brief[/url] 串口服务器
* @param pvParameters: [输入/出]
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-03-28
**************************************************************/
static void xTaskUARTServer( void *pvParameters )
{
err_t err = ERR_OK;
struct netbuf *recvbuf;
void *data = NULL;
uint16_t len = 0;
tcpconnUart = netconn_new( NETCONN_TCP );
netconn_bind( tcpconnUart, IPADDR_ANY, 5001 );
netconn_listen( tcpconnUart );
for( ;; )
{
err = netconn_accept( tcpconnUart, &newconnUart );
COM1_Info.RxCnt = 0;
if( err == ERR_OK )
{
for( ;; )
{
if( ( err = netconn_recv( newconnUart, &recvbuf ) ) == ERR_OK )
{
do
{
netbuf_data( recvbuf, &data, &len ); /* 搬运收到的数据 */
USART1_SendData_UseIT( data, len ); /* 串口中断发送数据 */
} while( netbuf_next( recvbuf ) >= 0 );
netbuf_delete( recvbuf );
COM1_Info.RxCnt = 0;
}
else
{
netconn_close( newconnUart );
while( ERR_OK != netconn_delete( newconnUart ) )
{
vTaskDelay( 1 );
}
break;
}
}
}
vTaskDelay( 10 );
}
}
接着实现串口到网络。基本思路是串口通过中断接收数据,接收完成后,通过LwIP相关函数(netconn_write),发送出去,在TCP客户端可以接收到发送的数据。以往,我都是通过串口空闲中断来判断一帧数据的接收完成。但是,写程序时发现,HC32F4A0这个单片机没有串口空闲中断这个功能,取而代之的是一个叫串口接收超时中断的功能。在这里忍不住想要吐槽两句,这个串口空闲中断的功能怎么能没有呢,接收超时功能只有4个串口(1、2、6、7)可以用,而且要用到相关定时器0。接收超时功能与定时器的对应情况,如下图所示。
以下是定时器0的配置代码,这里面的超时时间,根据不同的时钟源、分频系数和比较直来设置。这个定时器初始化函数,在串口初始化函数中调用。
/**************************************************************
* @Name bsp_TimerForUSART1_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
static void bsp_TimerForUSART1_Init( void )
{
stc_tmr0_init_t stcTmr0_InitStruct;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
FCG_Fcg2PeriphClockCmd( FCG2_PERIPH_TMR0_1, ENABLE );
TMR0_DeInit( CM_TMR0_1 );
TMR0_SetCountValue( CM_TMR0_1, TMR0_CH_A, 0 );
stcTmr0_InitStruct.u32Func = TMR0_FUNC_CMP;
stcTmr0_InitStruct.u32ClockSrc = TMR0_CLK_SRC_INTERN_CLK; /* ---PLCK1:120MHz--- */
stcTmr0_InitStruct.u32ClockDiv = TMR0_CLK_DIV32;
stcTmr0_InitStruct.u16CompareValue = 330;
TMR0_Init( CM_TMR0_1, TMR0_CH_A, &stcTmr0_InitStruct );
TMR0_HWStartCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
TMR0_HWClearCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
}
下面是串口初始化函数,基本参数初始化后,配置了3个中断,分别是接收中断、发送中断和超时接收中断。
/**************************************************************
* @Name bsp_Usart_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
void bsp_Usart_Init( void )
{
stc_usart_uart_init_t Usart_InitStruct;
stc_irq_signin_config_t stcIrqSigninConfig;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---解保护--- */
/* Configure USART RX/TX pin. */
GPIO_SetFunc( USART1_RX_PORT, USART1_RX_PIN, GPIO_FUNC_33 );
GPIO_SetFunc( USART1_TX_PORT, USART1_TX_PIN, GPIO_FUNC_32 );
USART1_FCG_ENABLE(); /* ---使能串口1功能时钟--- */
USART_UART_StructInit(&Usart_InitStruct);
Usart_InitStruct.u32ClockSrc = USART_CLK_SRC_INTERNCLK; /* ---选择内部时钟--- */
Usart_InitStruct.u32ClockDiv = USART_CLK_DIV64; /* ---时钟64分频--- */
Usart_InitStruct.u32CKOutput = USART_CK_OUTPUT_DISABLE; /* ---不输出时钟--- */
Usart_InitStruct.u32Baudrate = 115200UL;
Usart_InitStruct.u32Parity = USART_PARITY_NONE;
Usart_InitStruct.u32DataWidth = USART_DATA_WIDTH_8BIT;
Usart_InitStruct.u32StopBit = USART_STOPBIT_1BIT;
Usart_InitStruct.u32FirstBit = USART_FIRST_BIT_LSB; /* ---LSB线性--- */
Usart_InitStruct.u32HWFlowControl = USART_HW_FLOWCTRL_NONE;
Usart_InitStruct.u32StartBitPolarity = USART_START_BIT_LOW; /* ---起始位为低--- */
Usart_InitStruct.u32OverSampleBit = USART_OVER_SAMPLE_8BIT; /* ---8倍过采样--- */
if ( LL_OK != USART_UART_Init( CM_USART1, &Usart_InitStruct, NULL ) )
{
for (;;)
{
}
}
stcIrqSigninConfig.enIRQn = INT003_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_RxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT001_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_TI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_TxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT002_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RTO; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_IdleCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
USART_FuncCmd( CM_USART1, ( USART_TX | USART_RX | USART_INT_RX ), ENABLE ); /* ---打开发送接收功能、中断接收功能--- */
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
USART_FuncCmd( CM_USART1, USART_INT_RX_TIMEOUT | USART_RX_TIMEOUT, ENABLE );
COM1_Info.pRxBuf = RxBuf;
COM1_Info.pTxBuf = TxBuf;
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---上保护--- */
bsp_TimerForUSART1_Init();
}
下面是bsp_Usart.h、bsp_Usart.c中的内容:
/**
************************************* Copyright ******************************
* (C) Copyright 2022,Zachray,HPU, China.
* All Rights Reserved
*
* FileName : bsp_Usart.h
* Version : v1.0
* Author : Zachary
* Date : 2023-02-15
* Description: This is the first edition
---Zachary,in Zhejiang Hanpu.
******************************************************************************
*/
#ifndef __BSP_USART_H_
#define __BSP_USART_H_
/* Includes --------------------------------------------------------------*/
#include "main.h"
/* --- Define ------------------------------------------------------------*/
#define USART1_FCG_ENABLE() FCG_Fcg3PeriphClockCmd( FCG3_PERIPH_USART1, ENABLE )
#define USART1_TX_PIN GPIO_PIN_15
#define USART1_TX_PORT GPIO_PORT_H
#define USART1_RX_PIN GPIO_PIN_13
#define USART1_RX_PORT GPIO_PORT_H
/* --- Typedef -----------------------------------------------------------*/
typedef struct
{
uint8_t TxLen;
uint8_t TxCnt;
uint8_t *pTxBuf;
uint8_t RxLen;
uint8_t RxCnt;
uint8_t *pRxBuf;
} COM_Typedef;
/* --- Variables ---------------------------------------------------------*/
extern COM_Typedef COM1_Info;
/* --- Functions ---------------------------------------------------------*/
void bsp_Usart_Init( void );
void USART1_SendData_UseIT( uint8_t *pBuf, uint8_t pLen );
/*------------------------------------------------------------------------*/
#endif /* __BSP_USART_H_ */
/**
************************************* Copyright ******************************
*
* (C) Copyright 2022,Zachary ,HPU, China.
* All Rights Reserved
*
* FileName : bsp_Usart.c
* Version : v1.0
* Author : Zachary
* Date : 2023-02-13
* Description:
******************************************************************************
*/
/* Includes --------------------------------------------------------------*/
#include <stdio.h>
#include "bsp_Usart.h"
#include "delay.h"
#include "os_includes.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
uint8_t TxBuf[250];
uint8_t RxBuf[250];
COM_Typedef COM1_Info;
/* --- Functions ---------------------------------------------------------*/
static void USART1_RxCpltCallback( void );
static void USART1_TxCpltCallback( void );
static void USART1_IdleCallback( void );
static void bsp_TimerForUSART1_Init( void );
/*------------------------------------------------------------------------*/
/**************************************************************
* @Name bsp_Usart_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
void bsp_Usart_Init( void )
{
stc_usart_uart_init_t Usart_InitStruct;
stc_irq_signin_config_t stcIrqSigninConfig;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---解保护--- */
/* Configure USART RX/TX pin. */
GPIO_SetFunc( USART1_RX_PORT, USART1_RX_PIN, GPIO_FUNC_33 );
GPIO_SetFunc( USART1_TX_PORT, USART1_TX_PIN, GPIO_FUNC_32 );
USART1_FCG_ENABLE(); /* ---使能串口1功能时钟--- */
USART_UART_StructInit(&Usart_InitStruct);
Usart_InitStruct.u32ClockSrc = USART_CLK_SRC_INTERNCLK; /* ---选择内部时钟--- */
Usart_InitStruct.u32ClockDiv = USART_CLK_DIV64; /* ---时钟64分频--- */
Usart_InitStruct.u32CKOutput = USART_CK_OUTPUT_DISABLE; /* ---不输出时钟--- */
Usart_InitStruct.u32Baudrate = 115200UL;
Usart_InitStruct.u32Parity = USART_PARITY_NONE;
Usart_InitStruct.u32DataWidth = USART_DATA_WIDTH_8BIT;
Usart_InitStruct.u32StopBit = USART_STOPBIT_1BIT;
Usart_InitStruct.u32FirstBit = USART_FIRST_BIT_LSB; /* ---LSB线性--- */
Usart_InitStruct.u32HWFlowControl = USART_HW_FLOWCTRL_NONE;
Usart_InitStruct.u32StartBitPolarity = USART_START_BIT_LOW; /* ---起始位为低--- */
Usart_InitStruct.u32OverSampleBit = USART_OVER_SAMPLE_8BIT; /* ---8倍过采样--- */
if ( LL_OK != USART_UART_Init( CM_USART1, &Usart_InitStruct, NULL ) )
{
for (;;)
{
}
}
stcIrqSigninConfig.enIRQn = INT003_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_RxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT001_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_TI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_TxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT002_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RTO; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_IdleCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
USART_FuncCmd( CM_USART1, ( USART_TX | USART_RX | USART_INT_RX ), ENABLE ); /* ---打开发送接收功能、中断接收功能--- */
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
USART_FuncCmd( CM_USART1, USART_INT_RX_TIMEOUT | USART_RX_TIMEOUT, ENABLE );
COM1_Info.pRxBuf = RxBuf;
COM1_Info.pTxBuf = TxBuf;
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---上保护--- */
bsp_TimerForUSART1_Init();
}
/**************************************************************
* @Name USART1_IdleCallback
* @brief 超时功能用作空闲中断
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
extern TaskHandle_t xTaskHandleTask2;
static void USART1_IdleCallback( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( SET == USART_GetStatus( CM_USART1, USART_FLAG_RX_TIMEOUT ) )
{
if( xTaskHandleTask2 != NULL )
{
vTaskNotifyGiveFromISR( xTaskHandleTask2, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
TMR0_Stop( CM_TMR0_1, TMR0_CH_A );
USART_ClearStatus( CM_USART1, USART_FLAG_RX_TIMEOUT );
}
}
/**************************************************************
* @Name USART1_RxCpltCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-20
**************************************************************/
static void USART1_RxCpltCallback( void )
{
uint8_t u8Data = 0x0;
if( USART_GetStatus( CM_USART1, USART_FLAG_RX_FULL ) != RESET )
{
u8Data = ( uint8_t )USART_ReadData( CM_USART1 );
COM1_Info.pRxBuf[COM1_Info.RxCnt] = u8Data;
//USART_WriteData( CM_USART1, COM1_Info.pRxBuf[COM1_Info.RxCnt] );
COM1_Info.RxCnt++;
}
}
/**************************************************************
* @Name USART1_TxCpltCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-21
**************************************************************/
static void USART1_TxCpltCallback( void )
{
if( USART_GetStatus( CM_USART1, USART_FLAG_TX_EMPTY ) != RESET )
{
if( COM1_Info.TxLen > 0x00 )
{
CM_USART1->DR = COM1_Info.pTxBuf[COM1_Info.TxCnt] & 0X01FF;
COM1_Info.TxCnt++;
COM1_Info.TxLen--;
}
else
{
COM1_Info.TxLen = 0x00;
COM1_Info.TxCnt = 0x00;
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
}
}
}
/**************************************************************
* @Name USART1_SendData_UseIT
* @brief
* @param pBuf: [输入/出]
** pLen: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-23
**************************************************************/
void USART1_SendData_UseIT( uint8_t *pBuf, uint8_t pLen )
{
COM1_Info.TxCnt = 0x00;
COM1_Info.TxLen = pLen;
memcpy( COM1_Info.pTxBuf, pBuf, COM1_Info.TxLen );
CM_USART1->DR = pBuf[COM1_Info.TxCnt];
COM1_Info.TxCnt++;
COM1_Info.TxLen--;
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, ENABLE );
}
/**************************************************************
* @Name bsp_TimerForUSART1_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
static void bsp_TimerForUSART1_Init( void )
{
stc_tmr0_init_t stcTmr0_InitStruct;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
FCG_Fcg2PeriphClockCmd( FCG2_PERIPH_TMR0_1, ENABLE );
TMR0_DeInit( CM_TMR0_1 );
TMR0_SetCountValue( CM_TMR0_1, TMR0_CH_A, 0 );
stcTmr0_InitStruct.u32Func = TMR0_FUNC_CMP;
stcTmr0_InitStruct.u32ClockSrc = TMR0_CLK_SRC_INTERN_CLK; /* ---PLCK1:120MHz--- */
stcTmr0_InitStruct.u32ClockDiv = TMR0_CLK_DIV32;
stcTmr0_InitStruct.u16CompareValue = 330;
TMR0_Init( CM_TMR0_1, TMR0_CH_A, &stcTmr0_InitStruct );
TMR0_HWStartCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
TMR0_HWClearCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
}
/**************************************************************
* @Name _sys_exit
* @brief 定义_sys_exit()以避免使用半主机模式
* @param
* @retval
* @author Zachary
* @Data 2022-09-05
**************************************************************/
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit( int x )
{
x = x;
}
/**************************************************************
* @Name fputc
* @brief
* @param ch: [输入/出]
** f: [输入/出]
* @retval
* @author Zachary
* @Data 2022-02-23
**************************************************************/
int fputc( int ch, FILE *f )
{
USART_WriteData( CM_USART1, ch );
while( USART_GetStatus( CM_USART1, USART_FLAG_TX_EMPTY ) == RESET );
return ch;
}
实现了串口超时接收的功能,在超时接收中断服务函数中调用FreeRTOS的直接通知任务功能,通知串口数据转发处理任务。在这个任务中调用netconn_write函数,将数据通过网络发出去。以下是超时接收中断函数内容、串口数据转发处理任务的内容。需要注意的是,超时接收中断需要软件清零。
/**************************************************************
* @Name USART1_IdleCallback
* @brief 超时功能用作空闲中断
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
extern TaskHandle_t xTaskHandleTask2;
static void USART1_IdleCallback( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( SET == USART_GetStatus( CM_USART1, USART_FLAG_RX_TIMEOUT ) )
{
if( xTaskHandleTask2 != NULL )
{
vTaskNotifyGiveFromISR( xTaskHandleTask2, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
TMR0_Stop( CM_TMR0_1, TMR0_CH_A );
USART_ClearStatus( CM_USART1, USART_FLAG_RX_TIMEOUT );
}
}
/**************************************************************
* @Name xTask2
* @brief 用作串口接收转发
* @param pvParameters: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTask2( void *pvParameters )
{
for( ;; )
{
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
if( newconnUart != NULL )
{
netconn_write( newconnUart, COM1_Info.pRxBuf, COM1_Info.RxCnt, NETCONN_COPY );
}
COM1_Info.RxCnt = 0;
}
}
视频验证:
UartServer
工程源码:
|