Linux应用层框架思想

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接口等我总结完会更新。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值