[MCU] 极海APM32E103VET6测评之串口(USART1)

Zachary_yo   2022-9-14 14:52 楼主

        在之前的工程基础上,新建bsp_Usart.c和bsp_Usart.h,用于编写APM32E103VET6的串口测试程序。本测试程序主要测试USART1的查询发送和中断发送,中断接收只是做了简单的回显,通过注释相关代码,打开或关闭回显。其中在中断发送下,波特率到512000bps时,串口调试助手出现接收到的数据不全或者乱码,当然出现这个问题的原因有很多,应该是我程序的问题,也可能是USB转232的问题。在此,我因工作时间的关系未作深究,但是使用查询发送512000bps发送是正常的,原因等有空了再查,代码我会放在文末。目前,我仅测的是板载的232,至于485,只不过是传输介质的不同,但是485相较于232来说,布局布线,接口电路等要求更高,原本可能使用STM32芯片的电路使能正常工作的,但是替换为别的厂家的PIN To PIN芯片就有问题,这其中涉及到不同厂家芯片的兼容性,这是工作中真实遇到的。 

        1、串口配置代码

void bsp_InitUart( void )
{
    UartVarInit();
    bsp_Usart_Init();
}

static void UartVarInit( void )
{

#if UART1_FIFO_EN == 1
    g_tUart1.uart_periph = USART1;				/* 串口设备 */
    g_tUart1.pTxBuf = g_TxBuf1;					/* 发送缓冲区指针 */
    g_tUart1.pRxBuf = g_RxBuf1;					/* 接收缓冲区指针 */
    g_tUart1.usTxBufSize = UART1_TX_BUF_SIZE;	/* 发送缓冲区大小 */
    g_tUart1.usRxBufSize = UART1_RX_BUF_SIZE;	/* 接收缓冲区大小 */
    g_tUart1.usTxWrite = 0;						/* 发送FIFO写索引 */
    g_tUart1.usTxRead = 0;						/* 发送FIFO读索引 */
    g_tUart1.usRxWrite = 0;						/* 接收FIFO写索引 */
    g_tUart1.usRxRead = 0;						/* 接收FIFO读索引 */
    g_tUart1.usRxCount = 0;						/* 接收到的新数据个数 */
    g_tUart1.usTxCount = 0;						/* 待发送的数据个数 */
    g_tUart1.SendBefore = 0;					/* 发送数据前的回调函数 */
    g_tUart1.SendOver = 0;						/* 发送完毕后的回调函数 */
    g_tUart1.ReciveNew = 0;						/* 接收到新数据后的回调函数 */
    g_tUart1.Sending = 0;						/* 正在发送中标志 */
#endif

}

static void bsp_Usart_Init( void )
{
    GPIO_Config_T s_GPIO_InitStruct;
#if UART1_FIFO_EN
    /* ---使能外设时钟------ */
    USART1_TX_GPIO_CLK_ENABLE();
    USART1_RX_GPIO_CLK_ENABLE();
    USART1_CLK_ENABLE();
    RCM_EnableAPB2PeriphClock( RCM_APB2_PERIPH_AFIO );
    /* ---配置TX引脚------ */
    s_GPIO_InitStruct.pin = USART1_TX_PIN;
    s_GPIO_InitStruct.mode = GPIO_MODE_AF_PP;
    s_GPIO_InitStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config( USART1_TX_GPIO_PORT, &s_GPIO_InitStruct );
    /* ---配置RX引脚------ */
    s_GPIO_InitStruct.pin = USART1_RX_PIN;
    s_GPIO_InitStruct.mode = GPIO_MODE_IN_PU;
    s_GPIO_InitStruct.speed = GPIO_SPEED_50MHz;
    GPIO_Config( USART1_RX_GPIO_PORT, &s_GPIO_InitStruct );
    /* ---配置中断------ */
    NVIC_EnableIRQRequest( USART1_IRQn, 2, 0 );
    /* ---配置波特率、奇偶校验------ */
    bsp_SetUartParameter( USART1, 500000, USART_PARITY_NONE, USART_WORD_LEN_8B, USART_STOP_BIT_1 );
    /* ---使能接收中断---- */
    USART_EnableInterrupt( USART1, USART_INT_RXBNE );
    /* ---清中断标志位---- */
    USART_ClearIntFlag( USART1, USART_INT_TXBE );
    USART_ClearStatusFlag( USART1, USART_FLAG_TXC );
    USART_ClearStatusFlag( USART1, USART_FLAG_RXBNE );
#endif
}

void bsp_SetUartParameter( USART_T* uart, uint32_t baudrate, USART_PARITY_T parity, USART_WORD_LEN_T wordlength, USART_STOP_BIT_T stopbits )
{
    USART_Config_T s_UsartInitStruct = { 0 };

    s_UsartInitStruct.baudRate = baudrate;
    s_UsartInitStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
    s_UsartInitStruct.mode = USART_MODE_TX_RX;
    s_UsartInitStruct.parity = parity;
    s_UsartInitStruct.stopBits = stopbits;
    s_UsartInitStruct.wordLength = wordlength;
    USART_Config( uart, &s_UsartInitStruct );

    USART_EnableInterrupt( uart, USART_INT_RXBNE );

    USART_Enable( uart );
}

        2、中断服务函数,代码借鉴了安富莱的串口代码并作了更改

static void USART1_IRQHandler( void )
{
    uint32_t isrflags   = USART1->STS;
    uint32_t cr1its     = USART1->CTRL1;
//    uint32_t cr3its     = USART1->CTRL3;

    /* 处理接收中断  */
    if ( ( isrflags & USART_FLAG_RXBNE ) != RESET )
    {
        /* 从串口接收数据寄存器读取数据存放到接收FIFO */
        uint8_t ch;

        ch = USART1->DATA_B.DATA;
        g_tUart1.pRxBuf[g_tUart1.usRxWrite] = ch;
        if ( ++g_tUart1.usRxWrite >= g_tUart1.usRxBufSize )
        {
            g_tUart1.usRxWrite = 0;
        }
        if ( g_tUart1.usRxCount < g_tUart1.usRxBufSize )
        {
            g_tUart1.usRxCount++;
        }

		if ( g_tUart1.ReciveNew )
		{
			g_tUart1.ReciveNew( ch );
		}
		
		//USART1->DATA_B.DATA = ch;	//回显测试
    }
    else if( ( isrflags & USART_FLAG_IDLE ) != RESET )
    {
        USART1->STS;
        USART1->DATA_B.DATA;
    }

    if( ( isrflags & USART_FLAG_PE ) != RESET )
    {
        USART1->STS;
        USART1->DATA_B.DATA;
    }
    if( ( isrflags & USART_FLAG_FE ) != RESET )
    {
        USART1->STS;
        USART1->DATA_B.DATA;
    }
    if( ( isrflags & USART_FLAG_NE ) != RESET )
    {
        USART1->STS;
        USART1->DATA_B.DATA;
    }
    if( ( isrflags & USART_FLAG_OVRE ) != RESET )
    {
        USART1->STS;
        USART1->DATA_B.DATA;
    }

    /* 处理发送缓冲区空中断 */
    if ( ( ( isrflags & 0x80 ) != RESET ) && ( cr1its & 0x80 ) != RESET )
    {
        //if (_pUart->usTxRead == _pUart->usTxWrite)
        if ( g_tUart1.usTxCount == 0 )
        {
            /* 发送缓冲区的数据已取完时, 禁止发送缓冲区空中断 (注意:此时最后1个数据还未真正发送完毕)*/
            USART_DisableInterrupt( g_tUart1.uart_periph, USART_INT_TXBE );

            /* 使能数据发送完毕中断 */
            USART_EnableInterrupt( g_tUart1.uart_periph, USART_INT_TXC );
        }
        else
        {
            g_tUart1.Sending = 1;

            /* 从发送FIFO取1个字节写入串口发送数据寄存器 */
            USART1->DATA_B.DATA = g_tUart1.pTxBuf[g_tUart1.usTxRead];
            if ( ++g_tUart1.usTxRead >= g_tUart1.usTxBufSize )
            {
                g_tUart1.usTxRead = 0;
            }
            g_tUart1.usTxCount--;
        }
    }
    /* 数据bit位全部发送完毕的中断 */
    if ( ( ( isrflags & 0x40 ) != RESET ) && ( ( cr1its & 0x40 ) != RESET ) )
    {
        //if (_pUart->usTxRead == _pUart->usTxWrite)
        if ( g_tUart1.usTxCount == 0 )
        {
            /* 如果发送FIFO的数据全部发送完毕,禁止数据发送完毕中断 */
            //CLEAR_BIT( _pUart->uart->CR1, USART_CR1_TCIE );
            USART_DisableInterrupt( g_tUart1.uart_periph, USART_INT_TXC );

            if ( g_tUart1.SendOver )
            {
                g_tUart1.SendOver();
            }

            g_tUart1.Sending = 0;
        }
        else
        {
            /* 正常情况下,不会进入此分支 */

            /* 如果发送FIFO的数据还未完毕,则从发送FIFO取1个数据写入发送数据寄存器 */
            USART1->DATA_B.DATA = g_tUart1.pTxBuf[g_tUart1.usTxRead];
            if ( ++g_tUart1.usTxRead >= g_tUart1.usTxBufSize )
            {
                g_tUart1.usTxRead = 0;
            }
            g_tUart1.usTxCount--;
        }
    }
}
#endif /* UART0_FIFO_EN */

        3、printf重定向代码,其中有两种方式实现打印输出,一种是串口查询发送,另一种是使用中断发送,可以通过注释相关代码,来实现测试查询发送和中断发送。

#pragma import(__use_no_semihosting)
struct __FILE
{
    int handle;
};
FILE __stdout;
void _sys_exit( int x )
{
    x = x;
}

int fputc( int ch, FILE *f )
{
//    USART_TxData( USART1, ch );
//    while( USART_ReadStatusFlag( USART1, USART_FLAG_TXC ) == RESET );		//查询方式
    comSendBuf( COM1, ( uint8_t * )&ch, 1 );	//中断方式
    return ch;
}

        4、主程序代码

int main( void )
{
	NVIC_ConfigPriorityGroup( NVIC_PRIORITY_GROUP_4 );
    SysInit();
	
    while( 1 )
	{
		GPIOB->ODATA_B.ODATA9 ^= 1;
		printf( "hello world\r\n" );
		delay_ms( 1000 );
    }
}

/**************************************************************
  * [url=home.php?mod=space&uid=32621]@name[/url] SysInit                                   
  * [url=home.php?mod=space&uid=159083]@brief[/url] * @param   None                                     
  * @retval                                                   
  * [url=home.php?mod=space&uid=1315547]@author[/url] Zachary                                          
  * [url=home.php?mod=space&uid=34591]@data[/url] 2022-09-13                                           
 **************************************************************/
static void SysInit( void )
{
    delay_init( 120 );
    bsp_Gpio_Init();
	bsp_InitUart();
}

        5、串口测试截图,115200bps下实现1S打印hello world,上位机使用的是sscom。

         image.png

        6、中断发送下,512000bps以上波特率乱码截图,再次声明,本测试不代表这款芯片有问题,问题应该是出在我的程序和usb转232上。
image.png      

回复评论 (4)

115200的速率是使用的最多的,在上就会出现丢数现象,太快了就对线路有要求的

在爱好的道路上不断前进,在生活的迷雾中播撒光引
点赞  2022-9-14 21:36

目前,我仅测的是板载的232,至于485,只不过是传输介质的不同,但是485相较于232来说,布局布线,直接上TTL转USB,用好一点的芯片吧

 

点赞  2022-9-15 06:11

最好是上示波器捕获一下时序图,看是串口输出有问题,还是接收芯片有问题,这样对串口的理解更加深透。

点赞  2022-9-15 06:12

谢谢分享,期待后续!

默认摸鱼,再摸鱼。2022、9、28
点赞  2022-9-15 23:06
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复