替换设备树文件
挂载boot分区:
mount /dev/mmcblk2 /boot
拷贝新的设备树文件到boot分区
cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot
reboot重启,查询设备节点
ls /dev/ttySTM*
发现新的设备节点ttySTM3
/dev/ttySTM0 /dev/ttySTM1 /dev/ttySTM3
设备树开启串口8的原理就是通过Pinctrl指定芯片连接的扩展板引脚,涉及到驱动方面的知识,具体查看视频:修改设备树,开启串口驱动
串口API
在 Linux 系统中,操作设备的统一接口就是:open/ioctl/read/write。
对于 UART,又在 ioctl 之上封装了很多函数,主要是用来设置行规程。所以对于 UART,编程的套路就是:
- open;
- 设置行规程,比如波特率、数据位、停止位、检验位、RAW 模式、一有数据就返回;
- read/write;
行规程采用默认模式时,需要按下enter键才能执行上传
设置行规程
struct termios 结构体
该结构体主要用于与驱动进行通信的载体
行规程函数
函数名 | 作用 |
---|---|
tcgetattr | get terminal attributes,获得终端的属性 |
tcsetattr | set terminal attributes,修改终端参数 |
tcflush | 清空终端未完成的输入/输出请求及数据 |
cfsetispeed | sets the input baud rate,设置输入波特率 |
cfsetospeed | sets the output baud rate,设置输出波特率 |
cfsetspeed | 同时设置输入、输出波特率 |
这些函数在名称上有一些惯例:
- tc:terminal contorl
- cf: control flag
串口应用——回环
接线
代码:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
/* set_opt(fd,115200,8,'N',1) */
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;
//获取默认的参数,保存在oldtio结构体中
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
newtio.c_oflag &= ~OPOST; /*Output*/
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VMIN] = 1; /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间:
* 比如VMIN设为10表示至少读到10个数据才返回,
* 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
* 假设VTIME=1,表示:
* 10秒内一个数据都没有的话就返回
* 如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
*/
tcflush(fd,TCIFLUSH);
//通过该结构体设置行规程
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
//printf("set done!\n");
return 0;
}
int open_port(char *com)
{
int fd;
//fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
fd = open(com, O_RDWR|O_NOCTTY);//O_NOCTTY表示不设置为控制终端,不会接收控制信号
if (-1 == fd){
return(-1);
}
if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态,若第三个参数为FNDELAY则表示没有数据返回0*/
{
printf("fcntl failed!\n");
return -1;
}
return fd;
}
/*
* ./serial_send_recv <dev>
*/
int main(int argc, char **argv)
{
int fd;
int iRet;
char c;
/* 1. open */
/* 2. setup
* 115200,8N1
* RAW mode
* return data immediately
*/
/* 3. write and read */
if (argc != 2)
{
printf("Usage: \n");
printf("%s </dev/ttySAC1 or other>\n", argv[0]);
return -1;
}
fd = open_port(argv[1]);
if (fd < 0)
{
printf("open %s err!\n", argv[1]);
return -1;
}
iRet = set_opt(fd, 115200, 8, 'N', 1);
if (iRet)
{
printf("set port err!\n");
return -1;
}
printf("Enter a char: ");
while (1)
{
scanf("%c", &c);
iRet = write(fd, &c, 1);
iRet = read(fd, &c, 1);
if (iRet == 1)
printf("get: %02x %c\n", c, c);
else
printf("can not get data\n");
}
return 0;
}