串口参数设置及查看
stty -F /dev/ttyS1 ispeed 4800 ospeed 4800 cs8
stty -F /dev/ttyS1 -a |head -n 1
读串口代码
#include <stdlib.h>
#include <fcntl.h>
#include "stdio.h"
#include "termios.h"
#include "unistd.h"
#include "limits.h"
#include <stdint.h>
#include "time.h"
//===================
#include <sys/select.h>
#include <sys/time.h>
//===================
#define UART_DEV "/dev/ttyUSB0" //根据电脑插入的串口号定义
void main()
{
int fd =0;
int RxLen=0;
uint8_t RxBuff[1024]={0};
//==========串口打开============//
fd = open(UART_DEV ,O_RDWR|O_NOCTTY);
if(fd<0){
printf("COM (%s) Open Fail ! \n",UART_DEV); //必须要权限.
return;
}
printf("COM (%s) Open Success ! Watting recv...\n\n",UART_DEV);
//==========配置串口============//
struct termios opt; //配置串口的属性定义在结构体struct termios中
tcgetattr(fd, & opt); //获取终端控制属性
cfsetispeed(& opt, B115200); //指定输入波特率(若不设置系统默认9600bps)
cfsetospeed(& opt, B115200); //指定输出波特率(若不设置系统默认9600bps)
/* c_lflag 本地模式 */
opt.c_cflag &= ~ INPCK; //不启用输入奇偶检测
opt.c_cflag |= (CLOCAL | CREAD); //CLOCAL忽略 modem 控制线,CREAD打开接受者
/* c_lflag 本地模式 */
opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //ICANON启用标准模式;ECHO回显输入字符;ECHOE如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词;ISIG当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时,产生相应的信号
/* c_oflag 输出模式 */
opt.c_oflag &= ~ OPOST; //OPOST启用具体实现自行定义的输出处理
opt.c_oflag &= ~(ONLCR | OCRNL); //ONLCR将输出中的新行符映射为回车-换行,OCRNL将输出中的回车映射为新行符
/* c_iflag 输入模式 */
opt.c_iflag &= ~(ICRNL | INLCR); //ICRNL将输入中的回车翻译为新行 (除非设置了 IGNCR),INLCR将输入中的 NL 翻译为 CR
opt.c_iflag &= ~(IXON | IXOFF | IXANY); //IXON启用输出的 XON/XOFF流控制,IXOFF启用输入的 XON/XOFF流控制,IXANY(不属于 POSIX.1;XSI) 允许任何字符来重新开始输出
/* c_cflag 控制模式 */
opt.c_cflag &= ~ CSIZE; //字符长度掩码,取值为 CS5, CS6, CS7, 或 CS8,加~就是无
opt.c_cflag |= CS8; //数据宽度是8bit
opt.c_cflag &= ~ CSTOPB; //CSTOPB设置两个停止位,而不是一个,加~就是设置一个停止位
opt.c_cflag &= ~ PARENB; //PARENB允许输出产生奇偶信息以及输入的奇偶校验,加~就是无校验
/* c_cc[NCCS] 控制字符 */
opt.c_cc[VTIME] = 0 ; //等待数据时间(10秒的倍数),每个单位是0.1秒 若20就是2秒
opt.c_cc[VMIN] = 0 ; //最少可读数据,非规范模式读取时的最小字符数,设为0则为非阻塞,如果设为其它值则阻塞,直到读到到对应的数据,就像一个阀值一样,比如设为8,如果只接收到3个数据,那么它是不会返回的,只有凑齐8个数据后一齐才READ返回,阻塞在那儿
/* new_cfg.c_cc[VMIN] = 8;//DATA_LEN;
new_cfg.c_cc[VTIME] = 20;//每个单位是0.1秒 20就是2秒
如果这样设置,就完全阻塞了,只有串口收到至少8个数据才会对READ立即返回,或才少于8个数据时,超时2秒也会有返回
另外特别注意的是当设置VTIME后,如果read第三个参数小于VMIN ,将会将VMIN 修改为read的第三个参数*/
/*TCIFLUSH 刷清输入队列
TCOFLUSH 刷清输出队列
TCIOFLUSH 刷清输入、输出队列*/
tcflush(fd, TCIOFLUSH); //刷串口清缓存
tcsetattr(fd, TCSANOW, &opt); //设置终端控制属性,TCSANOW:不等数据传输完毕就立即改变属性
while(1)
{
//==========串口接收(字符串)============//
while( ((RxLen = read(fd, RxBuff,sizeof(RxBuff))) > 0) )
{
RxBuff[RxLen] = 0;
printf( "%s",RxBuff);
}
}
}
实测结果:
串口读写
/*************************************************************************
> File Name: series.c
> Author: kayshi
> Mail: kayshi2019@qq.com
> Created Time: Sat 12 Sep 2020 02:18:21 PM CST
************************************************************************/
#include <stdio.h> /*标准输入输出定义*/
#include <stdlib.h> /*标准函数库定义*/
#include <string.h>
#include <unistd.h> /*Unix 标准函数定义*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> /*文件控制定义*/
#include <termios.h> /*PPSIX 终端控制定义*/
#include <errno.h> /*错误号定义*/
#include <pthread.h>
#include <sys/timerfd.h>
#include <stdint.h>
#include <sys/time.h>
//#define bzero(a, b) memset(a, 0, b)
#define PATH_TTY "/dev/ttyRS4"
uint8_t buf1[] = {0x68, 0x0F, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x49, 0x16};
uint8_t buf2[] = {0x68, 0x0F, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x02, 0x00, 0x58, 0x16};
uint8_t buf3[] = {0x68, 0x0F, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x02, 0x00, 0x48, 0x16};
int fd;
/*
* 打印数组
*/
void show_buf(uint8_t *buf, int len)
{
int i;
for (i = 0; i < len ; i++)
{
printf("%02x ", buf[i]);
}
printf("\n");
}
/*
* 设置定时器事件
*/
static int timerfd_set(int timer_fd, uint32_t timer_ms)
{
struct itimerspec new_value;
new_value.it_value.tv_sec = timer_ms / 1000; //秒
new_value.it_value.tv_nsec = 1000000 * (timer_ms % 1000); //纳秒
new_value.it_interval.tv_sec = 0;
new_value.it_interval.tv_nsec = 0;
if (timerfd_settime(timer_fd, 0, &new_value, NULL) == -1) //设置定时器
{
printf("timerfd_settime is err\n");
return -1;
}
return 0;
}
/*
* 接收数据线程
*/
static void thread_recv(void)
{
uint8_t buf[512];
int nread = 0;
int i =0;
int buf_size = 512;
int max_fd;
int timerfd;
int fd_list[2]; //用来记录使用了哪些文件描述符
fd_set fds;
int rx_len = 0;
//FD_ZERO(&fds);
//FD_SET(fd, &fds);
if ((timerfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0)//创建一个定时器字符
{
printf("%s:%d timerfd create is fail\n", __FILE__, __LINE__);
}
// else
// {
// FD_SET(timerfd, &fds);
// }
fd_list[0] = fd;
fd_list[1] = timerfd;
//找到所有文件描述符的最大值,并加 1 作为select的第一个参数。这样做是因为,select有一个监听范围为0 - 第一个参数值。例如:设置了5,就会监听0 - 4
if (fd > timerfd)
max_fd = fd + 1;
else
max_fd = timerfd + 1;
while(1)
{
FD_ZERO(&fds);//清0
FD_SET(fd, &fds);//添加文件字符到集合重
FD_SET(timerfd, &fds);//添加文件字符到集合重
select(max_fd, &fds, NULL, NULL, NULL);//监听集合重是否有可以读的描述符,没有阻塞在这
for (i = 0; i < 2; i++)
{
if (FD_ISSET(fd_list[i], &fds) == 0)//对比是哪个字符可以读
continue;
if (fd_list[i] == fd)//fd可以读,代表串口有数据到来
{
nread = read(fd, &buf[rx_len], buf_size - nread);//进行读取
if(nread > 0)
{
timerfd_set(timerfd, 1000);//把定时器打开,设置为1000ms,这样1000ms后定时器描述符就可以读
rx_len += nread;//定时器未到时间内收到的数据累加
if(rx_len > 512)//如果收到长度大于规定值,也表示读完
{
printf("recv:");
show_buf(buf, rx_len);
rx_len = 0;//长度清0,表述所有数据读完
}
}
else
{
printf("receive failed\n");
}
}
else if (fd_list[i] == timerfd)//定时器描述符可以读
{
printf("recv:");
timerfd_set(timerfd, 0); //关闭定时器
show_buf(buf, rx_len);
rx_len = 0;
}
}
}
}
int serial_send(int fd, uint8_t *buff, uint32_t len)
{
int i = 0;
uint32_t tx_len = 0;
if ((buff == NULL) || (len == 0))
{
return -1;
}
for (; i < len; i += tx_len)
{
tx_len = write(fd, &buff[i], len - i);
if (tx_len == 0)
{
break;
}
}
return 0;
}
int main(void)
{
struct termios newtio;
pthread_t id1;
fd = open(PATH_TTY, O_RDWR | O_NOCTTY);
if (-1 == fd)
{
printf("can't open %s\n", PATH_TTY);
return -1;
}
printf("open %s sucess!\n", PATH_TTY);
if (fcntl(fd, F_SETFL, 0) < 0) //设置串口为阻塞状态
{
printf("%s fcntl failed!\n", PATH_TTY);
}
if (isatty(STDIN_FILENO) == 0) //测试是否为终端设备
{
printf("%s standard input is not a terminal device\n", PATH_TTY);
}
if (tcgetattr(fd, &newtio) != 0)
{
printf("uart_config: tcgetattr");
return -1;
}
bzero(&newtio, sizeof(newtio));
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
newtio.c_iflag |= INPCK;
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
newtio.c_cflag &= ~CSTOPB;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
tcflush(fd, TCIFLUSH);
if ((tcsetattr(fd, TCSANOW, &newtio)) != 0)
{
perror("uart_config: tcsetattr");
return -1;
}
if (pthread_create(&id1, NULL, (void *)thread_recv, NULL) != 0)
{
printf("Failed to create 10ms thread");
}
while(1)
{
printf("send:");
show_buf(buf1, sizeof(buf1));
serial_send(fd, buf1, sizeof(buf1));
sleep(2);
printf("send:");
show_buf(buf2, sizeof(buf2));
serial_send(fd, buf2, sizeof(buf2));
sleep(2);
printf("send:");
show_buf(buf3, sizeof(buf3));
serial_send(fd, buf3, sizeof(buf3));
sleep(2);
}
return 0;
}
编译命令:添加 -lpthread或者-pthread
gcc rxtx.c -o rxtx.out -lpthread
如果需要多次调用select函数监听文件描述符,每次调用前都必须初始化结构体fd_set fds, 包括FD_ZERO和FD_SET,这是因为select调用之后fds的值就改变了,和你想要监听的值就有差异了,需要通过重复的初始化操作来进行恢复。
上面代码之所以要在接收到数据后启动定时器,就是为了在定时器的事件到达之前,把这段时间读到的数据全部放到一个buf中去,这是为了避免,一笔数据被分成2到n份接收到。通过这种方式可以做到只要在定时器未到时收到的数据,都集中放到一个buf中去
带有输入参数的串口收发
链接: https://blog.csdn.net/weixin_41471318/article/details/116230465
Makefile参考
参考资料
链接: https://blog.csdn.net/weixin_43693705/article/details/126177624
CRC校验及编译遇到的错误
gcc client2.c -o a.out
client2.c:29:14: 错误:expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘auchCRCHi’
uchar code auchCRCHi[] = {
^
client2.c:58:14: 错误:expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘auchCRCLo’
uchar code auchCRCLo[] = {
^
client2.c: 在函数‘crc16’中:
client2.c:95:25: 错误:‘auchCRCHi’未声明(在此函数内第一次使用)
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
^
client2.c:95:25: 附注:每个未声明的标识符在其出现的函数内只报告一次
client2.c:96:14: 错误:‘auchCRCLo’未声明(在此函数内第一次使用)
uchCRCLo = auchCRCLo[uIndex] ;
原因是定义问题,单片机里定义:
code的作用是告诉单片机,我定义的数据要放在ROM(程序存储区)里面,写入后就不能再更改。
去掉code
就可以编译通过:
链接: 校验工具
测试数据:
01 10 00 01 00 02 04 00 0a 00 0b
数组:
char bbuf[] = {0x01,0x10,0x00,0x01,0x00,0x02,0x04,0x00,0x0a,0x00,0x0b};
这里必须强调:a、b的类型必须为unsigned
否则会出现错误
引用头文件:
#include "crc16.h"
crc16.h
#ifndef _CRCTABLE_H_
#define _CRCTABLE_H_
extern const unsigned char CRCHi[];
extern const unsigned char CRCLo[];
unsigned int crc16(unsigned char *puchMsg, unsigned int usDataLen);
#endif
crc16.c
#include <stdio.h>
#include "crc16.h"
#define uchar unsigned char
#define uint unsigned int
/* CRC 高位字节值表 */
// uchar code auchCRCHi[] = {
uchar auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
} ;
/* CRC低位字节值表*/
// uchar code auchCRCLo[] = {
uchar auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
} ;
unsigned int crc16(unsigned char *puchMsg, unsigned int usDataLen)
{
unsigned int uchCRCHi = 0xFF ; /* 高CRC字节初始化 */
unsigned int uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */
unsigned int uIndex ; /* CRC循环中的索引 */
while (usDataLen--) { /* 传输消息缓冲区 */
uIndex = uchCRCHi ^ *puchMsg++ ; /* 计算CRC */
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
uchCRCLo = auchCRCLo[uIndex] ;
}
return (uchCRCHi << 8 | uchCRCLo) ;
}
串口代码非阻塞模式
serport1fd = open(argv[1],O_RDWR | O_NOCTTY | O_NDELAY);//不成为控制终端程序,不受其他程序输出输出影响
这种open方式打开,进行串口昔日以通信测试比较顺利。
上述两种模式会出现只发送不接收现象
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <error.h>
#include <termios.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
typedef struct termios termios_t;
typedef struct serial_data{
char databuf[100];//发送/接受数据
int serfd;//串口文件描述符
}ser_Data;
void *sersend(void *arg);
void *serrecv(void *arg);
int main(int argc,char *argv[])
{
pthread_t pid1,pid2;
pthread_attr_t *pthread_arr1,*pthread_arr2;
pthread_arr1 = NULL;
pthread_arr2 = NULL;
int serport1fd;
/* 进行串口参数设置 */
termios_t *ter_s = malloc(sizeof(*ter_s));
serport1fd = open(argv[1],O_RDWR | O_NOCTTY | O_NDELAY);//不成为控制终端程序,不受其他程序输出输出影响
if(serport1fd < 0){
printf("%s open faild\r\n",argv[1]);
return -1;
}
bzero(ter_s,sizeof(*ter_s));
ter_s->c_cflag |= CLOCAL | CREAD; //激活本地连接与接受使能
ter_s->c_cflag &= ~CSIZE;//失能数据位屏蔽
ter_s->c_cflag |= CS8;//8位数据位
ter_s->c_cflag &= ~CSTOPB;//1位停止位
ter_s->c_cflag &= ~PARENB;//无校验位
ter_s->c_cc[VTIME] = 0;
ter_s->c_cc[VMIN] = 0;
/*1 VMIN> 0 && VTIME> 0
VMIN为最少读取的字符数,当读取到一个字符后,会启动一个定时器,在定时器超时事前,如果已经读取到了VMIN个字符,则read返回VMIN个字符。如果在接收到VMIN个字符之前,定时器已经超时,则read返回已读取到的字符,注意这个定时器会在每次读取到一个字符后重新启用,即重新开始计时,而且是读取到第一个字节后才启用,也就是说超时的情况下,至少读取到一个字节数据。
2 VMIN > 0 && VTIME== 0
在只有读取到VMIN个字符时,read才返回,可能造成read被永久阻塞。
3 VMIN == 0 && VTIME> 0
和第一种情况稍有不同,在接收到一个字节时或者定时器超时时,read返回。如果是超时这种情况,read返回值是0。
4 VMIN == 0 && VTIME== 0
这种情况下read总是立即就返回,即不会被阻塞。----by 解释粘贴自博客园
*/
cfsetispeed(ter_s,B115200);//设置输入波特率
cfsetospeed(ter_s,B115200);//设置输出波特率
tcflush(serport1fd,TCIFLUSH);//刷清未处理的输入和/或输出
if(tcsetattr(serport1fd,TCSANOW,ter_s) != 0){
printf("com set error!\r\n");
}
char buffer[] = {"hello my world!\r\n"};
char recvbuf[100] = {};
ser_Data snd_data;
ser_Data rec_data;
snd_data.serfd = serport1fd;
rec_data.serfd = serport1fd;
memcpy(snd_data.databuf,buffer,strlen(buffer));//拷贝发送数据
pthread_create(&pid1,pthread_arr1,sersend,(void *)&snd_data);
pthread_create(&pid2,pthread_arr2,serrecv,(void *)&rec_data);
ssize_t sizec;
while(1){
usleep(100000);
}
pthread_join(pid1,NULL);
pthread_join(pid2,NULL);
free(ter_s);
return 0;
}
void *sersend(void *arg)//串口发送线程函数
{
ser_Data *snd = (ser_Data *)arg ;
int ret;
while(1){
ret = write(snd->serfd,snd->databuf,strlen(snd->databuf));
if(ret > 0){
printf("send success, data is %s\r\n",snd->databuf);
}else{
printf("send error!\r\n");
}
usleep(300000);
/*
if(发生中断)
break;//退出
*/
}
}
void *serrecv(void *arg)//串口发送线程函数
{
ser_Data *rec= (ser_Data *)arg ;
int ret;
while(1){
ret = read(rec->serfd,rec->databuf,1024);
if(ret > 0){
printf("recv success,recv size is %d,data is %s\r\n",ret,rec->databuf);
}else{
/*
什么也不做
*/
}
usleep(1000);
/*
if(发生中断)
break;//退出
*/
}
}
UART接收jpg(未测试)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#define BUFFER_SIZE 1024
int main() {
int serial_fd, file_fd;
char buffer[BUFFER_SIZE];
ssize_t bytes_read;
FILE *jpg_file;
// 打开串口
serial_fd = open("/dev/ttyS0", O_RDWR);
if (serial_fd < 0) {
perror("Error opening serial port");
return 1;
}
// 打开文件
jpg_file = fopen("received.jpg", "wb");
if (jpg_file == NULL) {
perror("Error opening file");
close(serial_fd);
return 1;
}
// 配置串口
struct termios options;
tcgetattr(serial_fd, &options);
cfsetispeed(&options, B115200); // 设置波特率为 115200
cfsetospeed(&options, B115200);
options.c_cflag |= CS8; // 设置数据位为 8 bits
options.c_cflag &= ~PARENB; // 禁用奇偶校验
options.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控制
options.c_cflag &= ~CSTOPB; // 设置停止位为 1 bit
tcsetattr(serial_fd, TCSANOW, &options);
// 读取数据并写入文件
while ((bytes_read = read(serial_fd, buffer, BUFFER_SIZE)) > 0) {
fwrite(buffer, 1, bytes_read, jpg_file);
}
// 关闭文件和串口
fclose(jpg_file);
close(serial_fd);
return 0;
}
这段代码中,我们首先打开了串口设备 /dev/ttyS0 ,你可以根据实际情况修改成你要使用的串口设备(如 /dev/ttyUSB0 等)。
然后,我们打开了一个用于保存接收到 JPG 文件的文件,这里使用了 fopen 函数,并指定以二进制写入模式 “wb” 打开文件。你可以根据需要更改文件名和打开模式。
接下来,我们对串口进行了配置,使用了 tcgetattr 和 tcsetattr 函数获取和设置串口的属性,例如波特率、数据位、停止位和奇偶校验等。这里设置的波特率为 115200,数据位为 8 bits,禁用了奇偶校验和软件流控制,停止位设置为 1 bit。你可以根据需要调整这些设置。
在读取数据并写入文件的循环中,我们使用 read 函数从串口读取数据,并使用 fwrite 函数将数据写入文件。
最后,我们关闭了文件和串口,释放资源。
请注意,在运行这段代码之前,你需要以 root 权限运行程序,或者对串口设备文件(/dev/ttyS0 或其他)进行适当的权限设置。
这是一个简单的示例,实际应用中可能需要更多的错误处理、超时设置等。确保充分理解代码并根据实际需求进行修改和扩展。