前言
在详细阅读广大网友的教程之后,我对STM32和Python通过USB通信的流程烂熟于心。
尝试用ST公司的NUCLEO-L476RG板子进行简单的回环通信测试,发现还是存在网上无法找到的问题,这个耽搁了几天,期间找到了原因,但没有焊接调试,所以暂时就不以它为例子进行写了。
后采用正点原子的F103最小系统板
进行demo测试,成功了。流程都是类似,所以这里做一个记录。
1. 查看原理图
新建工程前,我们需要对芯片的USB脚进行查询,通过查看原理图可以看到USB的DP与DM脚对应的芯片引脚,以及DP,DM脚在哪里使用到,这个就为我们插USB数据线进行通信测试打下了基础。
DP,DM啥意思呢?是D+,D - ;是Data +,Data - ;是数据接收与发送的正负方向。
正点原子的原理图如下所示:
通过原理图我们可以看到数据的传输是通过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的最小系统板,其实主要是为了验证通信的流程以及功能。