树莓派的的串口通信协议

首先,回顾一下串口的核心知识点,也是面试重点

  • 串口通信通常使用在多机通讯中
  • 串口通信是全双工的
  • 决定串口通信的成功与否的是 数据格式 和 波特率
  • 数据格式:1. 数据位 2.停止位 3. 奇偶校验位

树莓派恢复串口

回忆前几节树莓派刷机的开端,为了能通过串口连接到树莓派,事先将串口设置为了系统交互的方式,这会导致无法进行正常的串口的开发,况且现在已经成功可以使用SSH登录,所以可以将串口设置回来了:

  • 修改 /boot/cmdline.txt:
cpp 复制代码
1. sudo vi /boot/cmdline.txt
2. 将"console=serial,115200"删除
  • sudo reboot重启

设置完成!

树莓派的串口开发

再打开串口后,由于都是基于wiringPi库,所以串口开发和香橙派的也是一样一样的,这里我直接把在香橙派写好的串口开发文件上传过来然后直接开始写:

(注意,树莓派的串口驱动文件是/dev/下的ttyAMA0

mjm_uart_tool.c:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "wiringSerial.h"

int myserialOpen (const char *device, const int baud)
{
	struct termios options ;
	speed_t myBaud ;
	int status, fd ;
	switch (baud){
		case 9600: myBaud = B9600 ; break ;
		case 115200: myBaud = B115200 ; break ;
	}
	if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
		return -1 ;
	fcntl (fd, F_SETFL, O_RDWR) ;
	// Get and modify current options:
	tcgetattr (fd, &options) ;
	cfmakeraw (&options) ;
	cfsetispeed (&options, myBaud) ;
	cfsetospeed (&options, myBaud) ;
	options.c_cflag |= (CLOCAL | CREAD) ;
	options.c_cflag &= ~PARENB ;
	options.c_cflag &= ~CSTOPB ;
	options.c_cflag &= ~CSIZE ;
	options.c_cflag |= CS8 ;
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
	options.c_oflag &= ~OPOST ;
	options.c_cc [VMIN] = 0 ;
	options.c_cc [VTIME] = 100 ; // Ten seconds (100 deciseconds)
	tcsetattr (fd, TCSANOW, &options) ;
	ioctl (fd, TIOCMGET, &status);
	status |= TIOCM_DTR ;
	status |= TIOCM_RTS ;
	ioctl (fd, TIOCMSET, &status);
	usleep (10000) ; // 10mS
	return fd ;
}

void serialSendstring (const int fd, const char *s)
{
	int ret;
	ret = write (fd, s, strlen (s));
	if (ret < 0)
		printf("Serial Puts Error\n");
}

int serialGetstring (const int fd, char *buffer)
{
	int n_read;
	n_read = read(fd, buffer,32);
	return n_read;
}

int serialDataAvail (const int fd)
{
  int result ;

  if (ioctl (fd, FIONREAD, &result) == -1)
    return -1 ;

  return result ;
}

mjm_uart_tool.h:

cpp 复制代码
int myserialOpen (const char *device, const int baud);
void serialSendstring (const int fd, const char *s);
int serialGetstring (const int fd, char *buffer);
int serialDataAvail (const int fd);

serial_mjm_test.c:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include "mjm_uart_tool.h"

void *write_serial(void *arg)
{
	char *sendbuf;
	sendbuf = (char *)malloc(32*sizeof(char));

	while(1){
		memset(sendbuf,'\0',32*sizeof(char));
		fgets(sendbuf,sizeof(sendbuf),stdin);
		serialSendstring (*((int *)arg), sendbuf) ;
	}

	pthread_exit(NULL);

}


void *read_serial(void *arg)
{
	char readbuf[32] = {'\0'};
	while(1){
		while(serialDataAvail (*((int *)arg))){
			serialGetstring (*((int *)arg),readbuf) ;
			printf("-> %s\n",readbuf);
			memset(readbuf,'\0',32);
		}
	}

	pthread_exit(NULL);
}



int main ()
{
	int fd ;

	int ret;
	pthread_t read_thread;
	pthread_t write_thread;

	if ((fd = myserialOpen ("/dev/ttyAMA0", 115200)) < 0) //打开驱动文件,配置波特率
	{
		fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
		return 1 ;
	}

	/*	if (wiringPiSetup () == -1)
		{
		fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
		return 1 ;
		}*/

	ret = pthread_create(&read_thread,NULL,read_serial,(void *)&fd);
	if(ret != 0){
		printf("read_serial create error\n");
		return 1;
	}
	ret = pthread_create(&write_thread,NULL,write_serial,(void *)&fd);
	if(ret != 0){
		printf("write_serial create error\n");
		return 1;
	}

	pthread_join(read_thread,NULL);
	pthread_join(write_thread,NULL);

	return 0 ;
}

编译

由于此处实现的串口函数是我参考wiringPi库自己实现的,所以无需链wiringPi库,只需链线程库就ok

cpp 复制代码
gcc serial_mjm_test.c mjm_uart_tool.c -lpthread

运行效果

可见,轻松的实现了通讯效果!

封装动态库

由于这里使用的是我自己封装的串口函数,所以可以根据上上节的知识将它封装成动态库:

详见我之前的博文:

使用树莓派学习Linux系统编程的 --- 库编程(面试重点)-CSDN博客

  • 生成动态库,命名为"mjmserial
cpp 复制代码
gcc -shared -fpic mjm_uart_tool.c -o libmjmserial.so
  • 编译main函数所在C文件,链库,将可执行文件命名为"serialtest":
cpp 复制代码
gcc serial_mjm_test.c -L ./ -lmjmserial -lpthread -o serialtest
  • 此时可以使用环境变量的方法指定动态库搜索路径 或者直接复制动态库到/usr/lib下,由于后者上节实现过了,这次尝试前者:

写一个脚本:

cpp 复制代码
1. vi serialtest.sh
2. 内容为:"
export LD_LIBRARY_PATH="/home/pi/mjm_code/"
 
./serialtest"
3. chmod +x serialtest.sh
  • 最后直接执行" ./serialtest.sh":

可见,运行成功! 此时我只需要提供动态库的.so文件和.h文件给别人,别人就可以调用我封装好的串口函数了,并且别人无法看到我封装函数的具体实现

相关推荐
qq_4336184413 分钟前
shell 编程(五)
linux·运维·服务器
Zer0_on1 小时前
数据结构栈和队列
c语言·开发语言·数据结构
一只小bit1 小时前
数据结构之栈,队列,树
c语言·开发语言·数据结构·c++
马浩同学2 小时前
【GD32】从零开始学GD32单片机 | DAC数模转换器 + 三角波输出例程
c语言·单片机·嵌入式硬件·mcu
一个没有本领的人2 小时前
win11+matlab2021a配置C-COT
c语言·开发语言·matlab·目标跟踪
一只自律的鸡2 小时前
C项目 天天酷跑(下篇)
c语言·开发语言
长安——归故李3 小时前
【C语言】成绩等级制
c语言·开发语言
广而不精zhu小白3 小时前
CentOS Stream 9 挂载Windows共享FTP文件夹
linux·windows·centos
一休哥助手3 小时前
全面解析 Linux 系统监控与性能优化
linux·运维·性能优化
二进制杯莫停4 小时前
掌控网络流量的利器:tcconfig
linux