12.2内核空间基于SPI总线的OLED驱动

在内核空间编写SPI设备驱动的要点

  1. 在SPI总线控制器的设备树节点下增加SPI设备的设备树节点,节点中必须包含 reg 属性、 compatible 属性、 spi-max-frequency 属性, reg 属性用于描述片选索引, compatible属性用于设备和驱动的匹配, spi-max-frequency 用于描述设备可支持的最大 SPI 总线频率,在注册SPI总线控制器会解析其中的子节点,并注册成SPI设备。
  2. 创建并初始化struct spi_driver对象,其中重点关注of_match_table、probe、remove,of_match_table用于设备树和驱动匹配,probe在设备驱动匹配成功时执行,remove在设备或驱动卸载时执行。
  3. 在模块初始化函数中使用int spi_register_driver(struct spi_driver *sdrv)注册SPI设备驱动
  4. 使用void spi_message_init(struct spi_message *m)和void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)组织数据包,然后使用int spi_sync(struct spi_device *spi, struct spi_message *message)或int spi_async(struct spi_device *spi, struct spi_message *message)传输数据包
  5. 在模块卸载函数中使用void spi_unregister_driver(struct spi_driver *sdrv)注销SPI设备驱动

SPI OLED驱动编写

OLED模块原理图

模块一共由7个引脚,采用SPI模式时引脚定义如下:
GND:电源地
VCC:2.2V~5.5V
SCL(D0):CLK 时钟 (高电平 2.2V~5.5V)
SDA(D1):MOSI 数据(高电平 2.2V~5.5V)
RST:复位(高电平 2.2V~5.5V)
D/C:数据/命令(高电平 2.2V~5.5V)
CS:SPI片选
注意:没有MISO引脚,因为主控只能向OLED写数据,不能读取OLED的数据
在这里插入图片描述

与主控的连接示意图

在这里插入图片描述
要操作OLED,只需使用SPI接口发送数据,并不需要使用SPI接口读取数据。除此之外,还需要控制D/C引脚:

  • 当DC引脚是低电平时,是命令:比如复位、打开显示、设置地址
  • 当DC引脚是高电平时,是数据:写入要显示的数据

显存和像素

OLED上有128*64个像素(128列,64行),每个像素只有2种状态:亮、灭。
在这里插入图片描述
OLED内部有一块显存GDDRAM(Graphic Display Data RAM),显存中每位对应一个像素,入下图所示
在这里插入图片描述

  • byte0对应屏幕左上角竖向排列的8个像素,即COL0第0~第7行的8个像素
  • byte1对应COL1列第0~第7行的8个像素
  • ……
  • byte127对应COL127列第0~第7行的8个像素
  • byte128对应COL0那列第8~第15行的8个像素
  • ……

显存寻址模式

显存被分为8页、128列,要写某个字节时,需要先指定地址(哪页、哪列),然后写入1字节的数据。
OLED有三种寻址模式:

  • 页地址模式(Page addressing mode):每写入1个字节,行地址不变,列地址增1,列地址达到127后会从0开始
    在这里插入图片描述
  • 水平地址模式(Horizontal addressing mode):每写入1个字节,行地址不变,列地址增1,列地址达到127后从0开始,行地址指向下一页,列地址达到127、行地址达到7时,列地址和行地址都被复位为0,指向左上角(在此驱动中初始化时将地址设置为此模式)
    在这里插入图片描述
  • 垂直地址模式(Vertical addressing mode): 每写入1个字节,行地址增1,列地址不变,行地址达到7后从0开始,列地址指向下一列, 列地址达到127、行地址达到7时,列地址和行地址都被复位为0,指向左上角
    在这里插入图片描述

编写OLED设备树

  1. 在 stm32mp15-pinctrl.dtsi 的 &pinctrl_z 节点中修改 SPI 的引脚配置为如下内容:
	spi1_pins_a: spi1-0 {
   
		pins1 {
   
			pinmux = <STM32_PINMUX('Z', 0, AF5)>, /* SPI1_SCK */
				 <STM32_PINMUX('Z', 2, AF5)>; /* SPI1_MOSI */
			bias-disable;
			drive-push-pull;
			slew-rate = <3>;
		};

		pins2 {
   
			pinmux = <STM32_PINMUX('Z', 1, AF5)>; /* SPI1_MISO */
			bias-disable;
			drive-push-pull;
			slew-rate = <3>;
		};
	};

	spi1_sleep_pins_a: spi1-sleep-0 {
   
		pins {
   
			pinmux = <STM32_PINMUX('Z', 0, ANALOG)>, /* SPI1_SCK */
				 <STM32_PINMUX('Z', 1, ANALOG)>, /* SPI1_MISO */
				 <STM32_PINMUX('Z', 2, ANALOG)>; /* SPI1_MOSI */
		};
	};
  1. 在顶层设备树中引用spi1节点,并加入如下内容:
&spi1 {
   
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&spi1_pins_a>;
	pinctrl-1 = <&spi1_sleep_pins_a>;
	cs-gpios = <&gpioz 3 GPIO_ACTIVE_LOW>, <&gpioa 14 GPIO_ACTIVE_LOW>;
	status = "okay";

	/* OLED屏幕 */
	oled@1 {
   
		compatible = "atk,oled";
		reg = <1>; /* CS #1 */
		spi-max-frequency = <1000000>;
		dc-gpios = <&gpioi 3 GPIO_ACTIVE_LOW>;
		rst-gpios = <&gpioi 11 GPIO_ACTIVE_LOW>;
	};
};
  1. 用make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树,并用新的.dtb文件启动系统

使能SPI控制器驱动

内核中使能 SPI 控制器驱动, ST 默认将SPI控制器驱动编译为模块,使能步骤如下:

  1. 执行命令make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- menuconfig打开内核配置菜单
  2. 进行如下配置
Device Drivers
	SPI support (SPI [=y])
		<*> STMicroelectronics STM32 SPI controller //编译进内核
  1. 使用命令make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- all LOADADDR=0XC2000040 -j16编译内核
  2. 使用命令make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- uImage dtbs LOADADDR=0XC2000040 -j16生成uImage

驱动代码编写

OLED驱动程序基于SPI总线驱动框架和缓冲帧驱动框架编写,有关缓冲帧的内容参考8.1缓冲帧(Framebuffer)驱动框架8.2LCD-TFT显示控制器驱动 (LCD驱动)部分,驱动代码主要包括以下几个部分:

  1. 注册/注销SPI设备驱动
  2. 注册/注销缓冲帧驱动
  3. OLED初始化
  4. OLED显示更新
    驱动代码的完成内容如下所示:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>

#define OLED_DISPLAY_RAM_SIZE	(8*128)

struct oled_handle{
   
	struct spi_device *spi;				//oled所属spi设备
	int rst_gpio;						//复位引脚
	int dc_gpio;						//数据命令选择引脚
	struct task_struct *kthread;		//用于将显存内容更新到OLED的内核线程
	uint8_t (*oled_buffer)[128];		//oled buffer,将缓冲帧中的数据转换为OLED格式后在通过SPI总线发送到OLED
	struct fb_info *fb;					//缓冲帧句柄
	unsigned int pseudo_palette[16];	//调色板
	uint8_t (*fb_buffer)[16];			//缓冲帧
	dma_addr_t phy_addr;				//缓冲帧物理地址
	uint8_t (*old_fb_buffer)[16];		//缓冲帧上一次更新时的状态
};

//初始化OLED的复位引脚和数据命令选择引脚
static int devm_pin_init(struct oled_handle *oled)
{
   
	int result;

	//获取RST GPIO号
	oled->rst_gpio = of_get_named_gpio(oled->spi->dev.of_node, "rst-gpios"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值