IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> stm32驱动框架 -> 正文阅读

[嵌入式]stm32驱动框架

?? 此方法实现一种类似linux驱动的框架,用于对硬件设备管理,对每个硬件设备独立封装起来,成为一个个模组。该框架存在:1.统一接口;2.移植扩展性好等优点。
相对应的,因为接口统一,在一些情况下使用起来不方便。
?

一、定义驱动结构体

/* 驱动设备 */
typedef struct
{
  char name[64];
  enum device_class_type type;        //设备类型
  unsigned short flag;                //设备参数
  unsigned short open_flag;           //设备打开标志
  const struct _file_operations *fops;//设备操作方法
} device_t, *device_pt;

/* 驱动设备的操作 */
typedef struct _file_operations
{
  //设备通用接口
  int (*open)(device_pt dev, unsigned short oflag);
  int (*close)(device_pt dev);
  int (*read)(device_pt dev, unsigned int pos, void *buffer, unsigned int size);
  int (*write)(device_pt dev, unsigned int pos, const void *buffer, unsigned int size);
  int (*ioctl)(device_pt dev, int cmd, void *args);
} file_operations_t;

二、以串口为例,实现驱动模型

????????新建serial.c文件,定义串口的驱动设备。

//定义所有接口函数
static int stm32Usart1Open(device_pt dev, unsigned short oflag);
static int stm32Usart1Close(device_pt dev);
static int stm32Usart1Read(device_pt dev, unsigned int pos, void *buffer, unsigned int size);
static int stm32Usart1Write(device_pt dev, unsigned int pos, const void *buffer, unsigned int size);
static int stm32Usart1Ioctl(device_pt dev, int cmd, void *args);

//定义接口结构体
static const file_operations_t stm32_usart1_fops = 
{
  .open  = stm32Usart1Open,
  .close = stm32Usart1Close,
  .read  = stm32Usart1Read,
  .write = stm32Usart1Write,
  .ioctl = stm32Usart1Ioctl,
};

//定义串口驱动设备
static device_t stm32_usart1_dev =
{
  .name      = "serial/usart1",
  .open_flag = 0,
  .fops      = &stm32_usart1_fops,
};

三、驱动管理

????????新建module.c文件,用于管理所有的驱动设备。?

3.1?定义一个驱动设备的数组。

static device_pt g_device_array[MAX_DEVICE_ARRAY];

????????我们把所有的设备驱动都添加到该数组上。

3.2?驱动注册函数

int moduleRegister(device_pt dev)
{
  assert_param(dev != NULL);

  if (g_cur_device_index >= MAX_DEVICE_ARRAY - 1)
    return R_EFULL;

  g_device_array[g_cur_device_index] = dev;
  g_cur_device_index++;
  return R_EOK;
}

????????? 首先,我们需要把stm32_usart1_dev这个结构体变量添加到驱动设备数组g_device_array中,这样我们可以从数组获取到串口的设备。这样做好处一点是,serial.c里所有的函数,变量都可以定义成static静态的,上层使用不需要直接包含.c里的函数,所有驱动只需通过module.c里的接口可间接的调用。如此我们如果需要移植串口驱动,或者扩展其他驱动,我们只需要把serial.c文件移植,获取新建新驱动的.c文件,无需修改上层代码。

3.3 其他接口函数

 /*
  * 描述  :打开对应的驱动
  * 参数  :
  *        [in]  name     驱动名
  *        [in]  oflag    驱动的状态
  * 返回  :
  *        驱动设备的描述符
  */
  int open(const char *name, unsigned short oflag)
  {
    assert_param(name != NULL);

    int i;
    for (i = 0; i < g_cur_device_index; i++)
    {
      if (strcmp(g_device_array[i]->name, name) == 0)
      {
        //检测是否已经打开
        if (g_device_array[i]->open_flag != 0)
          return i;

        //调用对应驱动的open函数
        if (g_device_array[i]->fops->open(g_device_array[i], oflag) != R_EOK)
        {
          return R_ERROR;
        }

        //修改参数
        g_device_array[i]->flag = oflag;
        g_device_array[i]->open_flag = 1;
        return i;
      }
    }

    return R_ERROR;
  }

 /*
  * 描述  :关闭对应的驱动
  * 参数  :
  *        [in]  fd     设备描述符
  * 返回  :
  *        错误描述表
  */
  int close(int fd)
  {
    if (fd == -1 || fd >= MAX_DEVICE_ARRAY)
    {
      return R_ERROR;
    }

    //本来就是关闭
    if (g_device_array[fd]->open_flag == 0)
      return R_EOK;

    int ret;
    ret = g_device_array[fd]->fops->close(g_device_array[fd]);
    g_device_array[fd]->open_flag = 0;
    return ret;
  }

 /*
  * 描述  :读取驱动的缓存数据
  * 参数  :
  *        [in]  fd      设备描述符
  *        [in]  pos     根据具体设备函数的定义
  *        [out] buffer  数据指针
  *        [in]  size    读取的大小
  * 返回  :
  *        错误描述表
  */
  int read(int fd, unsigned int pos, void *buffer, unsigned int size)
  {
    if (fd == -1 || fd >= MAX_DEVICE_ARRAY)
    {
      return R_ERROR;
    }

    if (g_device_array[fd]->flag && DEVICE_FLAG_RDONLY == 0)
    {
      return R_ENOSUPPORT;
    }
    
    int ret;
    ret = g_device_array[fd]->fops->read(g_device_array[fd], pos, buffer, size);
    return ret;
  }

 /*
  * 描述  :写入驱动数据
  * 参数  :
  *        [in]  fd      设备描述符
  *        [in]  pos     根据具体设备函数的定义
  *        [in]  buffer  数据指针
  *        [in]  size    写入的大小
  * 返回  :
  *        错误描述表
  */
  int write(int fd, unsigned int pos, const void *buffer, unsigned int size)
  {
    if (fd == -1 || fd >= MAX_DEVICE_ARRAY)
    {
      return R_ERROR;
    }

    if (g_device_array[fd]->flag && DEVICE_FLAG_WRONLY == 0)
    {
      return R_ENOSUPPORT;
    }

    int ret;
    ret = g_device_array[fd]->fops->write(g_device_array[fd], pos, buffer, size);
    return ret;
  }

 /*
  * 描述  :调用驱动的命令
  * 参数  :
  *        [in]  fd      设备描述符
  *        [in]  cmd     命令
  *        [in]  args    传入参数
  * 返回  :
  *        错误描述表
  */
  int ioctl(int fd, int cmd, void *args)
  {
    if (fd == -1 || fd >= MAX_DEVICE_ARRAY)
    {
      return R_ERROR;
    }
    
    int ret;
    ret = g_device_array[fd]->fops->ioctl(g_device_array[fd], cmd, args);
    return ret;
  }

四、设备自动注册

????????linux驱动是使用了module_init的方式。我本来也是想使用这样方式,后面发现有更方便的办法。在serial.c文件里,添加函数

 /*
  * 描述  :串口注册函数,在main函数之前自动调用
  * 参数  :
  *        无
  * 返回  :
  *        无
  */
  __attribute__((constructor)) void stm32Usart1Init(void)
  {
    moduleRegister(&stm32_usart1_dev);
  }

????????使用__attribute__((constructor))修饰过的函数,系统执行main()函数之前调用该函数,这样我们就可以让系统自动把stm32_usart1_dev变量添加到g_device_array数组上。注意是main()函数之前,一些系统还没有完全初始化完成,不能使用执行与系统相关的操作。

五、使用

????????上层使用串口驱动过程。

5.1?打开驱动

????????所有的设备使用之前,必须要调用open函数。

int g_usart1_fd = -1;
//可读可写,中断读操作
g_usart1_fd = open("serial/usart1", DEVICE_FLAG_RDWR | DEVICE_FLAG_INT_RX);
if (g_usart1_fd == R_ERROR)
{
  Error_Handler();
}

5.2?读取数据

????????这里我使用的是串口中断方式,串口数据先通过中断处理函数存放在缓存里,再通过read函数读取。

unsigned char read_buffer[256] = {0};
int len = read(g_usart1_fd, 0, read_buffer, 256);
if (len > 0)
{
  //存在数据,添加处理代码
}

5.3?发送数据

unsigned char write_buffer[10] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9};
write(g_usart1_fd, 0, write_buffer, 10);

六、过程

????????整个串口驱动使用的过程。

  1. 把stm32_usart1_dev添加到g_device_array数组里统一管理,stm32_usart1_dev设备包含了设备名,open,read,write,ioctl,close接口函数。
  2. 使用时,open函数通过name设备名,在g_device_array数组里寻找到对应的设备,执行对应设备的open函数,返回设备所有数组中的下标位置。
  3. 通过open函数返回的下标,间接调用设备对应的read,write,ioctl,close函数。

七、完整代码

完整代码下载地址

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 14:21:29  更:2021-07-22 14:23:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:29:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码