欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 美景 > SPI驱动学习四(通过SPI操作外设模块)

SPI驱动学习四(通过SPI操作外设模块)

2024/10/24 21:35:48 来源:https://blog.csdn.net/weixin_45842280/article/details/141832233  浏览:    关键词:SPI驱动学习四(通过SPI操作外设模块)

目录

  • 一、SPI_DAC模块
    • 1. 硬件
      • 1.1 原理图
      • 1.2 连接
    • 2. DAC操作原理
      • 2.1 内部框图
      • 2.2 时序图
      • 2.3 DAC公式
    • 3. 代码实现
      • 3.1 驱动部分
      • 3.2 APP部分
  • 二、SPI OLED模块
    • 1. 硬件
      • 1.1 原理图
      • 1.2 连接
    • 2. oled工作原理
      • 2.1 显存和像素
      • 2.2 显存寻址模式
      • 2.3 具体操作
        • 2.3.1 初始化
        • 2.3.2 设置地址
        • 2.3.4 写入数据
    • 3. 代码实现
    • 3.1 驱动部分
    • 3.2 APP部分

一、SPI_DAC模块

参考资料:
* 内核驱动:`drivers\spi\spidev.c`
* 内核提供的测试程序:`tools\spi\spidev_fdx.c`
* 内核文档:`Documentation\spi\spidev`	
* DAC芯片手册:`TLC5615.pdf`

1. 硬件

1.1 原理图

在这里插入图片描述

1.2 连接

DAC模块接到主控扩展板的SPI_A插座上:
在这里插入图片描述

2. DAC操作原理

2.1 内部框图

操作过程为:

  • CS为低,选中SPI从设备;
  • 在SCLK的上升沿,向DIN传入16位数据,存入下图中的16-Bit Shift Register
  • 在CS的上升沿,把16-Bit Shift Register中的10位数据传入10-Bit DAC Register,作为模拟量在OUT引脚输出
    在这里插入图片描述
    注意
  • 如图可知传输的16位数据中,高4位是无意义的
  • 中间10位才被转换为模拟量,传入到了DAC Register
  • 最低2位必须是0

2.2 时序图

在这里插入图片描述
使用SPI传输的细节:

  • SCLK初始电平为低
  • 使用16个SCLK周期来传输16位数据
  • 在SCLK上升沿从DIN输入数据,作为模拟量在OUT引脚输出
  • 在SCLK上升沿读取DOUT信号
  • DOUT数据来自16-Bit Shift Register
    • 第1个数据是上次数据遗留下的LSB位(最低位)
    • 其余15个数据来自16-Bit Shift Register的高15位
    • 16-Bit Shift Register的LSB在下一个周期的第1个时钟传输
    • LSB必定是0,所以当前的周期里读出16-Bit Shift Register的15位数据也足够了

2.3 DAC公式

在这里插入图片描述

输出电压 = 2 * VREFIN * n / 1024 = 2 * 2.048 * n / 1024
其中: n为10位数值

3. 代码实现

3.1 驱动部分

  编写设备树: 根据 dac 数据手册确认确认SPI时钟最大频率;

周期:T = 25 + 25 = 50ns
频率:F = 20000000 = 20MHz

设备树如下:

    dac: dac {compatible = "spidev";reg = <0>;spi-max-frequency = <20000000>;};

  根据主控板原理图确认接到 SOC 的那一路spi了。
在这里插入图片描述
  DAC模块接在这个插座上,那么要在设备树里spi1的节点下创建子节点。在arch/arm/boot/dts/中找到主控板对应的设备树文件,添加如下内容:

&ecspi1 {                        // 引用名为 ecspi1 的 SPI 控制器节点pinctrl-names = "default";   // 指定引脚控制组的名称为 "default"pinctrl-0 = <&pinctrl_ecspi1>; // 使用名为 pinctrl_ecspi1 的引脚配置fsl,spi-num-chipselects = <2>; // SPI 控制器支持 2 个芯片选择信号cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, // 芯片选择 0 的 GPIO 配置,该信息也是根据原理图或者芯片手册得来<&gpio4 24 GPIO_ACTIVE_LOW>; // 芯片选择 1 的 GPIO 配置status = "okay";              // 启用该 SPI 控制器dac: dac {                    // 定义一个名为 dac 的子节点,代表一个 SPI 设备compatible = "spidev";    // 设备兼容性为 spidev(通用 SPI 设备接口)reg = <0>;                // 从设备地址为 0spi-max-frequency = <20000000>; // SPI 设备支持的最大时钟频率为 20 MHz};
};

  编译spidev驱动:首先要确定内核中已经含有spidev。在内核目录下执行make menuconfig,查看是否有改驱动,如下图:

-> Device Drivers-> SPI support (SPI [=y]) < >   User mode SPI device driver support  

如果User mode SPI device driver support前面不是<Y><M>,可以输入M表示把它编译为模块。

  • 如果已经是<Y>,则不用再做其他事情。
  • 如果你设置为<M>,在内核目录下执行make modules,把生成的drivers/spi/spidev.ko部署到主控板;

3.2 APP部分

/* 参考: tools\spi\spidev_fdx.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>#include <linux/types.h>
#include <linux/spi/spidev.h>/* dac_test /dev/spidevB.D <val> */
int main(int argc, char **argv)
{int fd;unsigned int val;struct spi_ioc_transfer	xfer[1];int status;unsigned char tx_buf[2];	unsigned char rx_buf[2];	if (argc != 3){printf("Usage: %s /dev/spidevB.D <val>\n", argv[0]);return 0;}fd = open(argv[1], O_RDWR);if (fd < 0) {printf("can not open %s\n", argv[1]);return 1;}val = strtoul(argv[2], NULL, 0);val <<= 2;     /* bit0,bit1 = 0b00 */val &= 0xFFC;  /* 只保留10bit *///数据传入16位移位寄存器时,先传入的是高位地址的字节tx_buf[1] = val & 0xff;tx_buf[0] = (val>>8) & 0xff;	memset(xfer, 0, sizeof xfer);xfer[0].tx_buf = tx_buf;xfer[0].rx_buf = rx_buf;xfer[0].len = 2;status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);if (status < 0) {printf("SPI_IOC_MESSAGE\n");return -1;}/* 打印 */val = (rx_buf[0] << 8) | (rx_buf[1]);val >>= 2;printf("Pre val = %d\n", val);return 0;
}

编译APP:

arm-buildroot-linux-gnueabihf-gcc  -o  dac_test  dac_test.c

上机实验:
如果spidev没有被编译进内核,那么先执行:

insmod spidev.ko

确定设备节点:

ls /dev/spidev*

假设设备节点为/dev/spidev0.0,执行测试程序:

./dac_test  /dev/spidev0.0  500
./dac_test  /dev/spidev0.0  600
./dac_test  /dev/spidev0.0  1000

二、SPI OLED模块

显示屏型号为:SSD1306-Revision 1.1 (Charge Pump);
显示屏芯片对应数据手册已和本篇博客进行资源绑定;
显示屏模块数据手册:SPEC UG-2864TMBEG01 https://www.sekorm.com/product/501837191.html

1. 硬件

1.1 原理图

在这里插入图片描述

1.2 连接

在这里插入图片描述

2. oled工作原理

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

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

2.1 显存和像素

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

  • byte0的8位数据对应屏幕上角左侧、竖向排列的8个像素,即COL0的像素,bit0对应第0行,bit1对应第1行,……
  • byte0对应COL1那列第0~第7行的8个像素
    * ……
  • byte127对应COL127那列第0~第7行的8个像素
  • byte128对应COL0那列第0~第7行的8个像素
    在这里插入图片描述

2.2 显存寻址模式

显存被分为8页、127列,要写某个字节时,需要先指定地址,然后写入1字节的数据

  • 哪页(Page)?
  • 哪列(Col)?
  • 写入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,指向左上角
      在这里插入图片描述

2.3 具体操作

2.3.1 初始化

  对于OLED的初始化,在参考手册SPEC UG-2864TMBEG01 .pdf中列出的流程图:
在这里插入图片描述

2.3.2 设置地址
  • 设置地址模式,比如设置为页地址模式时,先写命令0x22,再写命令0x02。页地址模式是默认的,可以不设置。
    在这里插入图片描述* 设置页地址:有0~7页,想设置哪一页(n)就发出命令:0xB0 | n
    在这里插入图片描述
  • 设置列地址:列地址范围是0~127,需要使用2个命令来设置列地址
    在这里插入图片描述
2.3.4 写入数据

  让DC引脚为高,发起SPI写操作即可。

3. 代码实现

3.1 驱动部分

&ecspi1 {pinctrl-names = "default";pinctrl-0 = <&pinctrl_ecspi1>;fsl,spi-num-chipselects = <2>;cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;status = "okay";oled: oled {compatible = "100ask,oled";reg = <0>;spi-max-frequency = <10000000>; //来源于oled芯片数据手册,由周期计算而来dc-gpios = <&gpio4 20 GPIO_ACTIVE_HIGH>; };
};

  整体过程与SPI DAC模块类似,将该设备树内容添加到主控板设备树文件,重新编译内核,烧录到主控板重启即可;

3.2 APP部分

  将汉字” 百问网“转换成像素点数据

//font.h
#ifndef  _FONT_H_
#define _FONT_H_
const unsigned char oled_asc2_8x16[95][16]=
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 0{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00},//!1{0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//"2{0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00},//#3{0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00},//$4{0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00},//%5{0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10},//&6{0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//'7{0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},//(8{0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},//)9{0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},//*10{0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00},//+11{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00},//,12{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01},//-13{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},//.14{0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00},///15{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},//016{0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//117{0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},//218{0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00},//319{0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00},//420{0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00},//521{0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00},//622{0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00},//723{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},//824{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00},//925{0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},//:26{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00},//;27{0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},//<28{0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00},//=29{0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},//>30{0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00},//?31{0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00},//@32{0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},//A33{0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},//B34{0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},//C35{0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},//D36{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},//E37{0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},//F38{0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},//G39{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},//H40{0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//I41{0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},//J42{0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},//K43{0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},//L44{0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00},//M45{0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},//N46{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},//O47{0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},//P48{0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00},//Q49{0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},//R50{0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},//S51{0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//T52{0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//U53{0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},//V54{0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00},//W55{0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},//X56{0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},//Y57{0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},//Z58{0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},//[59{0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},//\60{0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},//]61{0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//^62{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},//_63{0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//`64{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20},//a65{0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},//b66{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},//c67{0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20},//d68{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00},//e69{0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//f70{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},//g71{0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//h72{0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//i73{0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},//j74{0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00},//k75{0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},//l76{0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},//m77{0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},//n78{0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},//o79{0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00},//p80{0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80},//q81{0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},//r82{0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},//s83{0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00},//t84{0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},//u85{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00},//v86{0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00},//w87{0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00},//x88{0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00},//y89{0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},//z90{0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40},//{91{0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00},//|92{0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00},//}93{0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//~94
};      
const unsigned char hz_1616[][32]={
{0x02,0x00,0x02,0x00,0xE2,0xFF,0x22,0x42,0x22,0x42,0x32,0x42,0x2A,0x42,0x26,0x42,0x22,0x42,0x22,0x42,0x22,0x42,0x22,0x42,0xE2,0xFF,0x02,0x00,0x02,0x00,0x00,0x00},/*"百",0*/
{0x00,0x00,0xF8,0xFF,0x01,0x00,0x02,0x00,0x00,0x00,0xE2,0x1F,0x22,0x08,0x22,0x08,0x22,0x08,0xE2,0x1F,0x02,0x00,0x02,0x40,0x02,0x80,0xFE,0x7F,0x00,0x00,0x00,0x00},/*"问",1*/
{0x00,0x00,0xFE,0xFF,0x02,0x10,0x22,0x08,0x42,0x06,0x82,0x01,0x72,0x0E,0x02,0x10,0x22,0x08,0x42,0x06,0x82,0x01,0x72,0x4E,0x02,0x80,0xFE,0x7F,0x00,0x00,0x00,0x00},/*"网",2*/
};
#endif

  编写程序操作通过spi操作oled屏;

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>#include <linux/types.h>
#include <linux/spi/spidev.h>#include "font.h"static int fd_spidev;
static int dc_pin_num;void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd);
int oled_init(void);
int oled_fill_data(unsigned char fill_Data);
void OLED_DIsp_Clear(void);
void OLED_DIsp_All(void);
void OLED_DIsp_Set_Pos(int x, int y);
void OLED_DIsp_Char(int x, int y, unsigned char c);
void OLED_DIsp_String(int x, int y, char *str);
void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no);
void OLED_DIsp_Test();void dc_pin_init(int number)
{// echo 509 > /sys/class/gpio/export// echo out > /sys/class/gpio/gpio509/direction	char cmd[100];dc_pin_num = number;sprintf(cmd, "echo %d > /sys/class/gpio/export", number);system(cmd);sprintf(cmd, "echo out > /sys/class/gpio/gpio%d/direction", number);system(cmd);	
}void oled_set_dc_pin(int val)
{/* echo 1 > /sys/class/gpio/gpio509/value* echo 0 > /sys/class/gpio/gpio509/value*/char cmd[100];sprintf(cmd, "echo %d > /sys/class/gpio/gpio%d/value", val, dc_pin_num);system(cmd);	
}void spi_write_datas(const unsigned char *buf, int len)
{write(fd_spidev, buf, len);
}void oled_write_datas(const unsigned char *buf, int len)
{oled_set_dc_pin(1);//拉高,表示写入数据spi_write_datas(buf, len);
}/*********************************************************************** 函数名称: oled_write_cmd* 功能描述: oled向特定地址写入数据或者命令* 输入参数:@uc_data :要写入的数据@uc_cmd:为1则表示写入数据,为0表示写入命令* 输出参数:无* 返 回 值: 无* 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/04		 V1.0	  芯晓		  创建***********************************************************************/
void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd)
{unsigned char uc_read=0;if(uc_cmd==0){oled_set_dc_pin(0);}else{oled_set_dc_pin(1);//拉高,表示写入数据}spi_write_datas(&uc_data, 1);//写入
}
/*********************************************************************** 函数名称: oled_init* 功能描述: oled_init的初始化,包括SPI控制器得初始化* 输入参数:无* 输出参数: 初始化的结果* 返 回 值: 成功则返回0,否则返回-1* 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建***********************************************************************/
int oled_init(void)
{unsigned char uc_dev_id = 0;oled_write_cmd_data(0xae,OLED_CMD);//关闭显示oled_write_cmd_data(0x00,OLED_CMD);//设置 lower column addressoled_write_cmd_data(0x10,OLED_CMD);//设置 higher column addressoled_write_cmd_data(0x40,OLED_CMD);//设置 display start lineoled_write_cmd_data(0xB0,OLED_CMD);//设置page addressoled_write_cmd_data(0x81,OLED_CMD);// contract controloled_write_cmd_data(0x66,OLED_CMD);//128oled_write_cmd_data(0xa1,OLED_CMD);//设置 segment remapoled_write_cmd_data(0xa6,OLED_CMD);//normal /reverseoled_write_cmd_data(0xa8,OLED_CMD);//multiple ratiooled_write_cmd_data(0x3f,OLED_CMD);//duty = 1/64oled_write_cmd_data(0xc8,OLED_CMD);//com scan directionoled_write_cmd_data(0xd3,OLED_CMD);//set displat offsetoled_write_cmd_data(0x00,OLED_CMD);//oled_write_cmd_data(0xd5,OLED_CMD);//set osc divisionoled_write_cmd_data(0x80,OLED_CMD);//oled_write_cmd_data(0xd9,OLED_CMD);//ser pre-charge periodoled_write_cmd_data(0x1f,OLED_CMD);//oled_write_cmd_data(0xda,OLED_CMD);//set com pinsoled_write_cmd_data(0x12,OLED_CMD);//oled_write_cmd_data(0xdb,OLED_CMD);//set vcomholed_write_cmd_data(0x30,OLED_CMD);//oled_write_cmd_data(0x8d,OLED_CMD);//set charge pump disable oled_write_cmd_data(0x14,OLED_CMD);//oled_write_cmd_data(0xaf,OLED_CMD);//set dispkay onreturn 0;
}		  			 		  						  					  				 	   		  	  	 	  
/*********************************************************************** 函数名称: oled_fill_data* 功能描述: 整个屏幕显示填充某个固定数据* 输入参数:@fill_Data:要填充的数据* 输出参数: 填充结果* 返 回 值: 成功则返回0* 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建***********************************************************************/
int oled_fill_data(unsigned char fill_Data)
{unsigned char x,y;for(x=0;x<8;x++){oled_write_cmd_data(0xb0+x,OLED_CMD);		//page0-page1oled_write_cmd_data(0x00,OLED_CMD);		//low column start addressoled_write_cmd_data(0x10,OLED_CMD);		//high column start address	for(y=0;y<128;y++){oled_write_cmd_data(fill_Data,OLED_DATA);//填充数据}				}return 0;
}
/*********************************************************************** 函数名称: OLED_DIsp_Clear* 功能描述: 整个屏幕显示数据清0* 输入参数:无* 输出参数: 无* 返 回 值: * 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建***********************************************************************/
void OLED_DIsp_Clear(void)  
{unsigned char x, y;char buf[128];memset(buf, 0, 128);for (y = 0; y < 8; y++){OLED_DIsp_Set_Pos(0, y);//for (x = 0; x < 128; x++)//    oled_write_cmd_data(0, OLED_DATA); /* 清零 */oled_write_datas(buf, 128);}
}/*********************************************************************** 函数名称: OLED_DIsp_All* 功能描述: 整个屏幕显示全部点亮,可以用于检查坏点* 输入参数:无* 输出参数:无 * 返 回 值:* 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建***********************************************************************/
void OLED_DIsp_All(void)  
{unsigned char x, y;for (y = 0; y < 8; y++){OLED_DIsp_Set_Pos(0, y);for (x = 0; x < 128; x++)oled_write_cmd_data(0xff, OLED_DATA); /* 全点亮 */}
}//坐标设置
/*********************************************************************** 函数名称: OLED_DIsp_Set_Pos* 功能描述:设置要显示的位置* 输入参数:@ x :要显示的column address@y :要显示的page address* 输出参数: 无* 返 回 值: * 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建***********************************************************************/
void OLED_DIsp_Set_Pos(int x, int y)
{ 	oled_write_cmd_data(0xb0+y,OLED_CMD);oled_write_cmd_data((x&0x0f),OLED_CMD); oled_write_cmd_data(((x&0xf0)>>4)|0x10,OLED_CMD);
}   	      	   			 
/*********************************************************************** 函数名称: OLED_DIsp_Char* 功能描述:在某个位置显示字符 1-9* 输入参数:@ x :要显示的column address@y :要显示的page address@c :要显示的字符的ascii码* 输出参数: 无* 返 回 值: * 修改日期		版本号	  修改人 	   修改内容* -----------------------------------------------* 2020/03/15		  V1.0	   芯晓		   创建
***********************************************************************/
void OLED_DIsp_Char(int x, int y, unsigned char c)
{int i = 0;/* 得到字模 */const unsigned char *dots = oled_asc2_8x16[c - ' '];/* 发给OLED */OLED_DIsp_Set_Pos(x, y);/* 发出8字节数据 *///for (i = 0; i < 8; i++)//	oled_write_cmd_data(dots[i], OLED_DATA);oled_write_datas(&dots[0], 8);OLED_DIsp_Set_Pos(x, y+1);/* 发出8字节数据 *///for (i = 0; i < 8; i++)//oled_write_cmd_data(dots[i+8], OLED_DATA);oled_write_datas(&dots[8], 8);
}/*********************************************************************** 函数名称: OLED_DIsp_String* 功能描述: 在指定位置显示字符串* 输入参数:@ x :要显示的column address@y :要显示的page address@str :要显示的字符串* 输出参数: 无* 返 回 值: 无* 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建
***********************************************************************/
void OLED_DIsp_String(int x, int y, char *str)
{unsigned char j=0;while (str[j]){		OLED_DIsp_Char(x, y, str[j]);//显示单个字符x += 8;if(x > 127){x = 0;y += 2;}//移动显示位置j++;}
}
/*********************************************************************** 函数名称: OLED_DIsp_CHinese* 功能描述:在指定位置显示汉字* 输入参数:@ x :要显示的column address@y :要显示的page address@chr :要显示的汉字,三个汉字“百问网”中选择一个* 输出参数: 无* 返 回 值: 无* 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建***********************************************************************/void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no)
{      			    unsigned char t,adder=0;OLED_DIsp_Set_Pos(x,y);	for(t=0;t<16;t++){//显示上半截字符	oled_write_cmd_data(hz_1616[no][t*2],OLED_DATA);adder+=1;}	OLED_DIsp_Set_Pos(x,y+1);	for(t=0;t<16;t++){//显示下半截字符oled_write_cmd_data(hz_1616[no][t*2+1],OLED_DATA);adder+=1;}					
}
/*********************************************************************** 函数名称: OLED_DIsp_Test* 功能描述: 整个屏幕显示测试* 输入参数:无* 输出参数: 无* 返 回 值: 无* 修改日期 	   版本号	 修改人		  修改内容* -----------------------------------------------* 2020/03/15		 V1.0	  芯晓		  创建***********************************************************************/
void OLED_DIsp_Test(void)
{ 	int i;OLED_DIsp_String(0, 0, "wiki.100ask.net");OLED_DIsp_String(0, 2, "book.100ask.net");OLED_DIsp_String(0, 4, "bbs.100ask.net");for(i = 0; i < 3; i++){   //显示汉字 百问网OLED_DIsp_CHinese(32+i*16, 6, i);}
} /* spi_oled /dev/spidevB.D <DC_pin_number> */
int main(int argc, char **argv)
{int dc_pin;if (argc != 3){printf("Usage: %s <dev/spidevB.D> <DC_pin_number>\n", argv[0]);return -1;}fd_spidev = open(argv[1], O_RDWR);if (fd_spidev < 0) {printf("open %s err\n", argv[1]);return -1;}dc_pin = strtoul(argv[2], NULL, 0);dc_pin_init(dc_pin);oled_init();OLED_DIsp_Clear();OLED_DIsp_Test();return 0;
}

  本文章参考了韦东山老师驱动大全部分笔记,其余内容为自己整理总结而来。水平有限,欢迎各位在评论区指导交流!!!😁😁😁

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com