STM32与Python上位机通过USB虚拟串口通信

前言

在详细阅读广大网友的教程之后,我对STM32和Python通过USB通信的流程烂熟于心。
尝试用ST公司的NUCLEO-L476RG板子进行简单的回环通信测试,发现还是存在网上无法找到的问题,这个耽搁了几天,期间找到了原因,但没有焊接调试,所以暂时就不以它为例子进行写了。

后采用正点原子的F103最小系统板进行demo测试,成功了。流程都是类似,所以这里做一个记录。


1. 查看原理图

新建工程前,我们需要对芯片的USB脚进行查询,通过查看原理图可以看到USB的DP与DM脚对应的芯片引脚,以及DP,DM脚在哪里使用到,这个就为我们插USB数据线进行通信测试打下了基础。

DP,DM啥意思呢?是D+,D - ;是Data +,Data - ;是数据接收与发送的正负方向。

正点原子的原理图如下所示:
USB口
在这里插入图片描述在这里插入图片描述

通过原理图我们可以看到数据的传输是通过USB-SLAVE进行的,也就是实物图红框部分。

这个板子稍有些搞人的地方在于,这个红框部分不是烧录引脚,烧录是通过另一个microUSB脚。
我写这个是因为一开始用上面的microUSB口接电脑烧录发现无法烧录,后来仔细看板子的标识,上面对每一个口进行了标注,红框部分是USB_TTL,没标框部分是USB_DEVICE。我们通常使用ST-Link和串口进行烧录,这里TTL对应的接口是串口,DEVICE接口只是作为通信部分。

2. 新建工程

工程采用STM32CubeMX进行自动生成,配置直接看图即可。

因为只是做了个回环测试,所以这里我们根据上面看的原理图只需要进行相应端口配置即可。
选择引脚,配置功能
在这里插入图片描述
USB两个引脚配置成功后。


设置外部晶振,用来产生时钟信号。
在这里插入图片描述


设置调试模式,使用SW接口。
在这里插入图片描述


设置STM32的USB模式和配置,默认即可。
在这里插入图片描述


设置虚拟串口通信
在这里插入图片描述

看过很多教程,他们还会在这里设置一个中断使能,我没设置,但最后效果一样。
在这里插入图片描述


配置一下时钟树
在这里插入图片描述

直接在第二个红箭头那里拉到最高即可,其余点确定,不用管,软件会自动配置好。


设置一些项目配置,堆栈大小的话,如果只和我一样做个通信测试,其实小点就小点无所谓。如果你代码写了不少,出现USB插上以后无法识别等情况,这个堆栈大小可以调大,比如都为0x1000

在这里插入图片描述


这里配置代码生成所需要的库,以及.c.h文件是否分开存放。
在这里插入图片描述


这些都配置好了?那就点击。
在这里插入图片描述


3.添加代码与烧录

因为是做回环测试,所以我们只需要在接受数据的时候把数据发送回去就行。
找到usb_cdc_if.c文件,
260行左右有个CDC_Receive_FS函数,
在return语句前一句添加CDC_Transmit_FS(Buf,*Len);
在这里插入图片描述

重新编译后,进行烧录可能会报错。
在这里插入图片描述
我这个最小系统班的构造有些独特,所以我keil里面直接烧录就出错了。
这个时候我们可以通过生成hex文件进行烧录,hex文件的生成设置是在魔术棒里设置的。
在这里插入图片描述

然后我们可以用串口工具,比如:FlyMcu(正点原子自带的),选择hex文件烧录就行了。我这个板子烧录需要改变接口到USB_TTL部分,其他型号板子可能问题都不太一样,大同小异,看清原理图就知道了。下图还有个Port:可以点击一下选择自己的串口对应的端口。我这里烧录口对应的端口为COM8
在这里插入图片描述


4. python代码编写

从机的相关配置在前三步已经OK了,下面就是上位机的部分了。

python编写上位机通信,我这里主要是通过第三方库pyserial,这个安装如果失败了可以参考《python pycharm安装包失败 使用pip安装失败 解决方案》

我们把STM32的端口换到USB_DEVICE口,对应的电脑上串口在电脑上的标识就可以通过右键此电脑->管理查询到。下图中我的板子对应的是虚拟串口COM16
在这里插入图片描述


找到这个有什么用呢?那当然是配置上位机端口ID用呀。
代码已经贴在下面了,下面是具体说明。
1.导入第三方库
2.定义串口通信初始化函数,这里deviceId的值就是我们刚才在设备管理器中看到的端口号。
3.定义一个串口,并进行初始化。这里注释了很多语句,因为我们用的是USB虚拟串口通信,这个波特率什么的其实作用不是很大。注释的语句分别表示:配置波特率为115200、是否分组、停止位的大小、字节大小、超时配置。
4.刷新缓存输入,返回定义好的串口。
5.写数据并配置编码方式。这里发送的数据一般都是ascii或者16进制进行发送,字符串我试过了似乎无法直接发送。
6.判断有无数据返回,如果返回的数据就是我们发送的数据则输出nice,否则就一直发送。这样写是为了调试方便,没上面其他意思。

import serial.tools.list_ports
# 定义USB通信初试化函数
def Serial_Init():
    deviceId = 'COM16'
    ser = serial.Serial(
        port=deviceId,
        #baudrate=115200,
        #parity=serial.PARITY_NONE,
        #stopbits=serial.STOPBITS_TWO,
        #bytesize=serial.EIGHTBITS,
        #timeout=None
    )
    ser.flushInput()
    return ser


ser = Serial_Init()
ser.write("11".encode('ascii'))
flag = False
while not flag:
    if ser.inWaiting() > 0:
        text = ser.read(ser.inWaiting())
        if text[0:2] == str("11").encode('ascii'):
            flag = True
            print("nice")

    else:
        ser.write("11".encode('ascii'))

执行成功后就会出现这样的结果。
在这里插入图片描述


总结

本文可以说是上位机从0开始记录了,怎么看原理图,怎么配工程,写代码等等。都是我一步一步踩坑走来的,虽然回首看去比较简单,但总归还是收获颇丰。
这一次尝试让我对上位机通信的具体流程有了一定的了解,后面具体运用之类的还会涉及到很多协议的上位机通信,到时候整明白了再做记录。


问题解决思路

这里对于我NUCLEO-L4768RG板子的一些问题,当时看遍了互联网也没找到的解决方案,但是看到了其他的一些问题与解决方案。

大体如下:
1.如果USB配置为全速模式,要接一个上拉电阻,我查了一下是在芯片手册里面有写,这个具体情况具体考虑,这里只写一下解决问题的思路。

2.如果出现USB无法识别或带问号的设备,考虑是否安装驱动,堆栈是否设置过小。

3.NUCLEO-L4768RG的有两个芯片,STM32F103CBT6和STM32L476RG两块,这里比较坑爹的地方就是板子烧写的代码是烧写到L476里面,但是唯一的USB供电以及烧录的端口是在F103上面。L476上面的DP,DM引脚没有接东西,只有两个空着的引脚;
我现在的思路是用一个USB的口,后面飞线大法,直接接到排针上面进行通信。理论上应该可行,就是实现起来稍微有点麻烦。
因此我这个博客写的是F103的最小系统板,其实主要是为了验证通信的流程以及功能。

猜你喜欢

转载自blog.csdn.net/Edwinwzy/article/details/130026696