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

目录

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

一、SPI_DAC模块

c 复制代码
参考资料:
* 内核驱动:`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公式

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

3. 代码实现

3.1 驱动部分

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

shell 复制代码
周期:T = 25 + 25 = 50ns
频率:F = 20000000 = 20MHz

设备树如下:

shell 复制代码
    dac: dac {
        compatible = "spidev";
        reg = <0>;
        spi-max-frequency = <20000000>;
    };

根据主控板原理图确认接到 SOC 的那一路spi了。

DAC模块接在这个插座上,那么要在设备树里spi1的节点下创建子节点。在arch/arm/boot/dts/中找到主控板对应的设备树文件,添加如下内容:

shell 复制代码
&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>;                // 从设备地址为 0
        spi-max-frequency = <20000000>; // SPI 设备支持的最大时钟频率为 20 MHz
    };
};

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

shell 复制代码
-> 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部分

c 复制代码
/* 参考: 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:

shell 复制代码
arm-buildroot-linux-gnueabihf-gcc  -o  dac_test  dac_test.c

上机实验:

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

shell 复制代码
insmod spidev.ko

确定设备节点:

shell 复制代码
ls /dev/spidev*

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

shell 复制代码
./dac_test  /dev/spidev0.0  500
./dac_test  /dev/spidev0.0  600
./dac_test  /dev/spidev0.0  1000

二、SPI OLED模块

c 复制代码
显示屏型号为: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 驱动部分

shell 复制代码
&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部分

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

c 复制代码
//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屏;

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>

#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 address
	oled_write_cmd_data(0x10,OLED_CMD);//设置 higher column address

	oled_write_cmd_data(0x40,OLED_CMD);//设置 display start line

	oled_write_cmd_data(0xB0,OLED_CMD);//设置page address

	oled_write_cmd_data(0x81,OLED_CMD);// contract control
	oled_write_cmd_data(0x66,OLED_CMD);//128

	oled_write_cmd_data(0xa1,OLED_CMD);//设置 segment remap

	oled_write_cmd_data(0xa6,OLED_CMD);//normal /reverse

	oled_write_cmd_data(0xa8,OLED_CMD);//multiple ratio
	oled_write_cmd_data(0x3f,OLED_CMD);//duty = 1/64

	oled_write_cmd_data(0xc8,OLED_CMD);//com scan direction

	oled_write_cmd_data(0xd3,OLED_CMD);//set displat offset
	oled_write_cmd_data(0x00,OLED_CMD);//

	oled_write_cmd_data(0xd5,OLED_CMD);//set osc division
	oled_write_cmd_data(0x80,OLED_CMD);//

	oled_write_cmd_data(0xd9,OLED_CMD);//ser pre-charge period
	oled_write_cmd_data(0x1f,OLED_CMD);//

	oled_write_cmd_data(0xda,OLED_CMD);//set com pins
	oled_write_cmd_data(0x12,OLED_CMD);//

	oled_write_cmd_data(0xdb,OLED_CMD);//set vcomh
	oled_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 on

	return 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-page1
		oled_write_cmd_data(0x00,OLED_CMD);		//low column start address
		oled_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;
}

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

相关推荐
三菱-Liu2 小时前
三菱MR-J4-B伺服连接器和信号排列
网络·驱动开发·硬件工程·制造·mr
懒惰的bit2 小时前
基础网络安全知识
学习·web安全·1024程序员节
矛取矛求3 小时前
Linux如何更优质调节系统性能
linux
内核程序员kevin4 小时前
在Linux环境下使用Docker打包和发布.NET程序并配合MySQL部署
linux·mysql·docker·.net
kayotin4 小时前
Wordpress博客配置2024
linux·mysql·docker
Natural_yz5 小时前
大数据学习09之Hive基础
大数据·hive·学习
龙中舞王5 小时前
Unity学习笔记(2):场景绘制
笔记·学习·unity
Natural_yz5 小时前
大数据学习10之Hive高级
大数据·hive·学习
Ztiddler5 小时前
【Linux Shell命令-不定期更新】
linux·运维·服务器·ssh
小小不董5 小时前
Oracle OCP认证考试考点详解082系列16
linux·运维·服务器·数据库·oracle·dba