Linux应用层开发系列文章目录
第一章:linux应用层框架思想
第二章:linux应用层之串口事件触发
第三章:linux应用层之内存管理
文章目录
前言
首先我建议大家先想一下这个场景(每次重开一个项目–不论是MCU还是LINUX),以下是我个人所想到的
第一:所有的驱动、业务代码重新写是什么概念;
第二:如果还像初阶那样接口初始化业务逻辑放一块儿会有什么后果(举例:天天的像单片机代码复制来复制去,改个参数要去通篇的翻代码找位置去改,效率何其低下,技术水平又体现在哪里);
第三:你的代码总要继承吧,总不能一个公司的N个项目N套代码吧;
第四:人活着就是要折腾就是要进步,不提高水平怎么加薪,怎么养家糊口!!!
这里说明以下Linux为什么要搭建框架的几个重要点:
1、提高开发效率:框架提供了一套通用的解决方案和开发模式,可以避免重复编写相似的代码,节省开发人员的时间和精力。开发人员可以专注于业务逻辑和功能实现,而不必关注底层细节。(各司其职,更好管理,工作更加专一)
2、简化复杂性:许多应用程序具有复杂的功能和模块,使用框架可以将这些功能和模块分解为更小的组件,并提供统一的接口和管理方式,从而简化系统的设计和实现过程。
3、促进标准化和规范化:框架定义了一套统一的开发规范和例程DEMO,使得不同开发人员之间可以更容易地协作和交流。同时框架还可以提供一些通用的功能和组件,帮助开发人员快速构建高质量的应用程序。
4、提供扩展性和灵活性:框架通常具有良好的可扩展性和灵活性,可以根据具体需求进行定制和扩展。开发人员可以根据需要添加新的功能模块或替换现有的组件,而不必修改整个系统。(N个项目也可共用一套代码,做预编译切换或更具出场配置切换等等方法去实现)
一、Linux应用框架是什么?
不了解的话不妨参考一下RTOS的架构,这里以国产RT-Thread举例:
其实也比较好看出来,rtthread这个基础结构已经包含了内核接口(内存管理、线程管理、线程通信/同步、时钟管理、等等),设备驱动接口(uart、spi、can、iic、pwm、等等),组件接口(类shell、日志管理、posix接口、文件系统操作接口、等等),软件包接口(理解为第三方库,可以留也可以舍)
应用框架简单可以理解为为业务开发去封装通用的API接口,然后API都提供给我们了,那么接下来我们可以说只要做一件事,那就是专注于业务逻辑的开发。
那我们要做Linux应用层框架应该包含哪些内容呢?
二、Linux框架怎么搭建
框架结构
借助rtthread的架构经验,个人觉得需要这样几个部分
1、第三方库模块
2、通用标准模块
3、硬件驱动模块
4、业务逻辑模块
5、工具模块
下面是我自己构思的思维导图可以细究一下
三、框架之下五大模块之硬件接口模块
接下来就以五大模块为基础去分别讲解每个模块的实际用处。
直接上代码吧,理论不如实践!
1、uart接口
uart_drv.c如下:
/**
*@brief 设置串口数据位,停止位和效验位, 波特率
*@param baude 类型 uint32_t 波特率
*@param databits 类型 uint32_t 数据位
*@param stopbits 类型 uint32_t 停止位
*@param parity 类型 uint32_t 效验
*/
int Uart_Init(char *uart_path,int32_t baude,uint32_t databits,uint32_t stopbits,uint32_t parity)
{
USART_TypeDef Uart_Paras;
int fd = -1;
//获取串口设备描述符
fd = open(uart_path, O_RDWR|O_NOCTTY);
if (fd < 0)
{
log_message(LOG_ERROR, "Fail to Open %s device", uart_path);
return -1;
}
//参数设置
Uart_Paras.baudrate = baude;
Uart_Paras.databits = databits;
Uart_Paras.stopbits = stopbits;
Uart_Paras.parity = parity;
if (set_uart_params(fd,&Uart_Paras))
{
log_message(LOG_ERROR, "%s device set para failed", uart_path);
return -1;
}
return fd;
}
/**
*@brief Uart_Send_Data
*@param fd 类型 int 打开的串口文件句柄
*@param buf 类型 u8
*@param length 类型 int 数据位
*/
bool Uart_Send_Data(int fd, char *buf, size_t length)
{
if (write(fd, buf, length)< 0)
{
LOG("UART SEND ERROR");
return FALSE;
}
return TRUE;
}
/**
*@brief Uart_Read_Data
*@param fd 类型 int 打开的串口文件句柄
*@param buf 类型 u8
*@param length 类型 int 数据位
*/
bool Uart_Read_Data(int fd, char *buf, size_t length)
{
if (read(fd, buf, length)< 0)
{
LOG("UART READ ERROR");
return FALSE;
}
return TRUE;
}
2、i2c接口
i2c_drv.c如下:
/**
* @name I2c_Drv_Init
* @brief 向无寄存器地址的i2c从设备发送数据/命令
* @param fd: i2c从设备文件描述符
* @param filename: iicbainhao
* @param slave_addr: dizhi
* @retval 成功: 0
* 失败: -1
*/
int I2c_Drv_Init(int fd,char *filename, unsigned char slave_address)
{
// 打开I2C设备文件
if ((fd = open(filename, O_RDWR)) < 0) {
LOG("Failed to open the I2C bus");
return -1;
}
if(ioctl(fd,I2C_TENBIT,0) < 0)
{
LOG("Failed to set I2C_TENBIT!\n");
close(fd);
return - 1;
}
// 设置I2C从设备地址
if (ioctl(fd, I2C_SLAVE, slave_address) < 0) {
LOG("Failed to acquire bus access to slave");
close(fd);
return -1;
}
return fd;
}
/**
* @name I2c_Write
* @brief 向无寄存器地址的i2c从设备发送数据/命令
* @param fd: i2c从设备文件描述符
* @param slave_addr: 需要发送的数据/命令
* @param slave_addr: 需要发送的数据/命令长度
* @retval 成功: 0
* 失败: -1
*/
int I2c_Write(int fd, uint8_t send_data, int len)
{
uint8_t data[8]= {0};
data[0] = send_data;
if (len != write(fd, data, len))
{
LOG("write iic data fail");
return -1;
}
return 0;
}
/**
* @name I2c_Read
* @brief 从无寄存器地址的i2c从设备读取数据
* @param fd: i2c从设备文件描述符
* @param recv_data_len: 指定读取长度
* @param recv_data: 读取到的数据
* @retval 成功: 0
* 失败: -1
*/
int I2c_Read(int fd, uint8_t * recv_data, int len)
{
if (len != read(fd, recv_data, len))
{
LOG("read iic data fail");
return -1;
}
return 0;
}
3、gpio接口
gpio_drv.c如下:
/**
* @name Gpio_Export
* @brief gpio注册
* @param gpio: gpio引脚编号
* @retval 成功: 0
* 失败: -1
*/
bool Gpio_Export(unsigned int gpio)
{
int fd, len;
char buf[MAX_BUF];
fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
if (fd < 0)
{
LOG("open gpio%d failed", gpio);
return FALSE;
}
len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return TRUE;
}
/**
* @name Gpio_Export
* @brief gpio取消注册
* @param gpio: gpio引脚编号
* @retval 成功: 0
* 失败: -1
*/
bool Gpio_Unexport(unsigned int gpio)
{
int fd, len;
char buf[MAX_BUF];
fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
if (fd < 0)
{
LOG("open gpio%d failed", gpio);
return FALSE;
}
len = snprintf(buf, sizeof(buf), "%d", gpio);
write(fd, buf, len);
close(fd);
return TRUE;
}
/**
* @name Gpio_Set_Dir
* @brief gpio取消注册
* @param gpio: gpio引脚编号
* @retval 成功: 0
* 失败: -1
*/
bool Gpio_Set_Dir(unsigned int gpio, const char *dir)
{
int fd;
char buf[MAX_BUF];
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0)
{
LOG("open gpio%d failed", gpio);
return FALSE;
}
write(fd, dir, strlen(dir)+1);
close(fd);
return TRUE;
}
/**
* @name Gpio_Set_Value
* @brief gpio设置高低电平
* @param gpio: gpio引脚编号
* @param value: 0/1
* @retval 成功: 0
* 失败: -1
*/
bool Gpio_Set_Value(unsigned int gpio, unsigned int value)
{
int fd;
char buf[MAX_BUF];
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0)
{
LOG("open gpio%d failed", gpio);
return FALSE;
}
if (value != 0)
{
write(fd, "1", 2);
}
else
{
write(fd, "0", 2);
}
close(fd);
return TRUE;
}
/**
* @name Gpio_Get_Value
* @brief 获取gpio读取值
* @param gpio: gpio引脚编号
* @param value: 0/1
* @retval 成功: 0
* 失败: -1
*/
bool Gpio_Get_Value(unsigned int gpio, unsigned int *value)
{
int fd;
char buf[MAX_BUF];
char ch;
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fd = open(buf, O_RDONLY);
if (fd < 0)
{
LOG("open gpio%d failed", gpio);
return FALSE;
}
read(fd, &ch, 1);
if (ch != '0') {
*value = 1;
} else {
*value = 0;
}
close(fd);
return TRUE;
}
/**
* @name Gpio_Set_Edge
* @brief 获取gpio读取值
* @param gpio: gpio引脚编号
* @param edge:
* none: 输入,非中断
* rising: 中断输入,上升沿触发
* falling:中断输入,下降沿触发
* both: 中断输入,边沿触发
* @retval 成功: 0
* 失败: -1
*/
bool Gpio_Set_Edge(unsigned int gpio, const char *edge)
{
int fd;
char buf[MAX_BUF];
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);
fd = open(buf, O_WRONLY);
if (fd < 0)
{
LOG("open gpio%d failed", gpio);
return FALSE;
}
write(fd, edge, strlen(edge) + 1);
close(fd);
return TRUE;
}
4、can接口
can_drv.c如下:
int Can_Socket_Create(char *can_dev)
{
int ret = -1;
int sockfd = -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
struct sockaddr_can can_addr = {0};
//创建套接字
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (sockfd < 0)
{
LOG("failed to create CAN socket, reason %s", strerror(errno));
return -1;
}
//设置CAN接口名称
strcpy(ifr.ifr_name, can_dev); //指定名字
ioctl(sockfd, SIOCGIFINDEX, &ifr);
can_addr.can_family = AF_CAN; //填充数据
can_addr.can_ifindex = ifr.ifr_ifindex;
//将can0 与套接字进行绑定
ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr));
if (0 > ret)
{
LOG("failed to bind CAN socket, reason %s", strerror(errno));
close(sockfd);
return -1;
}
int flags;
flags = fcntl(sockfd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(sockfd,F_SETFL,flags);//发送接收全非阻塞
//fcntl(sockfd,F_SETFL,FNDELAY);//用于设置read函数的非阻塞
return sockfd;
}
static int Can_Set_Opt(int sockfd)
{
/* 设置过滤规则:不接受任何报文、仅发送数据 */
//setsockopt(sockfd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
/* 设置新的缓冲区大小,单位为字节 */
int new_buf_size = 1024;
int result = setsockopt(sockfd, SOL_CAN_RAW, SO_SNDBUF, &new_buf_size, sizeof(new_buf_size));
if (result < 0) {
perror("Error setting send buffer size");
}
return 0;
}
int Can_Init(char *can_dev)
{
int can_socket = -1;
system(can_down);
system(CAN250kBaud);
system(can_up);//上面三行关闭CAN设备,设置波特率后,重新打开CAN设备
can_socket = can_socket_create(can_dev);
if (can_socket < 0)
{
return -1;
}
Can_Set_Opt(can_socket);
return can_socket;
}
int Can_Send_Sync(int sockfd, uint32_t id,uint8_t *pBuf, uint32_t len)
{
if (pBuf == NULL ||(len > CAN_MAX_LEN)) {
LOG("Invalid parameter:len=%d",len);
return -1;
}
struct can_frame frame={};
memcpy(frame.data, pBuf, len);
frame.can_dlc = len;
frame.can_id = id | CAN_EFF_FLAG; // 设置 CAN ID,并假设使用的是扩展帧
int nbytes = write(sockfd, &frame, sizeof(frame));
if (nbytes < 0) {
perror("Write error: ");
}
return nbytes;
}
int Can_Recv_Sync(int sockfd, uint32_t *id,uint8_t *pBuf)
{
struct can_frame frame;
if(pBuf == NULL){
LOG("Invalid parameter");
return -1;
}
int size = read(sockfd, &frame, sizeof(frame));
if (size <= 0) {
return -1;
}
memcpy(pBuf, frame.data, frame.can_dlc);
*id = frame.can_id & CAN_EFF_MASK;
return frame.can_dlc;
}
can_drv.h如下:
#ifndef __DRV_CAN_H
#define __DRV_CAN_H
#include "stdint.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include "errno.h"
#define __USE_MISC 1
#include <net/if.h>
#define CAN1MBaud "ip link set can3 type can bitrate 1000000 "
#define CAN800kBaud "ip link set can3 type can bitrate 800000 "
#define CAN500kBaud "ip link set can3 type can bitrate 500000 "
#define CAN250kBaud "ip link set can3 type can bitrate 250000 "
#define CAN125kBaud "ip link set can3 type can bitrate 125000 "
#define CAN100kBaud "ip link set can3 type can bitrate 125000 "
#define CAN50kBaud "ip link set can3 type can bitrate 50000 "
#define CAN20kBaud "ip link set can3 type can bitrate 25000 "
#define CAN10kBaud "ip link set can3 type can bitrate 10000 "
#define can_up "ifconfig can0 up"//打开CAN0
#define can_down "ifconfig can0 down"//关闭CAN0
#define CAN_MAX_LEN 8
int Can_Socket_Create(char *can_dev);
int Can_Send(int sockfd, struct can_frame frame);
int Can_Send_Sync(int sockfd, uint32_t id,uint8_t *pBuf, uint32_t len);
int Can_Recv_Sync(int sockfd, uint32_t *id,uint8_t *pBuf) ;
int Can_Init(char *can_dev);
#endif
总结
总结:后期先会围绕硬件接口怎样进行抽象、封装、继承为要点去更新
如果全部归为一篇,篇幅太大,后面会将每个大模块作为一章单独去写,本篇的can接口已经更新,spi接口等我总结完会更新。