串口通信协议
串口通信协议 ——多机通信
串口通信属于全双工
软件和硬件我们都是模块化的思想
串口通信协议
全双工和半双工的区别:
全双工:好比 男和女的在互骂,两个声音都听的到。
半双工:好比 男和女的在吵架 ,男的先骂,女的再骂,一个一个骂(一个能讲话时候,另一个闭嘴)。
两个人吵架,语言要互通,语速要正常
串口里呢就是:数据格式(语言要互通) 波特率(语速)
数据格式:
- 数据位
- 奇偶校验
- 停止位
接下来我们来用串口 实现树莓派和 win 的通信
关于串口库
串口通信,使用时需要包含头文件:#include <wiringSerial.h>
int serialOpen (char *device, int baud)
功能:打开并初始串口
参数:
device:串口的地址,在Linux中就是设备所在的目录。
默认一般是"/dev/ttyAMA0",我的是这样的。
baud:波特率
返回:
正常返回文件描述符,否则返回-1失败。
void serialClose (int fd)
功能:
关闭fd关联的串口
参数:
fd:文件描述符
void serialPutchar (int fd, unsigned char c)
功能:
发送一个字节的数据到串口
参数:
fd:文件描述符
c:要发送的数据
void serialPuts (int fd, char *s)
功能:
发送一个字符串到串口
参数:
fd:文件描述符
s:发送的字符串,字符串要以'\0'结尾
void serialPrintf (int fd, char *message, …)
功能:
像使用C语言中的printf一样发送数据到串口
参数:
fd:文件描述符
message:格式化的字符串
int serialDataAvail (int fd)
功能:
获取串口缓存中可用的字节数。
参数:
fd:文件描述符
返回:
串口缓存中已经接收的,可读取的字节数,-1代表错误
int serialGetchar (int fd)
功能:
从串口读取一个字节数据返回。
如果串口缓存中没有可用的数据,则会等待10秒,如果10后还有没,返回-1
所以,在读取前,做好通过serialDataAvail判断下。
参数:
fd:文件描述符
返回:
读取到的字符
void serialFlush (int fd)
功能:
刷新,清空串口缓冲中的所有可用的数据。
参数:
fd:文件描述符
*size_t write (int fd,const void * buf,size_t count)
功能:
这个是Linux下的标准IO库函数,需要包含头文件#include <unistd.h>
当要发送到的数据量过大时,wiringPi建议使用这个函数。
*size_t read(int fd,void * buf ,size_t count);
功能:
这个是Linux下的标准IO库函数,需要包含头文件#include <unistd.h>
当要接收的数据量过大时,wiringPi建议使用这个函数。
参数:
fd:文件描述符
buf:接受的数据缓存的数组
count:接收的字节数.
返回:
实际读取的字符数。
初次使用树莓派串口编程,需要配置。
/* 修改 cmdline.txt文件 */
cd /boot/
sudo vim cmdline.txt
删除【】之间的部分
dwc_otg.lpm_enable=0 【console=ttyAMA0,115200】 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
/*修改 inittab文件 */
cd /etc/
sudo vim inittab
注释掉最后一行内容:,在前面加上 # 号
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
sudo reboot 重启
串口通信编程
串口通信代码
#include<stdio.h>
#include<wirtingPi.h>
#include<wiringSerial.h>
int main()
{
int fd;
if((wiringPiSetup())<0) //初始化树莓派引脚
{
return 1;
}
if((fd=serialOpen("/dev/ttyAMA0",115200))<0) //打开并初始串口
///dev/ttyAMA0 串口的地址,在Linux中就是设备所在的目录。
{
return 1;
}
while(1){
serialPutchar(fd,'c'); //发送一个字节的数据到串口
delayMicroseconds(1000000); // 延时1秒 微秒数(1000微秒 = 1毫秒 = 0.001秒)
}
serialClose (fd);
return 0;
}
编译 : gcc demo1.c -lwiringPi -o demo1
运行:sudo ./demo1
连接usb模块到win
usb模块(ch340)和树莓派连接图
在win端 用串口助手接收树莓派发送的串口信息
串口通信获取数据
#include<stdio.h>
#include<wirtingPi.h>
#include<wiringSerial.h>
int main()
{
int fd;
int cmd;
if((wiringPiSetup())<0) //初始化树莓派引脚
{
return 1;
}
if((fd=serialOpen("/dev/ttyAMA0",115200))<0) //打开并初始串口
///dev/ttyAMA0 串口的地址,在Linux中就是设备所在的目录。
{
return 1;
}
while(1){
while(serialDataAvail (fd) != -1){ //获取串口缓存中可用的字节数。串口缓存中已经接收的,可读取的字节数,-1代表错误
cmd = serialGetchar (fd); //从串口读取一个字节数据返回。如果串口缓存中没有可用的数据,则会等待10秒,如果10后还有没,返回-1,所以,在读取前,做好通过serialDataAvail判断下。
printf("get data:cmd=%d\n",cmd);
}
}
serialClose (fd);
return 0;
}
接下来我们简单使用一下语音模块
使用树莓派和语音模块实现语音识别功能
树莓派和语音模块接线图
树莓派串口接收语音模块信息代码
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<wiringPi.h>
#include<wiringSerial.h>
int main()
{
int fd;
char cmd[128]={'\0'};
int n_read;
if(wiringPiSetup()<0){
return 1;
}
if((fd = serialOpen("/dev/ttyAMA0",9600))<0){
return 1;
}
while(1){
n_read=read(fd,cmd ,sizeof(cmd));
if(strlen(cmd)== 0){ //收到的字符长度=0 ,就打印超时,并且等待。
printf("chaoshi\n");
continue;
}
printf("getData: %d byte ,context:%s\n",n_read,cmd);
memset(cmd,'\0',sizeof(cmd)/sizeof(char));
}
return 0;
}
语音模块部分代码
对语音模块说话:极cc 它会反馈读到6个字节,内容不显示
对语音模块说话:极cc 打开灯 /关闭灯
结果把12字节拆分成了两次,我们本想它读128个字节,结果显示只要超过8个字节,就拆分。
我们来改变一下代码
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<wiringPi.h>
#include<wiringSerial.h>
int main()
{
int fd;
char cmd[128]={'\0'};
int n_read;
if(wiringPiSetup()<0){
return 1;
}
if((fd = serialOpen("/dev/ttyAMA0",9600))<0){
return 1;
}
while(1){
n_read=read(fd,cmd ,sizeof(cmd));
if(strlen(cmd)== 0){ //收到的字符长度=0 ,就打印超时,并且等待。
printf("chaoshi\n");
continue;
}
if(strstr(cmd,"open" != NULL)){
printf("open light\n");
}
if(strstr(cmd,"close" != NULL)){
printf("close light\n");
}
memset(cmd,'\0',sizeof(cmd)/sizeof(char));
}
return 0;
}
对语音模块说话:极cc 打开灯 /关闭灯
这样就实现了全部字符串显示