百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

STM32单片机-多串口printf()问题与ASCII码解析

liebian365 2024-10-22 15:35 6 浏览 0 评论

目录:

一、概述

二、设置标志位选择需要的串口

三、多串口printf工程应用

1、仪器指令集

2、正常与异常对比

3、数据解析成ASCII码

4、ASCII码与16进制互转

1)16进制转ASCII码 2)ASCII码转换16进制

5、STM32串口1只能发不能收


一、概述

printf()函数非常好用,但是重定义后只适用于单个串口,需要串口2使用printf(),需要重新定向。有关内容移步STM32关于printf重定向到串口。先贴一下双串口的配置和printf()的书写,mark一下。

void USART_Config()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    //配置串口1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    //配置串口2时钟,使用复用功能,打开AFIO,管脚重映射到PD5,PD6
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
   GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    
    /*配置串口1(USART1 Tx(PA.09))*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);    
    /* 配置串口1(USART1 Tx(PA.10))*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
        /*串口1工作模式(USART1 mode)配置 */
    USART_InitStructure.USART_BaudRate = 115200;
    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_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure); 
    USART_Cmd(USART1, ENABLE);//使能串口
    
    /*配置串口2(USART2 Tx(PD.05))*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    
    /*配置串口2(USART2 Tx(PD.05))*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    
    /*串口2工作模式(USART2 mode)配置 */
    
    USART_InitStructure.USART_BaudRate = 115200;
    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_Rx | USART_Mode_Tx;    

    USART_Init(USART2, &USART_InitStructure); 
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART2, ENABLE);
    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
              /*串口2中断配置*/                                
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*printf()函数重定向*/
int fputc(int ch, FILE *f)
{
    //将printf()内容发往串口1
    USART_SendData(USART1, (unsigned char) ch);
    while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);    
    return (ch);
} 

当只开串口1时,printf()可以正常使用,但是同时使用串口1和串口2时,使用printf()就会输出不了信息,并且程序无法往下执行。若不用printf()函数,而直接使用USART_SendData(USART1,(unsigned char)ch)时,串口1也能正常打印。

但这样太麻烦,每次打印一个字符。

二、设置标志位选择需要的串口

//标志量定义
int USART_PRINTF_FLAG = 2;//默认串口2

//改写fputc
int fputc(int ch, FILE *f)
{
	if (USART_PRINTF_FLAG == 2)
	{
		while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);
		USART_SendData(USART2,(uint8_t)ch);
	}
	else
	{
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
		USART_SendData(USART1,(uint8_t)ch);
	}
	
	return ch;
}

或者采用如下的方法。

/*
* 函数名:itoa
* 描述  :将整形数据转换成字符串
* 输入  :-radix =10 表示10进制,其他结果为0
*         -value 要转换的整形数
*         -buf 转换后的字符串
*         -radix = 10
* 输出  :无
* 返回  :无
* 调用  :被USART_printf()调用
*/
static char *itoa(int value, char *string, int radix)
{
        int     i, d;
        int     flag = 0;
        char    *ptr = string;

    /* This implementation only works for decimal numbers. */
        if (radix != 10)
        {
            *ptr = 0;
            return string;
        }

        if (!value)
        {
            *ptr++ = 0x30;
            *ptr = 0;
            return string;
        }

    /* if this is a negative value insert the minus sign. */
        if (value < 0)
        {
            *ptr++ = '-';

            /* Make the value positive. */
            value *= -1;
        }

        for (i = 10000; i > 0; i /= 10)
       {
            d = value / i;

            if (d || flag)
            {
                *ptr++ = (char)(d + 0x30);
                value -= (d * i);
                flag = 1;
            }
        }

        /* Null terminate the string. */
        *ptr = 0;

        return string;

} 

/*
* 函数名:USART_printf
* 描述  :格式化输出,类似于C库中的printf,但这里没有用到C库
* 输入  :-USARTx 串口通道
*                     -Data   要发送到串口的内容的指针
*                           -...    其他参数
* 输出  :无
* 返回  :无 
* 调用  :外部调用
*         典型应用USART_printf( USART1, "\r\n this is a demo \r\n" );
*                             USART_printf( USART2, "\r\n %d \r\n", i );
*                             USART_printf( USART3, "\r\n %s \r\n", j );
*/
void USART_printf(USART_TypeDef* USARTx, uint8_t *Data,...)
{
      const char *s;
      int d;   
      char buf[16];
      va_list ap;
      va_start(ap, Data);

      while ( *Data != 0)     // 判断是否到达字符串结束符
      {                                                          
                if ( *Data == 0x5c )  //'\'
                {                                                                          
                        switch ( *++Data )
                        {
                                case 'r':                   //回车符                                                   
                                        USART_SendData(USARTx, 0x0d);
                                        Data ++;
                                        break;

                                case 'n':                 //换行符                                                          //???
                                        USART_SendData(USARTx, 0x0a);        
                                        Data ++;
                                        break;
                                
                                default:
                                        Data ++;
                                    break;
                        }                         
                }
                else if ( *Data == '%')
                {                                                                          //
                        switch ( *++Data )
                        {                                
                                case 's':                //字符串                                                                  
                                        s = va_arg(ap, const char *);
          for ( ; *s; s++) 
                                        {
                                                USART_SendData(USARTx,*s);
                                                while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
          }
                                        Data++;
          break;

        case 'd':                      //十进制                                                          
          d = va_arg(ap, int);
          itoa(d, buf, 10);
          for (s = buf; *s; s++) 
                                        {
                                                USART_SendData(USARTx,*s);
                                                while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
          }
                                        Data++;
          break;
                                 default:
                                                Data++;
                                    break;
                        }                 
                } /* end of else if */
                else USART_SendData(USARTx, *Data++);
                while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET );
        }
}

三、多串口printf工程应用

1、仪器指令集

通过串口3与串口4分别控制两台TH9320-S8耐压仪,并将耐压仪测试数据接收过来,再通过串口1将检测的结果反馈给上位机。功能指令集见“TH9320-S8”说明书第5章。

此仪器内置有ModBus(ModBus_RTU通讯规约1)和SCPI(Standard Commands for Programmable Instruments)两种指令集,本应用的代码基于SCPI

2、正常与异常对比

3、数据解析成ASCII码

完整的原代码与相关资料请移步:多串口prinf输出与仪器通讯。

4、ASCII码与16进制互转

由于单片机只能识别和处理的是二进制码,而输入/输出设备(如LED显示器、微型打印机等)则常使用ASCII码或BCD码,故需进行转换。

1)16进制转ASCII码

16进制数0~9对应的ASCII码为30H~39H,字母A~F对应的ASCII码为41H~46H。

故数字0~9表示为ASCII码只需加上30H,便可得到相应的ASCII码值,即可表示为ASCII码。对于A~F,以A举例,0AH的二进制码为00001010B,加上37H(0011 0111B),便可得到41H(0100 0001B),而41H便为大写字母A的ASCII码值。

#include <stdio.h>

int main(void)
{
    unsigned int StringArray[20] = {0x41,0x43,0x2C,0x33,0x2E,0x33,0x30,0x31,0x2C,0x30,0x2E,0x30,0x35,0x34,0x2C,0x50,0x41,0x53,0x53,0x3B};
    unsigned char AscValue[20],i;
    
    for(i = 0;i < 19; i++)
    {
    	AscValue[i] = (char)StringArray[i];
    }
    
    printf("16进制:\n");
    for(i = 0;i < 20; i++)
    {
    	if(!i)
    		printf("%X", StringArray[0]);
   		else if((i > 0)&&(i < 19))
   			printf(" %X", StringArray[i]);
 		else
 			printf(" %X\n", StringArray[19]);
    }
    
    printf("ASCII码:\n");
    for(i = 0;i < 20; i++)
    {
    	if(!i)
    		printf("%c", AscValue[0]);
   		else if((i > 0)&&(i < 19)) 
   			printf(" %c", AscValue[i]);
 		else
 			printf("%c\n", AscValue[i]);
    }
    
    return 0;
}

2)ASCII码转换16进制

5、STM32串口1只能发不能收

1)串口接收中断没有打开。

2)一个串口使用两个串口芯片,如下图所示。此时两个RXD相互干扰,造成不能接收。


人生如逆旅,我亦是行人。觉得不错,动动发财的小手点个赞哦!关注我,后续干货官方有提醒!

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: