目录
1、串口基本认知
2、USB转TTL,使用ch340通信
2.1 TTL电平
2.2 串口接线方式
3、串口通信常用的API
4、代码通信实例
4.1 发送一个字符/字符串到串口
4.2 树莓读取串口数据(字符串)
4.3 双方互相通信
4.3.1 树莓派接收一个字符同时再发送字符到串口
4.3.2 树莓派子进程接收字符串父进程每一秒打印一个字符串到串口
串行接口简称串口,也称 串行通信 接口或 串行通讯接口 (通常指 COM 接口 ),是采用串行通信方 式的 扩展接口 。串行 接口 ( Serial Interface )是指数据一位一位地顺序传送。其特点是 通信线路 简 单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢
串口通信经常使用在多机通信中。不管是软件还是硬件,都存在模块化的编程思想。
● 半双工:A传数据给B,B只接收数据,什么也不能做;
● 全双工:A传数据给B的同时,B也能给A传输数据;
实现串口多机通信的协议设置:串口号,数据格式和波特率。
● 串口号:CH340软件来接收或生成串口号;
● 波特率:好比人的语速,常用的115200,9600;
● 数据格式:可以比作人交流的语种。数据位;奇偶校验位;停止位。
类似于人和人在交流沟通时,都必须使用同一种语言和语速,这样通信双方才能明白对方的内容。
作用:将通信双方识别的电平转换为通信电平。
笔记本电脑通过 TTL 电平与单片机通信TX 发送线(端口) 3.1RX 接收线 ( 端口) 3.0USB 转 TTL ,使用 ch340 通信
● 我们常用的单片机,引出来的串口,如果不加其他的接口电路,出来的信号就是TTL电平;
● 如果需要看串口的打印信息,一般是需要接一个上位机的,常规的就是电脑,而现在的电脑一般的通信接口只有USB;
● 对于USB口而言,是没法和TTL串口直接通信的,因为接口不匹配,电平也不匹配,这时候就需要借助其它设备来实现接口与电平的转换;
● 常规操作是使用CH340这种芯片所制作的USB转串口模块,这个模块就可以实现TTL串口和USB之前的转换。
RXD :数据输入引脚,数据接受;TXD :数据发送引脚,数据发送;
使用时需要包含头文件:#include
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, …) | fd:文件描述符 message:格式化的字符串 |
像使用C语言中的printf一样发送数据到串口 |
int serialDataAvail (int fd) | fd:文件描述符 返回:串口缓存中已经接收的,可读取的字节数,-1代表错误 |
获取串口缓存中可用的字节数。 |
int serialGetchar (int fd) | fd:文件描述符 返回:读取到的字符 |
从串口读取一个字节数据返回。 如果串口缓存中没有可用的数据,则会等待10秒,如果10后还有没,返回-1 所以,在读取前,做好通过serialDataAvail判断下。 |
void serialFlush (int fd) | fd:文件描述符 | 刷新,清空串口缓冲中的所有可用的数据。 |
*size_t read(int fd,void * buf ,size_t count); | fd:文件描述符 buf:需要发送的数据缓存数组 count:发送buf中的前count个字节数据 返回:实际写入的字符数,错误返回-1 |
这个是Linux下的标准IO库函数,需要包含头文件#include 当要发送到的数据量过大时,wiringPi建议使用这个函数。 |
*size_t read(int fd,void * buf ,size_t count); | fd:文件描述符 buf:接受的数据缓存的数组 count:接收的字节数. 返回:实际读取的字符数。 |
这个是Linux下的标准IO库函数,需要包含头文件#include |
注:初次使用树莓串口编程,需要配置,不然容易出错。
/* 修改 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
#include
#include
int main()
{
if(-1==wiringPiSetup()){
printf("初始化库错误\n");
return -1;
}
int fd; //Linux 的思想是:将一切IO设备,都看做文件,fd就是代表串口抽象出来的文件
if((fd = serialOpen("/dev/ttyAMA0",9600))==-1) //初始化串口,波特率9600
{
printf("打开串口错误");
return -1;
}
while(1)
{
//serialPutchar (fd, 'w');//打印一个字符
serialPuts (fd, "wangjian niubi\r\n");//打印字符串
delayMicroseconds (1000000);//每一秒打印一次
}
return 0;
}
结果演示:
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
if(wiringPiSetup() == -1) {
printf("硬件接口初始化失败!\n");
exit(-1);
}
fd=serialOpen("/dev/ttyAMA0",9600);//打开并初始化串口,波特率9600
if(fd != -1){
printf("serial open success\n");
printf("fd=%d\n",fd);
}
char cmd[128]={0};
int n=0;
while(1)
{
int i=0;
memset(cmd,0,128); //情况字符数组
while(serialDataAvail(fd)!=0)
{
char c=serialGetchar(fd); //接受一个字节
cmd[i]=c; //将接收到的每一个字节都存到数组中
i++;
if(i==7){ //当字节数超过8个的时候,需要延时一会供串口缓存
delay(200); //延时200ms
}
}
i=0;
while(cmd[i]!=0)
{
printf("%c",cmd[i]); //将数组打印出来
i++;
}
if(i>0){
printf("\n"); //换行
}
}
return 0;
}
结果演示:
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
if(wiringPiSetup() == -1){
printf("硬件接口初始化失败!\n");
exit(-1);
}
fd=serialOpen("/dev/ttyAMA0",9600);//打开并初始化串口,波特率9600
if(fd != -1){
printf("serial open success\n");
printf("fd=%d\n",fd);
}
while(1)
{
while(serialDataAvail(fd)!=0)
{
char cmd = 0;
cmd = serialGetchar (fd);
printf("get data:%c\n",cmd);
if(cmd == '2'){
serialPuts(fd,"hello 2\r\n");//串口中换行\r\n结合使用
}
if(cmd == '3'){ //树莓收到字符3时,就发送hello 3这个字符串到串口
serialPuts(fd,"hello 3\r\n");
}
if(cmd == '4'){
serialPuts(fd,"hello 4\r\n");
}
}
}
return 0;
}
结果演示:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
char cmd[128] = {0};
wiringPiSetup();
int fd =serialOpen("/dev/ttyAMA0",9600); //打开串口
if(fd == -1){ //如果打开串口失败则退出程序
printf("serialOpen failed!\n");
return 0;
}
if(fork() == 0){ //创建子进程读取串口发送来的数据
while(1)
{
int i=0;
memset(cmd,0,128); //情况字符数组
while(serialDataAvail(fd)!=0)
{
char c=serialGetchar(fd); //接受一个字节
cmd[i]=c;
i++;
if(i==7) //当字节数超过8个的时候,需要延时一会供串口缓存
{
delay(200); //延时200ms
}
}
i=0;
while(cmd[i]!=0)
{
printf("%c",cmd[i]); //打印十六进制
i++;
}
if(i>0){
printf("\n"); //换行
}
}
}else{//父进程
while(1)
{
serialPrintf(fd,"hello world!!\n"); //父进程每隔三秒发送一次hello world!!
delay(1000);
}
}
return 0;
}
结果演示:
上文串口相关API的部分内容借鉴了下面的csdn:
https://www.cnblogs.com/lulipro/p/5992172.html