SPI基础知识
SPI全称是serial peripheral interface,叫串行外设接口,是一种同步串行传输规范。
特点
1、高速,全双工,同步串行总线
并行通信:数据各位同时传送
串行:数据一位一位顺序传送
同步:主机和从机共用同一个时钟
异步:收发之间没有时钟
单工:信号只能在一个方向上传输,也就是智能发送和接收
半双工:信号可以在两个方向上传输,但是同一只可只允许一个方向
全双工:允许数据在两个方向上同时进行传输
2、主从两种模式,通常由一个主设备和多个从设备组成,SPI不支持多主机SPI不支持多主机SPI不支持多主机
3、SPI通信至少需要4条线,MISO,MOSI,SCLK,CS/SS
硬件连接方式


SPI通信原理
SPI没有起始信号,终止信号,应答信号,传输完一个字节之后立刻传输另外一个字节,速度较快(因为没有应答信号),数据安全性较低(因为没有应答信号),因此本人认为这个和UDP通信非常类似。
SPI总线驱动模型

驱动编写
我们将OLED挂载在spi总线下面
核心板引脚:

引脚引出来是:

所以设备树编写如下
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
oled:oled{
compatible = "100ask,oled";
reg = <0>;
}
驱动编写流程
- init函数
- exit函数
- spi_driver结构体
该结构体内包含.driver 、.probe 、.remove等属性 - of_device_id结构体
该结构体内包含多组compatible属性,匹配成功后会进入spi_driver结构体内的probe 函数 - file_operations结构体
可以在该结构体内注册操作函数,例如read,write,ioctl等函数 - probe函数:
在probe函数内部可以注册字符设备等操作具体外设的功能函数
完整的驱动代码如下:
c
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/list.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#define OLED_CMD 0
#define OLED_DATA 1
#define OLED_SET_XY 99
#define OLED_SET_XY_WRITE_DATA 100
#define OLED_SET_XY_WRITE_DATAS 101
#define OLED_SET_DATAS 102 /*102为低8位,高16伟表示长度*/
struct spi_device *oled_dev;
static struct gpio_desc *oled_dc;
static int major;
static struct class *oled_class;
static void oled_write_datas(unsigned char *buf,int len){
gpiod_set_value(oled_dc,1);
spi_write(oled_dev,buf,len);
}
static void oled_write_cmd_data(unsigned char uc_data,unsigned char uc_cmd){
// unsigned char uc_read=0;
if(uc_cmd == 0){
gpiod_set_value(oled_dc,0);
}
else{
gpiod_set_value(oled_dc,1);
}
spi_write(oled_dev,&uc_data,1);
}
static int oled_hardware_init(void){
unsigned char uc_dev_id = 0;
// GPIO4_GDIR_s = (volatile unsigned int *)(0x20a8000+0x4);
// GPIO4_DR_s = (volatile unsigned int *)(0x20a8000+0x4);
// spi_init(ESCPI1_BASE);
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);//设置 higher column address
oled_write_cmd_data(0x80,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 osc division
oled_write_cmd_data(0x00,OLED_CMD);//
oled_write_cmd_data(0xd9,OLED_CMD);//set 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;
}
static 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&0x0f)>>4)|0x10,OLED_CMD);
}
static void OLED_DIsp_Clear(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((y<4)?0:0xff,OLED_DATA);
}
}
}
static long oled_ioctl(struct file *file, unsigned int cmd,unsigned long arg){
const void __user *from = (const void __user *)arg;
char param_buf[3];
char data_buf[1024];
int size;
switch (cmd & 0xff)
{
case OLED_SET_XY:
{
copy_from_user(param_buf,from, 2);
OLED_DIsp_Set_Pos(param_buf[0],param_buf[1]);
break;;
}
case OLED_SET_XY_WRITE_DATA:
{
copy_from_user(param_buf,from, 3);
OLED_DIsp_Set_Pos(param_buf[0],param_buf[1]);
oled_write_cmd_data(param_buf[2], OLED_DATA);
/* code */
break;
}
case OLED_SET_XY_WRITE_DATAS:
{
copy_from_user(param_buf,from, 3);
size = param_buf[2];
copy_from_user(data_buf,from+3, size);
OLED_DIsp_Set_Pos(param_buf[0], param_buf[1]);
oled_write_datas(data_buf,size);
break;
}
case OLED_SET_DATAS:
{
size = cmd >> 8;
copy_from_user(data_buf,from, size);
oled_write_datas(data_buf,size);
break;
}
}
return 0;
}
static struct file_operations oled_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = oled_ioctl,
};
static int oled_probe(struct spi_device *spi)
{
printk("%s %s %d",__FILE__, __FUNCTION__, __LINE__);
printk("\n");
oled_dev = spi;
major = register_chrdev(0,"oled",&oled_fops);
oled_class = class_create(THIS_MODULE,"oled_class");
device_create(oled_class, NULL, MKDEV(major,0), NULL, "myoled");
/*spi oled init */
oled_dc = gpiod_get(&spi->dev,"dc",GPIOD_OUT_HIGH);
oled_hardware_init();
OLED_DIsp_Clear();
return 0;
}
static int oled_remove(struct spi_device *spi){
printk("%s %s %d",__FILE__, __FUNCTION__, __LINE__);
printk("\n");
device_destroy(oled_class, MKDEV(major,0) );
class_destroy(oled_class);
unregister_chrdev(major, "oled");
return 0;
}
// static const struct spi_device_id oled_spi_ids[]=
// {
// {"100ask,oled",},
// {}
// };
static const struct of_device_id oled_of_match[] =
{
{.compatible = "100ask,oled"},
{}
};
static struct spi_driver oled_driver = {
.driver = {
.name = "100ask,oled",
.of_match_table = oled_of_match,
},
.probe = oled_probe,
.remove = oled_remove,
// .id_table = oled_spi_ids,
};
int oled_init(void){
return spi_register_driver(&oled_driver);
}
static void oled_exit(void){
spi_unregister_driver(&oled_driver);
}
module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL v2");