Linux多线程间通信和多进程间通信的方式_多进程、多线程同步(通讯)的方法-CSDN博客
别再被多线程搞晕了!一篇文章轻松搞懂 Linux 多线程同步! - 江小康 - 博客园
1 创建一个串口线程
/*
============================================================================
Name : uart.c
Author : xx
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style, FMQL
============================================================================
*/
#include "uartps.h"
int uart_set(int fd, int speed, int flow_ctrl, int data_bits, int stop_bits, char parity)
{
int i;
int status;
speed_t baud;
int speed_get = 0;
struct termios newtio, oldtio;
/* 配置串口 */
// ...
printf("Uart set done\n");
return 0;
}
int uart_send(int fd, char *snd_buf, int snd_len)
{
int len = 0, fs_sel;
fd_set fs_write;
struct timeval time;
FD_ZERO(&fs_write);
FD_SET(fd, &fs_write);
time.tv_sec = 10;
time.tv_usec = 0;
fs_sel = select(fd+1, NULL, &fs_write, NULL, &time);
if(fs_sel)
{
len = write(fd, snd_buf, snd_len);
//printf("len = %d fs_sel=%d\n", len, fs_sel);
return len;
}
}
int uart_rec(int fd, char *rcv_buf, int rcv_len)
{
int len, fs_sel;
fd_set fs_read;
struct timeval time;
FD_ZERO(&fs_read);
FD_SET(fd, &fs_read);
time.tv_sec =10;
time.tv_usec = 0;
fs_sel = select(fd+1, &fs_read, NULL, NULL, &time);
if(fs_sel)
{
len = read(fd, rcv_buf, rcv_len);
printf("len = %d fs_sel=%d\n", len, fs_sel);
return len;
}
}
int open_uart(const char *device_name)
{
int fd;
const char *val="send_data";
char read_back[128], show_read_back[128];
int i=0;
int ret = 0;
int num = 0;
/* 1. open the uart */
fd = open(device_name,O_RDWR| O_NOCTTY | O_NDELAY);
if(fd < 0)
{
printf("can't open device %s\n", device_name);
return -1;
}
/* 2. 对fd进行操作 */
ret = fcntl(fd, F_SETFL, 0);
if(ret < 0)
{
printf("fcntl failed!\n");
return -2;
}
else
printf("fcntl =%d\n", ret);
uart_set(fd, 115200, -1, 8, 1, 'n');
uart_send(fd, (char *)val, strlen(val));
while(i<2)
{
// uart_send(fd, (char *)val, strlen(val));
//write(fd,val, strlen(val));
//i++;
sleep(3);
memset(show_read_back, 0x0, sizeof(show_read_back));
num = read(fd,read_back, sizeof(read_back));
printf("num = %d\n", num);
if(num > 0 )
{
memcpy(show_read_back, read_back, num);
printf("recved: %s", show_read_back);
}
}
i=0;
close(fd);
return 0;
}
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : main.c
作者 : Skylar
版本 : V1.0
描述 : FMQL uart + 异步通信
其他 : success
实现串口收发(单次发送,多次接收)
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/10 创建
***************************************************************/
#include "main.h"
int main() {
const char *device_name = "/dev/ttyPS1";
int fd = open_uart(device_name);
printf("main end\n");
return 0;
}
成功。
2 创建2个串口线程
共用串口线程函数
修改uartps.c 的代码,创建2个线程。
/*
============================================================================
Name : uart.c
Author : xx
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style, FMQL
官方程序
============================================================================
*/
// 异步处理 & 回调函数
#include "uartps.h"
#define UART_BUF_SIZE 256
int uart_set(int fd, int speed, int flow_ctrl, int data_bits, int stop_bits, char parity)
{
speed_t baud;
int speed_get = 0;
struct termios newtio, oldtio;
printf("Uart set done\n");
return 0;
}
int uart_send(int fd, char *snd_buf, int snd_len)
{
int len = 0, fs_sel;
fd_set fs_write;
struct timeval time;
FD_ZERO(&fs_write);
FD_SET(fd, &fs_write);
time.tv_sec = 10;
time.tv_usec = 0;
fs_sel = select(fd+1, NULL, &fs_write, NULL, &time);
if(fs_sel)
{
len = write(fd, snd_buf, snd_len);
//printf("len = %d fs_sel=%d\n", len, fs_sel);
return len;
}
}
int uart_rec(int fd, char *rcv_buf, int rcv_len)
{
int len, fs_sel;
fd_set fs_read;
struct timeval time;
FD_ZERO(&fs_read);
FD_SET(fd, &fs_read);
time.tv_sec =10;
time.tv_usec = 0;
fs_sel = select(fd+1, &fs_read, NULL, NULL, &time);
if(fs_sel)
{
len = read(fd, rcv_buf, rcv_len);
printf("len = %d fs_sel=%d\n", len, fs_sel);
return len;
}
}
int open_uart(const char *device_name[])
{
// int fd;
const char *val[2]={"ttyPS1:send_data!!!","ttyPS2:send_data!!!"};
// char read_back[128], show_read_back[128];
int ret = 0;
// printf("串口dev0: %s, 串口dev1: %s\r\n", device_name[0], device_name[1]);
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i < 2; i++){
// 初始化串口线程,指定串口设备名
uart_threads[i].uart_device_name = device_name[i];
printf("串口dev0: %s \r\n", uart_threads[i].uart_device_name);
/* 1. open the uart */
uart_threads[i].uart_fd = open(uart_threads[i].uart_device_name,O_RDWR| O_NOCTTY | O_NDELAY);;
if (uart_threads[i].uart_fd == -1) {
printf("串口 %d 初始化失败\r\n", 1);
return -1;
}
/* 2. 对fd进行操作 */
ret = fcntl(uart_threads[i].uart_fd, F_SETFL, 0);
if(ret < 0)
{
printf("fcntl failed!\n");
return -2;
}
// else
// printf("fcntl =%d\n", ret);
uart_set(uart_threads[i].uart_fd, 115200, -1, 8, 1, 'n');
/* 3. 创建线程 */
uart_send(uart_threads[i].uart_fd, val[i], strlen(val[i]));
if (uart_create_thread(&uart_threads[i])!= 0) {
perror("创建串口 线程失败");
return 1;
}
}
// 等待所有线程执行完毕(这里只是简单示例,实际可能不需要等待或者有其他退出条件)
for (int i = 0; i < 2; i++) {
pthread_join(uart_threads[i].thread_id, NULL);
printf("waiting\r\n");
}
// 关闭所有设备并释放相关资源
close_all_devices(uart_threads, 2);
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
/****************** 创建线程 ************************/
// 串口线程函数
void* uart_thread_function(void* arg) {
UartThread* uart_thread = (UartThread*)arg;
// 调用串口的初始化函数,获取文件描述符
// uart_thread->uart_fd = fd;
// 循环处理串口的数据收发等操作
while (1) {
int ret = pthread_mutex_lock(&mutex);
if (ret!= 0) {
perror("线程获取互斥锁失败\r");
// 可以在这里进行适当的错误处理,如退出线程或者尝试重新获取锁等操作
}
// char send_buf[UART_BUF_SIZE] = "send_data";
// 这里可以根据实际需求填充发送缓冲区 send_buf
// uart_send(uart_thread->uart_fd, send_buf,strlen(send_buf));
char receive_buf[UART_BUF_SIZE];
uart_rec(uart_thread->uart_fd, receive_buf, UART_BUF_SIZE);
pthread_mutex_unlock(&mutex);
// 可以在这里添加适当的延时,避免过于频繁地操作串口,根据实际情况调整
usleep(100);
}
pthread_exit(NULL);
}
// 创建串口线程函数实现
int uart_create_thread(UartThread* uart_thread) {
return pthread_create(&uart_thread->thread_id, NULL, uart_thread_function, uart_thread);
}
// 关闭单个串口设备及释放相关线程资源的函数实现,传入线程结构体指针
void close_uart_device(UartThread* uart_thread) {
if (uart_thread->uart_fd!= -1) {
close(uart_thread->uart_fd);
uart_thread->uart_fd = -1;
}
pthread_cancel(uart_thread->thread_id); // 取消线程执行
pthread_join(uart_thread->thread_id, NULL); // 等待线程真正结束,释放线程资源
}
// 关闭所有串口设备及释放相关线程资源的函数实现,传入线程结构体数组和数组大小
void close_all_devices(UartThread* uart_threads, int num_threads) {
for (int i = 0; i < num_threads; i++) {
close_uart_device(&uart_threads[i]);
}
}
运行结果:
- 串口1:正常接收;
- 串口2:接收延迟
串口1/2分开串口线程函数
void* uart_thread_function1(void* arg);
void* uart_thread_function2(void* arg)
分开接收缓冲区;
和uart_rev函数的编写有关:(猜测)FD_ZERO、FD_SET、select、tcflush函数的使用影响进程的时间。
3 不影响主线程的方式
4 创建tcp线程
lwip.c
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : lwip.c
作者 : Skylar
版本 : V1.0
描述 : FMQL lwip + pthread
其他 : 实现创建线程,轮询接收数据
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/19 创建
***************************************************************/
#include "lwip.h"
int init_tcp(TcpDevice* tcp_device){
if((tcp_device->socket_fd = socket(AF_INET,SOCK_STREAM,0)) < 0 )
{
printf("fd = %d\n",tcp_device->socket_fd);
perror("socket error\r");
return -1;
}
printf("socket\r\n");
bzero(&tcp_device->server_addr,sizeof(tcp_device->server_addr));
// IP & Port
tcp_device->server_addr.sin_family = AF_INET;
tcp_device->server_addr.sin_port = htons(tcp_device->port);
tcp_device->server_addr.sin_addr.s_addr = INADDR_ANY;
//绑定
if(bind(tcp_device->socket_fd, (struct sockaddr *)&tcp_device->server_addr,sizeof(tcp_device->server_addr)) < 0)
{
perror("bind error\r");
return -1;
}
printf("bind\r\n");
//调用listen把主动套接字变成被动套接字
if(listen(tcp_device->socket_fd,5) < 0) // 监听连接
{
perror("listen error\r");
return -1;
}
printf("listen\r\n");
socklen_t len;
struct sockaddr_in *cilt;
len = sizeof(struct sockaddr_in); //sockaddr
cilt =(struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
bzero(cilt,sizeof(struct sockaddr));
//阻塞等待客户端链接请求 // 如果连接失败,需要重新accept
tcp_device->newfd = -1;
tcp_device->newfd = accept(tcp_device->socket_fd,(struct sockaddr *)cilt,&len);
if(tcp_device->newfd < 0)
{
perror("accept error\r");
return -1;
// continue;
// 再次连接
}
printf("accept\r\n");
free(cilt);
return tcp_device->newfd;
}
int tcp_send(int socketfd, const char *send_buf){
int ret = send(socketfd, send_buf, strlen(send_buf), 0);
if(ret < 0){
perror("tcp send error\t");
return -1;
}else{
printf("tcp send message succeed\r\n");
}
return 0;
}
void* tcp_thread_function(void* arg){
TcpDevice* tcp_device = (TcpDevice*)arg;
int ret = 0;
char p[INET_ADDRSTRLEN];
socklen_t len;
struct sockaddr_in *cilt;
unsigned short int du;
len = sizeof(struct sockaddr_in);
cilt =(struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
// bzero(cilt,sizeof(struct sockaddr));
if (getpeername(tcp_device->newfd, (struct sockaddr *)cilt, &len) == 0){
du = ntohs(cilt->sin_port);
inet_ntop(AF_INET,(void *)&(cilt->sin_addr),p,len);
printf("tcp recv ready\n");
}else {
perror("getpeername error");
}
while(1)
{
// 清除缓冲区数据
bzero(tcp_device->buf,sizeof(tcp_device->buf));
do{
ret = read(tcp_device->newfd, tcp_device->buf, BUF_SIZ - 1);
} while(ret < 0 && EINTR == errno );
if(ret < 0)
{
perror("tcp read\r");
exit(1);
}
if(!ret)
break;
printf("tcp recv[%u]:[%s] data: %s\n",du,p,tcp_device->buf);
/* 数据处理 */
if(!strncasecmp(tcp_device->buf,"quit",strlen("quit")))
{
printf("tcp rev exit \n");
break;
}
}
free(cilt);
pthread_exit(NULL);
}
// 创建串口线程函数实现
int tcp_create_thread(TcpDevice* tcp_thread) {
int ret = pthread_create(&tcp_thread->thread_id, NULL, tcp_thread_function, tcp_thread);
// pthread_t tid;
if (ret != 0) {
perror("tcp pthread_create err\r");
exit(1);
}
printf("tcp pthread_create\n");
pthread_detach(tcp_thread->thread_id); //要不要 &
return ret;
}
#ifndef LWIP_H
#define LWIP_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#include <arpa/inet.h>
#include <pthread.h>
#define TCP_PORT 5001
#define IP_ADDR "192.168.1.138"
#define BUF_SIZ 128
// 定义TCP设备结构体
typedef struct {
int socket_fd;
int port;
struct sockaddr_in server_addr;
int newfd;
char buf[BUF_SIZ]; // rev_buf
pthread_t thread_id;
} TcpDevice;
TcpDevice* tcp_dev;
// TCP初始化函数声明,传入TCP设备结构体指针,返回初始化结果(成功为0,失败为-1)
int init_tcp(TcpDevice* tcp_device);
int tcp_send(int socketfd, const char *send_buf);
// TCP线程函数声明,传入包含TCP设备信息的结构体指针
void* tcp_thread_function(void* arg);
int tcp_create_thread(TcpDevice* tcp_thread);
#endif
main.c
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : main.c
作者 : Skylar
版本 : V1.0
描述 : FMQL lwip + pthread
其他 : 实现创建线程,轮询接收数据
问题:无法一次接收全部字符(分多次接收)
优化TcpDevice结构体
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/19 创建
***************************************************************/
#include "main.h"
int main() {
const char *hello = "Hello from TCP Server";
printf("main start\r\n");
// 初始化
tcp_dev = (TcpDevice *)malloc(sizeof(TcpDevice));
tcp_dev->port = TCP_PORT;
tcp_dev->newfd = -1;
bzero(tcp_dev->buf, sizeof(tcp_dev->buf));
tcp_dev->thread_id = 0;
int fd = init_tcp(tcp_dev);
if(fd == -1){
free(tcp_dev);
return -1;
}
usleep(100);
tcp_send(fd, hello);
usleep(100);
tcp_create_thread(tcp_dev);
printf("back to main\r");
usleep(100);
tcp_send(fd, hello);
usleep(100);
while(1);
return 0;
}
#ifndef MAIN_H
#define MAIN_H
#include "rtc_ds3231.h"
#include <stdio.h>
#include <pthread.h>
#endif
改进
tcp接收字符串数据部分还需要修改。
5 创建gpio线程
了解linux下gpio相关的文件。
fmql gpio
- 确定IO管脚对应的gpio编号。
- 先 export, 再 set direction, set value,不使用的话就unexport。
- 所有的gpio,只创建一个线程。
代码
找不到gpiochip文件夹
- app运行结果:
Device or resource busy
led不亮 - gpio引脚弄错
- 根据gpiochip基地址,确定gpio控制器(gpio0,gpio1,gpio2,gpio3)
修改之后,led亮了。
gpio.c
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : gpio.c
作者 : Skylar
版本 : V1.0
描述 : FMQL gpio + pthread
其他 : 实现创建线程,
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/20 创建
***************************************************************/
#include "gpio.h"
// GPIO引脚编号数组
gpio_pins[] = {59}; //led
// 根据GPIO编号获取对应的gpiochip控制器编号
int get_gpiochip_num(int gpio_pin) {
DIR *dir;
struct dirent *entry;
// char gpiochip_path[100];
int base_gpiochip_num = 0;
int found = 0;
dir = opendir(GPIO_BASE_DIR);
if (dir == NULL) {
perror("打开GPIO基础目录失败");
return -1;
}
if(gpio_pin >= 0 && gpio_pin < 32){ // gpiochip480
base_gpiochip_num = 480;
gpio_pin += 480;
} else if(gpio_pin >= 32 && gpio_pin < 54){ // gpiochip458
base_gpiochip_num = 458;
gpio_pin += 458 - 32;
} else if(gpio_pin >= 54 && gpio_pin < 86){ // gpiochip426
base_gpiochip_num = 426;
gpio_pin += 426 - 54;
} else if(gpio_pin >= 86 && gpio_pin < 118){ // gpiochip394
base_gpiochip_num = 394;
gpio_pin += 394 - 86;
} else {
perror("gpio引脚%d不是PS端io.\r\n",gpio_pin);
}
/*
// 思路是对的,但是失败了
while ((entry = readdir(dir))!= NULL) { // 遍历
printf("entry->d_name: %s\n", entry->d_name); // 只找到了gpiochip458
if (strncmp(entry->d_name, "gpiochip", 8) == 0) // 字符串比较函数
{
printf("找到符合条件的目录项: %s\n", entry->d_name);
sscanf(entry->d_name, "gpiochip%d", &base_gpiochip_num);
// printf("base_gpiochip_num = %d\n", base_gpiochip_num);
if (gpio_pin >= base_gpiochip_num &&
gpio_pin < base_gpiochip_num + 32) { // gpiochip480控制32个GPIO引脚
found = 480;
break;
} else if(gpio_pin >= base_gpiochip_num + 32 &&
gpio_pin < base_gpiochip_num + 54){
found = 458;
break;
} else if(gpio_pin >= base_gpiochip_num + 54 &&
gpio_pin < base_gpiochip_num + 86){
found = 426;
break;
} else if(gpio_pin >= base_gpiochip_num + 86 &&
gpio_pin < base_gpiochip_num + 118){
found = 394;
break;
} else {
found = -1;
break;
}
} else {
printf("找到不符合条件的目录项: %s\n", entry->d_name);
}
}
closedir(dir);
if (found == -1) {
printf("未找到对应gpio%d的gpiochip控制器,found = %d\n", gpio_pin, found);
return -1;
}
*/
return gpio_pin;
}
// 配置GPIO引脚的函数,传入引脚编号,将其配置为输出模式
int gpio_config(int gpio_pin) {
char export_path[100];
char direction_path[100];
int fd_export, fd_direction;
ssize_t bytes_written;
// 先将GPIO引脚导出到用户空间
snprintf(export_path, sizeof(export_path), GPIO_EXPORT_PATH);
fd_export = open(export_path, O_WRONLY);
if (fd_export < 0) {
perror("打开GPIO导出文件失败");
return -1;
}
printf("1-export GPIO%d 成功.\r\n",gpio_pin);
bytes_written = dprintf(fd_export, "%d", gpio_pin + 394);
if (bytes_written < 0) {
perror("写入GPIO引脚编号到导出文件失败");
close(fd_export);
return -1;
}
printf("2-写入GPIO引脚编号到导出文件%d成功.\r\n",gpio_pin);
close(fd_export);
// 设置GPIO引脚为输出方向
snprintf(direction_path, sizeof(direction_path), GPIO_DIRECTION_PATH, gpio_pin + 394);
fd_direction = open(direction_path, O_WRONLY);
if (fd_direction < 0) {
perror("打开GPIO方向设置文件失败");
return -1;
}
printf("3-打开GPIO%d方向设置文件成功.\r\n",gpio_pin);
bytes_written = write(fd_direction, "out", 3);
if (bytes_written < 0) {
perror("设置GPIO方向为输出失败");
close(fd_direction);
return -1;
}
printf("4-设置GPIO%d方向为输出成功.\r\n",gpio_pin);
close(fd_direction);
return 0;
}
// 设置GPIO引脚电平的函数,传入引脚编号和期望的电平值(0或1)
int gpio_set_level(int gpio_pin, int level) {
char value_path[100];
int fd_value;
ssize_t bytes_written;
snprintf(value_path, sizeof(value_path), GPIO_VALUE_PATH, gpio_pin + 394);
fd_value = open(value_path, O_WRONLY);
if (fd_value < 0) {
perror("打开GPIO值设置文件失败");
return -1;
}
printf("1-打开GPIO%d值设置文件成功.\r\n",gpio_pin);
bytes_written = dprintf(fd_value, "%d", level);
if (bytes_written < 0) {
perror("写入GPIO电平值失败");
close(fd_value);
return -1;
}
printf("2-写入GPIO%d电平值成功.\r\n",gpio_pin);
close(fd_value);
return 0;
}
// 线程函数,用于在一个线程中配置并设置多个GPIO引脚的电平
void *gpio_thread_function(void *arg) {
int num_pins = sizeof(gpio_pins) / sizeof(gpio_pins[0]);
int i;
for (i = 0; i < num_pins; i++) {
// 先获取对应gpio引脚的gpiochip编号并进行导出操作
int gpiochip_num[i] = get_gpiochip_num(gpio_pins[i]);
printf("GPIO%d对应的gpio_num = %d.\r\n",gpio_pins[i],gpiochip_num[i]);
if (gpiochip_num[i] < 0) {
printf("处理GPIO引脚 %d 失败,无法找到对应的gpiochip\n", gpio_pins[i]);
continue;
}
// 配置每个GPIO引脚为输出模式
if (gpio_config(gpiochip_num[i]) < 0) {
printf("配置GPIO引脚 %d 失败\n", gpio_pins[i]);
continue;
}
}
while (1) {
// 循环设置GPIO引脚电平,这里简单示例交替设置高低电平,实际可按具体逻辑调整
for (i = 0; i < num_pins; i++) {
if (gpio_set_level(gpiochip_num[i], 1) < 0) {
printf("设置GPIO引脚 %d 电平为高失败\n", gpiochip_num[i]);
continue;
}
}
usleep(5000000); // 睡眠5秒,可根据实际需求调整
for (i = 0; i < num_pins; i++) {
if (gpio_set_level(gpiochip_num[i], 0) < 0) {
printf("设置GPIO引脚 %d 电平为低失败\n", gpiochip_num[i]);
continue;
}
}
usleep(5000000); // 睡眠5秒,可根据实际需求调整
}
pthread_exit(NULL);
}
int gpio_create_thread(GpioThread *gpio_thread){
// 创建线程用于操作GPIO引脚
if (pthread_create(&gpio_thread, NULL, gpio_thread_function, NULL)!= 0) {
perror("创建GPIO线程失败\r");
return 1;
}
// 主线程可以继续执行其他操作,这里简单示例等待线程结束(实际可能不需要等待,根据需求调整)
pthread_join(gpio_thread, NULL);
return 0;
}
#ifndef GPIO_H
#define GPIO_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <pthread.h>
#include <dirent.h>
#include <poll.h>
/* GPIO 控制器 MIO Pin Num Pin
* gpiochip480 0 - 31 32 480 - 511
* gpiochip458 32 - 53 22 458 - 479
* gpiochip426 0 - 31 32
* gpiochip394 32 - 63 32
*/
// 定义GPIO的相关路径宏(基于设备树中相关节点的名称等信息,这里示例基于给定的设备树结构)
#define GPIO_BASE_DIR "/sys/class/gpio" // 与GPIO 相关操作的路径
#define GPIO_EXPORT_PATH GPIO_BASE_DIR "/export" // 导出GPIO引脚到用户空间
#define GPIO_UNEXPORT_PATH GPIO_BASE_DIR "/unexport"// 还原GPIO引脚回用户空间
#define GPIO_DIRECTION_PATH GPIO_BASE_DIR "/gpio%d/direction" // 设置GPIO引脚方向
#define GPIO_VALUE_PATH GPIO_BASE_DIR "/gpio%d/value" // 设置GPIO电平
// 定义TCP设备结构体
typedef struct {
pthread_t gpio_thread;
} GpioThread;
GpioThread *gpio_thread;
int gpio_pins[];
int get_gpiochip_num(int gpio_pin);
int gpio_config(int gpio_pin);
int gpio_set_level(int gpio_pin, int level);
void *gpio_thread_function(void *arg);
int gpio_create_thread(GpioThread* gpio_thread);
#endif
main.c
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : main.c
作者 : Skylar
版本 : V1.0
描述 : FMQL gpio + pthread
其他 : 实现创建线程,set gpio direction/value
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/20 创建
***************************************************************/
#include "main.h"
int main() {
printf("main start\r\n");
// 初始化
gpio_create_thread(gpio_thread);
usleep(100);
printf("back to main\r");
usleep(100);
while(1);
return 0;
}
/*****************************官方例程 - set gpio500 value
int main()
{
struct pollfd fds[1];
char buffer[16];
int len;
int fd=open("/sys/class/gpio/gpio500/value",O_RDONLY);
if(fd<0)
{
perror("open '/sys/class/gpio/gpio500/value' failed!\n");
return -1;
}
fds[0].fd=fd;
fds[0].events=POLLPRI;
while(1)
{
if(poll(fds,1,0)==-1)
{
perror("poll failed!\n");
return -1;
}
if(fds[0].revents&POLLPRI)
{
if(lseek(fd,0,SEEK_SET)==-1)
{
perror("lseek failed!\n");
return -1;
}
if((len=read(fd,buffer,sizeof(buffer)))==-1)
{
perror("read failed!\n");
return -1;
}
buffer[len]=0;
printf("%s",buffer);
}
}
return 0;
}
******************************/
#ifndef MAIN_H
#define MAIN_H
#include "gpio.h"
#include <stdio.h>
#include <pthread.h>
#endif
6 创建can线程
代码
can.c
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : can.c
作者 : Skylar
版本 : V1.0
描述 : FMQL can + pthread
其他 : 实现创建线程,can接收
论坛 : www.openedv.com
日志 : 初版V1.0 2025/01/07 创建
***************************************************************/
#include "can.h"
int init_can(CanDevice *can_dev)
{
struct ifreq ifr = {0};
memset(&can_dev->can_addr, 0, sizeof(can_dev->can_addr));
can_dev->sockfd = -1;
int ret;
can_dev->bitrate = 1000000; // 设置波特率为100M
/* test code */
printf("start init can0 succeed\r\n");
// 使用system函数执行设置波特率的命令(这里以ip命令为例设置波特率,不同系统设置方式可能有差异)
// char set_baudrate_cmd[100];
sprintf(can_dev->set_baudrate_cmd, "ip link set %s type can bitrate %d", CAN_DEV, can_dev->bitrate);
if (system(can_dev->set_baudrate_cmd) == -1) {
perror("system set baudrate command failed\r\n");
return -1;
}
printf("system set bitrate succeed\r\n");
// 使用system函数执行启用CAN接口的命令
// char up_cmd[50];
sprintf(can_dev->up_cmd, "ifconfig %s up", CAN_DEV);
if (system(can_dev->up_cmd) == -1) {
perror("system up command failed\r\n");
return -1;
}
printf("set can0 up succeed\r\n");
/* 打开套接字 */
can_dev->sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
printf("sockfd = %d\r\n",can_dev->sockfd);
if(0 > can_dev->sockfd) {
perror("socket error\r\n");
return -1;
}
/* 指定can0设备 */
strcpy(ifr.ifr_name, CAN_DEV); // 设置接口名称
if(ioctl(can_dev->sockfd, SIOCGIFINDEX, &ifr) < 0){ // 获取接口索引
perror("ioctl error\n");
return -1;
}
printf("ioctl succeed\r\n");
/* 配置套接字地址结构 */
can_dev->can_addr.can_family = AF_CAN;
can_dev->can_addr.can_ifindex = ifr.ifr_ifindex;
/* 将CAN_DEV与套接字进行绑定 */
ret = bind(can_dev->sockfd, (struct sockaddr *)&can_dev->can_addr, sizeof(can_dev->can_addr));
if (0 > ret) {
perror("bind error\r\n");
close(can_dev->sockfd);
return -1;// exit(EXIT_FAILURE);
}
printf("bind = %d\r\n",ret);
/* 设置过滤规则:不接受任何报文、仅发送数据 */
// setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); // NULL 换成 &rfilter, 0 换成sizeof(rfilter)
return 0;
}
int can_send(CanDevice *can_dev)
{
int ret = -1;
struct can_filter rfilter[2]; //设置过滤规则
printf("write start\r\n");
/* 设置过滤原则*/
// rfilter[0].can_id = 0x123; // 目标帧 ID
// 匹配标准帧,屏蔽扩展帧、远程帧
// rfilter[0].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG;
ret = write(can_dev->sockfd, &can_dev->frame, sizeof(can_dev->frame)); //发送数据
// printf("ret = %d,size of frame = %d\r\n", ret, sizeof(can_dev->frame));
if(sizeof(can_dev->frame) != ret) { //如果ret不等于帧长度,就说明发送失败
perror("write error\r\n");
return -1;
}
printf("write end\r\n");
sleep(1);
return 0;
// out:
// /* 关闭套接字 */
// close(can_dev->sockfd);
}
int cand_rcv(CanDevice *can_dev)
{
if (0 > read(can_dev->sockfd, &can_dev->frame, sizeof(struct can_frame))) {
perror("read error");
return -1;
} // error
printf("read end\r\n");
/* 校验是否接收到错误帧 */
if (can_dev->frame.can_id & CAN_ERR_FLAG) {
printf("Error frame!\n");
return -1;
}
/* 校验帧格式 */
if (can_dev->frame.can_id & CAN_EFF_FLAG) //扩展帧
printf("扩展帧 <0x%08x> ", can_dev->frame.can_id & CAN_EFF_MASK);
else //标准帧
printf("标准帧 <0x%03x> ", can_dev->frame.can_id & CAN_SFF_MASK);
/* 校验帧类型:数据帧还是远程帧 */
if (can_dev->frame.can_id & CAN_RTR_FLAG) {
printf("remote request\n");
// continue;
}
/* 打印数据长度 */
printf("rev[%d] ", can_dev->frame.can_dlc);
/* 打印数据 */
for (int i = 0; i < can_dev->frame.can_dlc; i++)
printf("%02x ", can_dev->frame.data[i]);
printf("\n");
return 0;
}
// 线程函数,用于在一个线程中配置CAN
void *can_thread_function(CanDevice *can_dev)
{
int ret = -1;
// CanDevice *can_dev = (CanDevice*)arg;
ret = init_can(can_dev);
printf("ret = %d\r\n",ret);
if(ret != 0){
perror("init CAN failed\r\n");
// exit(EXIT_FAILURE);
}
/* test code */
// printf("init can0 succeed\r\n");
// printf("can_dev: sockfd = %d, bitrate = %d\r\n", can_dev->sockfd, can_dev->bitrate);
// printf("can_dev address: %p\n", can_dev);
// printf("can_dev->frame address: %p\n", &can_dev->frame);
// printf("sizeof frame: %d\n", sizeof(can_dev->frame));
// 使用memset初始化 can_dev->frame
memset(&can_dev->frame, 0, sizeof(can_dev->frame));
printf("clear frame succeed\r\n");
can_dev->frame.data[0] = 0xA0;
can_dev->frame.data[1] = 0xB0;
can_dev->frame.data[2] = 0xC0;
can_dev->frame.data[3] = 0xD0;
can_dev->frame.data[4] = 0xE0;
can_dev->frame.data[5] = 0xF0;
can_dev->frame.can_dlc = 6; //一次发送6个字节数据
can_dev->frame.can_id = 0x123; //帧ID为0x123,标准帧
printf("start send data\r\n");
ret = can_send(can_dev);
if(ret != 0){
perror("send CAN失败\r\n");
return -1;
}
printf("send can0 data succeed\r\n");
while(1){
ret = cand_rcv(can_dev);
if(!ret){
perror("rev CAN失败\r");
// return -1;
}
usleep(1000);
}
}
void *can_create_thread(CanDevice *can_dev)
{
// 创建线程用于CAN通信 can_thread, NULL, thread_func, can_dev
if (pthread_create(&can_dev->can_thread, NULL, can_thread_function, can_dev)!= 0) {
perror("创建CAN线程失败\r");
exit(1);
}
printf("can pthread_create\n");
// 主线程可以继续执行其他操作,这里简单示例等待线程结束(实际可能不需要等待,根据需求调整)
// pthread_join(can_dev, NULL);
pthread_detach(can_dev->can_thread); //多线程分离
return 0;
}
#ifndef CAN_H
#define CAN_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <pthread.h>
#include <dirent.h>
#include <poll.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#define CAN_DEV "can0"
#define BUF_SIZ 128
// 定义CAN设备结构体
typedef struct {
pthread_t can_thread;
struct can_frame frame;
int sockfd;
// struct can_bittiming bt;
int bitrate;
struct sockaddr_can can_addr;
char set_baudrate_cmd[100];
char up_cmd[50];
} CanDevice;
CanDevice *can_dev;
int init_can(CanDevice *can_dev);
int can_send(CanDevice *can_dev);
int cand_rcv(CanDevice *can_dev);
void *can_thread_function(CanDevice *can_dev);
void *can_create_thread(CanDevice *can_dev);
#endif
main.c
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : main.c
作者 : Skylar
版本 : V1.0
描述 : FMQL gpio + pthread
其他 : 实现创建线程,
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/20 创建
***************************************************************/
#include "main.h"
int main() {
printf("main start\r\n");
// 为 can_dev 分配内存
CanDevice *can_dev = (CanDevice *)malloc(sizeof(CanDevice));
if (can_dev == NULL) {
perror("Memory allocation failed");
return 1;
}
// 初始化
can_create_thread(&can_dev);
usleep(100);
printf("back to main\r");
usleep(100);
while(1);
return 0;
}
#ifndef MAIN_H
#define MAIN_H
#include "can.h"
#include <stdio.h>
#include <pthread.h>
#endif
运行结果
7 创建rtc线程
set rtc
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : rtcAPP.c
作者 : Skylar
版本 : V1.0
描述 : rtc测试 + pthread
其他 : /dev/rtc0
使用方法 :
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/30 创建
***************************************************************/
#include "rtc_ds3231.h"
int rtc_open(const char *device_name){
int fd = open(device_name,O_RDWR);
if(fd < 0)
{
printf("%s 设备文件打开失败.\n",device_name);
return 0;
}
printf("%s 设备文件打开成功.\n",device_name);
return fd;
}
void *rtc_thread_function(RtcDevice* rtc_dev)
{
// int fd = *(int *)arg;
// rtc_read(rtc_dev->fd);
rtc_dev->fd = rtc_open(RTC_DEV);
// 读取RTC驱动的时间
struct rtc_time local_time;
if (ioctl(rtc_dev->fd, RTC_RD_TIME, &local_time) < 0) {
perror("读取RTC时间失败");
// 这里可以根据实际情况决定是否要退出线程或者进行其他处理
// continue;
}
printf("应用层读取的时间: %d-%d-%d %d:%d:%d\n",
local_time.tm_year + 1900,
local_time.tm_mon + 1,
local_time.tm_mday,
local_time.tm_hour,
local_time.tm_min,
local_time.tm_sec);
// 线程休眠一段时间,比如2秒,再去读取时间打印
sleep(2);
// 循环获取并打印时间
while (1) {
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
// set time
local_time.tm_year = 2020 - 1900;
local_time.tm_mon = 5 - 1;
local_time.tm_mday = 16;
local_time.tm_hour = 6;
local_time.tm_min = 46;
local_time.tm_sec = 0;
// char *time_str = rtc_set(22,5,13,6,34,28);
if (ioctl(rtc_dev->fd, RTC_SET_TIME, &local_time) < 0) {
perror("设置RTC时间失败");
// 这里可以根据实际情况决定是否要退出线程或者进行其他处理
continue;
}
// printf("Current time in thread: %s\n", time_str);
printf("应用层set的时间: %d-%d-%d %d:%d:%d\n",
local_time.tm_year + 1900,
local_time.tm_mon + 1,
local_time.tm_mday,
local_time.tm_hour,
local_time.tm_min,
local_time.tm_sec);
// 休眠一段时间,比如1秒
sleep(1);
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
}
return NULL;
}
int rtc_thread_create(RtcDevice* rtc_dev)
{
// 创建线程
if (pthread_create(&rtc_dev->thread_id, NULL, rtc_thread_function, rtc_dev)!= 0) {
perror("Error creating thread");
return 1;
}
printf("rtc pthread_create\n");
pthread_detach(rtc_dev->thread_id);
return 0;
}
#ifndef RTC_DS3231_H
#define RTC_DS3231_H
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/rtc.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define RTC_DEV "/dev/rtc0"
pthread_mutex_t mutex;
struct rtc_time local_time;
typedef struct{
pthread_t thread_id;
int fd;
} RtcDevice;
RtcDevice* rtc_dev;
int rtc_open(const char *device_name);
void *rtc_thread_function(RtcDevice* rtc_dev);
int rtc_thread_create(RtcDevice* rtc_dev);
#endif
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : main.c
作者 : Skylar
版本 : V1.0
描述 : FMQL gpio + pthread
其他 : 实现创建线程,read & set rtc time
succeed
论坛 : www.openedv.com
日志 : 初版V1.0 2024/12/20 创建
***************************************************************/
#include "rtc_ds3231.h"
#include <stdio.h>
#include <pthread.h>
int main() {
printf("main start\r\n");
rtc_thread_create(&rtc_dev);
usleep(100);
printf("back to main\r");
usleep(100);
while(1);
return 0;
}
8 创建timer线程
- 使用timer定时,有不同的方法:
- 模拟定时器,usleep
- 硬件定时器/sys/class/timer
- 硬件定时器/sys/class/timer + /dev/mem
尝试写代码,但是失败。待续。。。
fmql-linux程序生成步骤
- 设置环境变量
petalinux安装目录下;
SDK安装包目录下。
- 回到程序的路径
Makefile文件,用来生成驱动程序 .ko 文件(也可修改Makefile,生成app)
- 生成可执行文件(此处为生成app)
生成app需要main.c main.h **.c **.h等多个关联的文件,所以用Makefile生成,只需输入make即可:
- 应用程序拷贝至windows
- app传至开发板(开发板连接路由器,电脑连接路由器网关)
fmql官方提供的ubuntu:用户名和密码都是fmsh
- 把app拷贝至: /lib/modules/.../... 路径下
- sudo chmod 777 app 设置app权限
- sudo ./app 运行程序
文件夹内容(记录一下自己的学习过程)
上图:按照正点原子教程学的。
测试添加线程的外设(已测试uart、tcp)