首先,回顾一下串口的核心知识点,也是面试重点:
- 串口通信通常使用在多机通讯中
- 串口通信是全双工的
- 决定串口通信的成功与否的是 数据格式 和 波特率
- 数据格式: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
运行效果
可见,轻松的实现了通讯效果!
封装动态库
由于这里使用的是我自己封装的串口函数,所以可以根据上上节的知识将它封装成动态库:
详见我之前的博文:
- 生成动态库,命名为"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文件给别人,别人就可以调用我封装好的串口函数了,并且别人无法看到我封装函数的具体实现。