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

SPI驱动(六) -- SPI_OLED上机实验(使用spidev)

文章目录

  • 参考资料:
  • 一、硬件
    • 1.1 原理图
    • 1.2 扩展板连接图
  • 二、 OLED显示原理
    • 2.1 显存和像素
    • 2.2 显存寻址模式
  • 三、OLED显示操作过程
    • 3.1 初始化
    • 3.2 设置显存地址
    • 3.3 往显存写入数据
  • 四、使用spidev操作SPI_OLED模块
    • 4.1 编写设备树
    • 4.2 编译spidev.c
    • 4.3 编写APP
      • 4.3.1怎么控制CD引脚
      • 4.3.2基于spidev编写APP
  • 五、上机实验
    • 5.1 修改,编译,替换设备树。
    • 5.2 编译spidev.c
    • 5.3 编译APP
    • 5.4 执行测试程序


参考资料:

参考资料:

  • 内核驱动:drivers\spi\spidev.c
  • 内核提供的测试程序:tools\spi\spidev_fdx.c
  • 内核文档:Documentation\spi\spidev
  • OLEDC芯片手册:SPEC UG-2864TMBEG01 .pdfSSD1306-Revision 1.1 (Charge Pump).pdf

一、硬件

1.1 原理图

在这里插入图片描述

1.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列,要写某个字节时,需要先指定地址(哪个page,哪个col),然后写入1字节的数据。

OLED有三种寻址模式:

(1)页地址模式(Page addressing mode):每写入1个字节,行地址不变,列地址增1,列地址达到127后会从0开始。
在这里插入图片描述
(2)水平地址模式(Horizontal addressing mode)

  • 每写入1个字节,行地址不变,列地址增1
  • 列地址达到127后从0开始,行地址指向下一页
  • 列地址达到127、行地址达到7时,列地址和行地址都被复位为0,指向左上角

在这里插入图片描述
(3)垂直地址模式(Vertical addressing mode):

  • 每写入1个字节,行地址增1,列地址不变
  • 行地址达到7后从0开始,列地址指向下一列
  • 列地址达到127、行地址达到7时,列地址和行地址都被复位为0,指向左上角

在这里插入图片描述

三、OLED显示操作过程

想让OLED显示内容,首先需要初始化OLED模块,接着设置现存地址,最后往现存写入数据。

3.1 初始化

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

3.2 设置显存地址

分为下面3步:

  • 设置地址模式
  • 设置page
  • 设置col

以页地址模式为例:

  • 设置地址模式:DC引脚保持低电平,表示写命令,先写命令0x20,告诉芯片开始设置地址模式,再写命令0x02,表示页地址模式。页地址模式是默认的,可以不设置。
    在这里插入图片描述
  • 设置页地址:有0~7页,想设置哪一页(n)就发出命令:0xB0 | n ,(B0-B7表示0-7页)
    在这里插入图片描述
    设置列地址:列地址范围是0~127,需要使用2个命令来设置列地址。
    在这里插入图片描述

3.3 往显存写入数据

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

四、使用spidev操作SPI_OLED模块

要做的事情:

  • 编写设备树
  • 编译spidev.c
  • 编写APP
    在这里插入图片描述

4.1 编写设备树

DC引脚使用GPIO4_20,不过只需要在APP里直接控制DC引脚,无需在设备树里指定。

修改设备树文件\arch\arm\boot\dts\100ask_imx6ull-14x14.dts

&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 = "spidev";
        reg = <0>;
        spi-max-frequency = <10000000>; //最大时钟,查看芯片手册计算
    };
};

4.2 编译spidev.c

内核把SPI_SPIDEV选择上。
在这里插入图片描述

4.3 编写APP

4.3.1怎么控制CD引脚

查看原理图,确认引脚,然后参考如下文档即可使用APP操作GPIO,假设引脚号码为100,C语言代码如下:

system("echo 100  > /sys/class/gpio/export"); //导出引脚
system("echo out > /sys/class/gpio/gpio100/direction"); //设置方向
system("echo 1 > /sys/class/gpio/gpio100/value"); //拉高
system("echo 0 > /sys/class/gpio/gpio100/value"); //拉低
system("echo 100 > /sys/class/gpio/unexport"); //去除引脚

4.3.2基于spidev编写APP

#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 g_fd_spidev;
static int g_dc_pin_num;

void OLED_DIsp_Set_Pos(int x, int y);


void dc_pin_init(int number)
{
	// echo 509 > /sys/class/gpio/export
	// echo out > /sys/class/gpio/gpio509/direction	

	char cmd[100];

	g_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, g_dc_pin_num);
	system(cmd);	
}


/* spi写接口 */
void spi_write_datas(const unsigned char *buf, int len)
{
	write(g_fd_spidev, buf, len);
}

/* oled写数据 */
void oled_write_datas(const unsigned char *buf, int len)
{
	oled_set_dc_pin(1);//拉高,表示写入数据
	spi_write_datas(buf, len);
}

/* oled写命令 */
void oled_write_cmd(unsigned char uc_data)
{

	oled_set_dc_pin(0);//拉低,表示写入数据
	spi_write_datas(&uc_data, 1);//写入
}

/* oled初始化 */
int oled_init(void)
{
	unsigned char uc_dev_id = 0;

	oled_write_cmd(0xae);//关闭显示

	oled_write_cmd(0x00);//设置 lower column address
	oled_write_cmd(0x10);//设置 higher column address

	oled_write_cmd(0x40);//设置 display start line

	oled_write_cmd(0xB0);//设置page address

	oled_write_cmd(0x81);// contract control
	oled_write_cmd(0x66);//128

	oled_write_cmd(0xa1);//设置 segment remap

	oled_write_cmd(0xa6);//normal /reverse

	oled_write_cmd(0xa8);//multiple ratio
	oled_write_cmd(0x3f);//duty = 1/64

	oled_write_cmd(0xc8);//com scan direction

	oled_write_cmd(0xd3);//set displat offset
	oled_write_cmd(0x00);//

	oled_write_cmd(0xd5);//set osc division
	oled_write_cmd(0x80);//

	oled_write_cmd(0xd9);//ser pre-charge period
	oled_write_cmd(0x1f);//

	oled_write_cmd(0xda);//set com pins
	oled_write_cmd(0x12);//

	oled_write_cmd(0xdb);//set vcomh
	oled_write_cmd(0x30);//

	oled_write_cmd(0x8d);//set charge pump disable 
	oled_write_cmd(0x14);//

	oled_write_cmd(0xaf);//set dispkay on

	return 0;
}		  			 		  						  					  				 	   		  	  	 	  

/* oled全灭 */
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);
        oled_write_datas(buf, 128);
    }
}

/* oled全亮 */
void OLED_DIsp_All(void)  
{
	unsigned char x, y;
    char buf[128];
    
    memset(buf, 0xff, 128);
    
	for (y = 0; y < 8; y++)
	{
		OLED_DIsp_Set_Pos(0, y);
        oled_write_datas(buf, 128);
	}
}

/* 寻址,哪一页,哪一列 */
void OLED_DIsp_Set_Pos(int x, int y)
{ 	
    oled_write_cmd(0xb0 + y);  //页
	oled_write_cmd((x & 0x0f)); //列低四位
	oled_write_cmd(((x & 0xf0) >> 4) | 0x10); //列高四位
}   	      	   			 


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字节数据 */
	oled_write_datas(&dots[0], 8);

	OLED_DIsp_Set_Pos(x, y+1);
	/* 发出8字节数据 */
	oled_write_datas(&dots[8], 8);
}


/* 写字符串 */
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++;
	}
}

/* 中文字符 */
void OLED_DIsp_CHinese(unsigned char x,unsigned char y,unsigned char no)
{      			    
	unsigned char t;
	OLED_DIsp_Set_Pos(x, y);	
    for(t=0; t<16; t++)
	{
	    //显示上半截字符	
		oled_write_datas(&hz_1616[no][t*2], 1);
    }	
	OLED_DIsp_Set_Pos(x, y+1);	
    for(t=0; t<16; t++)
	{
	    //显示下半截字符
		oled_write_datas(&hz_1616[no][t*2+1], 1);
    }					
}

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;
	}

	g_fd_spidev = open(argv[1], O_RDWR);
	if (g_fd_spidev < 0) {
		printf("open %s err\n", argv[1]);
		return -1;
	}

	dc_pin = strtoul(argv[2], NULL, 0);
	//DC引脚初始化
	dc_pin_init(dc_pin);

    //OLED 初始化
	oled_init();

    //清屏
	OLED_DIsp_Clear();

	//显示
	OLED_DIsp_Test();

    close(g_fd_spidev);
	return 0;
}

五、上机实验

5.1 修改,编译,替换设备树。

  • 修改设备树arch/arm/boot/dts/100ask_imx6ull-14x14.dts
&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 = "spidev";
        reg = <0>;
        spi-max-frequency = <10000000>; //最大时钟,查看芯片手册计算
    };
};
  • 编译设备树:
make dtbs
  • 替换设备树:
//挂载nfs
mount -t nfs -o nolock 192.168.124.22:/home/zpz/share/ /mnt
//拷贝设备树到开发板
cp /mnt/imx6ullsdk/repo/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dtb /boot/
  • 重启开发板

5.2 编译spidev.c

首先要确定内核中已经含有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,复制到开发板手动加载。

5.3 编译APP

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

可执行文件拷贝到开发板。

5.4 执行测试程序

./spi_oled /dev/spidev0.0 116

相关文章:

  • 梯度计算中常用的矩阵微积分公式
  • 计算机网络----主要内容简介
  • Qwen2.5-7B-Instruct进行自我认知微调
  • HTTP 黑科技
  • 为什么会有结构体?
  • 《几何原本》命题I.25
  • PCIE接口
  • spring 和JVM之间关系
  • Go学习笔记
  • 【RAG】检索后排序 提高回答精度
  • 初识Linux
  • 一周学会Flask3 Python Web开发-SQLAlchemy定义数据库模型
  • 【Linux篇】调试器-gdb/cgdb使用
  • Redis- 切片集群
  • Linux驱动学习之平台总线设备树驱动模型
  • vue3 使用sass变量
  • 基于STC89C52的温度检测系统(DS18B20)
  • 如何使用Webpack打包React项目?
  • idea启动项目报端口被占用
  • leetcode hot100 图论
  • 做网站现在赚钱吗/seo美式
  • 怎么用vs2010做网站/百度关键词seo公司
  • 福州优秀网站建设公司/长春百度网站快速排名
  • 网站建设电话推广话术/合肥seo代理商
  • 绵阳 网站开发/营销软文200字
  • 塔式服务器主机建网站/市场营销方案