当前位置: 首页 > news >正文

为RTEMS Raspberrypi4 BSP添加SPI支持

为RTEMS Raspberrypi4 BSP添加SPI支持

主要参考了dev/bsps/shared/dev/spi/cadence-spi.c

RTEMS 使用了基于linux的SPI框架,SPI总线驱动已经在内核中实现。在这个项目中我需要实习的是 RPI4的SPI主机控制器驱动

SPI在RTEMS中的实现如图:
在这里插入图片描述
首先需要将SPI主机控制器设备在总线上注册,注册函数如下:

rtems_status_code raspberrypi_spi_init(raspberrypi_spi_device device)
{raspberrypi_spi_bus *bus;int eno;volatile raspberrypi_spi *regs;const char *bus_path;bus = (raspberrypi_spi_bus *) spi_bus_alloc_and_init(sizeof(*bus));if (bus == NULL) {return RTEMS_UNSATISFIED;}switch (device) {case raspberrypi_SPI0:regs = (volatile raspberrypi_spi *) BCM2711_SPI0_BASE;bus_path = "/dev/spidev0";break;case raspberrypi_SPI3:regs = (volatile raspberrypi_spi *) BCM2711_SPI3_BASE;bus_path = "/dev/spidev3";break;case raspberrypi_SPI4:regs = (volatile raspberrypi_spi *) BCM2711_SPI4_BASE;bus_path = "/dev/spidev4";break;case raspberrypi_SPI5:regs = (volatile raspberrypi_spi *) BCM2711_SPI5_BASE;bus_path = "/dev/spidev5";break;case raspberrypi_SPI6:regs = (volatile raspberrypi_spi *) BCM2711_SPI6_BASE;bus_path = "/dev/spidev6";break;default:spi_bus_destroy_and_free(&bus->base);return RTEMS_INVALID_NUMBER;break;}eno = spi_bus_register(&bus->base, bus_path);if (eno != 0) {spi_bus_destroy_and_free(&bus->base);return RTEMS_UNSATISFIED;}eno = raspberrypi_spi_init_gpio(device);if (eno != 0) {spi_bus_destroy_and_free(&bus->base);return RTEMS_INVALID_NUMBER;}bus->regs = regs;bus->num_cs = 2;bus->base.transfer = raspberrypi_spi_transfer;bus->base.destroy = raspberrypi_spi_destroy;bus->base.setup = raspberrypi_spi_setup;bus->base.bits_per_word = 8;bus->base.max_speed_hz = 250000000;bus->base.cs = 0;
#ifdef BSP_SPI_USE_INTERRUPTSbus->irq = BCM2711_IRQ_SPI;eno = rtems_interrupt_handler_install(bus->irq,"SPI",RTEMS_INTERRUPT_SHARED,raspberrypi_spi_interrupt,bus);if (eno != RTEMS_SUCCESSFUL) {return EAGAIN;}
#endifreturn RTEMS_SUCCESSFUL;
}

调用 spi_bus_alloc_and_init ,此为SPI总线驱动实现的函数,位于RTEMS内核 dev/cpukit/dev/spi/spi-bus.c

Allocates a bus control from the heap and initializes it. After a sucessful allocation and initialization the bus control must be destroyed via spi_bus_destroy_and_free(). A registered bus control will be automatically destroyed in case the device file is unlinked. Make sure to call spi_bus_destroy_and_free() in a custom destruction handler.参数:
size – The size of the bus control. This enables the addition of bus controller specific data to the base bus control. The bus control is zero initialized.返回值:
non-NULL The new bus control.
NULL An error occurred. The errno is set to indicate the error.

switch结构中根据枚举变量raspberrypi_spi_device 的值分别选择 SPI寄存器地址dev目录下的路径名称。寄存器地址定义在 raspberrypi.h 文件中。

设置bus的各种参数和接口函数。
使用宏定义BSP_SPI_USE_INTERRUPTS选择驱动使用中断模式或轮询模式。

中端句柄的安装,考虑到同时启用多个SPI的情况,使用RTEMS_INTERRUPT_SHARED

  eno = rtems_interrupt_handler_install(bus->irq,"SPI",RTEMS_INTERRUPT_SHARED,raspberrypi_spi_interrupt,bus);

调用spi_bus_register,将设备注册进总线。此函数为SPI总线驱动中实现的函数。

调用raspberrypi_spi_init_gpio,初始化gpio,将gpio设置为正确的功能。将此函数后置的原因:总线注册失败时,避免对gpio进行复原。

接下来介绍transfer函数,用于处理SPI读写。

static int raspberrypi_spi_transfer(spi_bus *base,const spi_ioc_transfer *msgs,uint32_t msg_count
)
{int rv = 0;raspberrypi_spi_bus *bus;bus = (raspberrypi_spi_bus *) base;rv = raspberrypi_spi_check_msg(bus, msgs, msg_count);if (rv == 0) {bus->msg_todo = msg_count;bus->msg = msgs;
#ifdef BSP_SPI_USE_INTERRUPTSbus->task_id = rtems_task_self();raspberrypi_spi_start(bus);rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
#elseraspberrypi_spi_transfer_msg(bus);
#endif}return rv;
}

调用raspberrypi_spi_check_msg函数,对msg进行检查,主要检查是否使用了驱动不支持的模式,cs是否超过cs的总数。
这里需要传入msg_count,因为msgs是地址连续的一个队列,可能包含多个msg。

检查通过则将数据的信息结构体的部分变量赋值给bus结构体中的相应变量。

如果使用中断模式则进入raspberrypi_spi_start,轮询模式则进入raspberrypi_spi_transfer_msg

本文主要介绍中断模式。

raspberrypi_spi_start只需要将传输启动,对于RPI4的SPI控制器,将TA=1,就会立即触发第一个中断,我认为这是与其他BSP不同的点。

static void raspberrypi_spi_start(raspberrypi_spi_bus *bus)
{volatile raspberrypi_spi *regs;regs = bus->regs;regs->spics = regs->spics | RPI_SPICS_INTR | RPI_SPICS_INTD;/* * Set TA = 1. This will immediately trigger a first interrupt with * DONE = 1. */regs->spics = regs->spics | RPI_SPICS_TA;
}

中断处理函数如下:

static void raspberrypi_spi_interrupt(void *arg)
{raspberrypi_spi_bus *bus;volatile raspberrypi_spi *regs;uint32_t val;bus = arg;regs = bus->regs;if (raspberrypi_spi_irq(regs)) {if (bus->todo > 0) {raspberrypi_spi_push(bus, regs);} else {--bus->msg_todo;++bus->msg;raspberrypi_spi_next_msg(bus);}while (regs->spics & RPI_SPICS_RXD && bus->in_transfer > 0) {/*  RX FIFO contains at least 1 byte. */val = regs->spififo;if (bus->rx_buf != NULL) {*bus->rx_buf = (uint8_t)val;++bus->rx_buf;}--bus->in_transfer;}}
}

函数 raspberrypi_spi_irq 用于判断中断是否是由当前SPI设备产生。这使得多个SPI设备可以同时使用。

函数raspberrypi_spi_next_msg用于切换到下一个msg,并将msg结构体中的剩余变量赋值给bus结构体。

rtems_event_transient_receive 和 rtems_event_transient_send 至关重要
传输开始时调用rtems_event_transient_receive

	bus->task_id = rtems_task_self();rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);

传输结束时调用rtems_event_transient_send

	rtems_event_transient_send(bus->task_id);

保证一条传输命令在传输结束前阻塞。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • css快捷代码【超出一行文本显示省略号/超出三行显示省略号/超出n行...】
  • springboot 缓存预热的几种方案
  • 科技云报道:算网筑基AI注智,中国联通如何讲出AI时代的“新故事”?
  • 《MySQL DBA 修炼之道》第二章 Mysql目录结构及bin目录下的文件含义
  • Nacos-2.4.0最新版本docker镜像,本人亲自制作,部署十分方便,兼容postgresql最新版本17和16,奉献给大家了
  • 4 Go语言的操作符
  • Qt 实战(3)数据类型 | 3.2、QVariant
  • 梯度消失和梯度爆炸
  • 前端开发知识(二)-css
  • 分布式系列之ID生成器
  • DockerCompose 安装环境
  • 10道JVM经典面试题
  • 时间序列预测 — — ARIMA模型(理论分析与代码详解)
  • lua 写一个 不同时区之间转换日期和时间 函数
  • qt 如何制作动态库插件
  • [deviceone开发]-do_Webview的基本示例
  • [NodeJS] 关于Buffer
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • codis proxy处理流程
  • emacs初体验
  • export和import的用法总结
  • Js基础知识(一) - 变量
  • Mysql5.6主从复制
  • nodejs:开发并发布一个nodejs包
  • Python - 闭包Closure
  • Python 基础起步 (十) 什么叫函数?
  • Spring Cloud中负载均衡器概览
  • vue2.0项目引入element-ui
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 爱情 北京女病人
  • 基于Android乐音识别(2)
  • 两列自适应布局方案整理
  • 盘点那些不知名却常用的 Git 操作
  • 日剧·日综资源集合(建议收藏)
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 微信开放平台全网发布【失败】的几点排查方法
  • 用jQuery怎么做到前后端分离
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (2022 CVPR) Unbiased Teacher v2
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (二)丶RabbitMQ的六大核心
  • (分布式缓存)Redis持久化
  • (离散数学)逻辑连接词
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (转)ObjectiveC 深浅拷贝学习
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .NET 漏洞分析 | 某ERP系统存在SQL注入
  • .net 使用ajax控件后如何调用前端脚本
  • @Pointcut 使用
  • @Responsebody与@RequestBody
  • [ C++ ] STL_vector -- 迭代器失效问题