17.【linux驱动】IIC驱动OLED屏(GPIO 模拟)

IIC系列文章
1.【linux驱动】IIC驱动-硬件、协议
2.【linux驱动】IIC驱动OLED屏(GPIO 模拟)
3.【linux驱动】IIC驱动(4418读取EEPROM:24AA025E48T-I/OT)
4.【linux驱动】IIC驱动OLED屏

driver

ARM-cortex系列的CPU不像一般的单片机,CPU的速度远大于GPIO频率,多次对电平的设置也会被合并成一次出现在GPIO上,另外IO电平翻转得特别快,也必须加入延时函数,否则设备无法正常通信。

GPIO模拟IIC只做master写入模式
下面直接上代码。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <mach/soc.h>
#include <linux/delay.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

dev_t devid;
struct cdev char_dev;
struct class * char_class;
int buffer_size = 100;
char * char_data;

#pragma pack(4)
static struct GPIO_B{
	unsigned int out_put;
	unsigned int out_enb;
	unsigned int detect_md_0;
	unsigned int detect_md_1;
	unsigned int int_enb;
	unsigned int event_detect;
	unsigned int pad_state;
	unsigned int resv;
	unsigned int func0;
	unsigned int func1;
	unsigned int DETMODEEX;
	unsigned int DETENB;
	unsigned int SLEW;
	unsigned int SLEW_DISABLE_DEFAULT;
	unsigned int DRV1;
	unsigned int DRV1_DISABLE_DEFAULT;
	unsigned int DRV0;
	unsigned int DRV0_DISABLE_DEFAULT;
	unsigned int pull_sell;
	unsigned int PULLSEL_DISABLE_DEFAULT;
	unsigned int pull_enb;
}* gpio_b;
#pragma pack()

#define SCL_H (gpio_b->out_put |= (1 << 31))
#define SCL_L (gpio_b->out_put &= ~(1 << 31))

#define SDA_H (gpio_b->out_put |= (1 << 30))
#define SDA_L (gpio_b->out_put &= ~(1 << 30))

#define cycle_ns 	1000 //iic周期,单位纳秒

/**********************************************
//IIC Start
**********************************************/
inline void IIC_Start(void)
{
   SCL_H;
   SDA_H;
   ndelay(cycle_ns);
   SDA_L;
   ndelay(cycle_ns);
   SCL_L;
   	ndelay(cycle_ns/2); //等待电平稳定
}

/**********************************************
//IIC Stop
**********************************************/
inline void IIC_Stop(void)
{
   SCL_L;
   SDA_L;
   ndelay(cycle_ns);
   SCL_H;
   ndelay(cycle_ns);
   SDA_H;
   ndelay(400);
}

/**********************************************
// 通过I2C总线写一个字节
**********************************************/
inline void Write_IIC_Byte(unsigned char IIC_Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		if(IIC_Byte & 0x80)
			SDA_H;
		else
			SDA_L;
   		ndelay(cycle_ns/2); //等待电平稳定
		SCL_H;
   		ndelay(cycle_ns); //维持采样时间
		SCL_L;
   		ndelay(cycle_ns/2); //等待电平稳定
		IIC_Byte<<=1;
	}
	SDA_H;
	gpio_b->out_enb &= ~(1 << 30);
   	ndelay(cycle_ns/2); //等待电平稳定
	SCL_H;
   	ndelay(cycle_ns); //等待ack
	gpio_b->out_enb |= (1 << 30);
	SCL_L;
	SDA_L;
   	ndelay(40); //下次传输间隔ack
}

static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){
	unsigned int dir,size,i;
	dir = _IOC_DIR(cmd);
	size = _IOC_SIZE(cmd);

	if(dir == _IOC_WRITE){
		i = size;
		while(i > 0){
			i = copy_from_user(char_data,(unsigned char *)arg,i);
		}
		IIC_Start();
		for (i = 0; i < size; ++i)
		{
			Write_IIC_Byte(char_data[i]);
		}
		IIC_Stop();
	}
	return 0;
}


struct file_operations my_opts = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = ioctl
};

static int __init iic_init(void){
	int ret = 0;

    devid = MKDEV(241, 1);								//换算设备号
    ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到
    if (ret < 0)
        goto err0;

    cdev_init(&char_dev,&my_opts);						//绑定opt结构体
    char_dev.owner = THIS_MODULE;
    ret = cdev_add(&char_dev,devid,1);					//注册字符设备驱动
    if (ret < 0)
    	goto err1;

    char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹
    device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1

    char_data = kzalloc(buffer_size,GFP_KERNEL);

	gpio_b = (struct GPIO_B *)ioremap(0xc001b000,sizeof(struct GPIO_B));			//映射地址

	gpio_b->func1 |= (1 << 30);
	gpio_b->func1 |= (1 << 28);
	printk("gpio b:%x\n",gpio_b->func1);

	gpio_b->out_enb |= (1 << 31);
	gpio_b->out_enb |= (1 << 30);
	gpio_b->pull_enb &= ~(1 << 30);
	// gpio_b->pull_sell |= (1 << 30);
	SDA_H;
	SCL_H;

	printk("iic init\n");
    return 0;

	err1:
	    unregister_chrdev_region(devid, 1);
    err0:
        return ret;
}

static void __exit iic_exit(void){
	SDA_H;
	SCL_H;
	iounmap(gpio_b);

	unregister_chrdev_region(devid, 1);
	cdev_del(&char_dev);
	device_destroy(char_class,devid);
	class_destroy(char_class);
	printk("iic exit\n");
}

module_init(iic_init);
module_exit(iic_exit);

application

#include <stdio.h>
#include <linux/ioctl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include "codetab.h"

#define	Brightness	0xCF 
#define X_WIDTH 	128
#define Y_WIDTH 	64

int fd;
/*********************OLED写数据************************************/ 
inline int OLED_WrDat(unsigned char IIC_Data)
{
	int ret,cmd;
	unsigned char data[] = {0x78,0x40,IIC_Data};
	cmd = _IOC(_IOC_WRITE,0x00,0x00,0x03);
	ret = ioctl(fd,cmd,data);
	if(ret < 0){
		perror("ioctl error");
		return ret;
	}
	return 0;
}
/*********************OLED写命令************************************/
inline int OLED_WrCmd(unsigned char IIC_Command)
{
	int ret,cmd;
	unsigned char data[] = {0x78,0x00,IIC_Command};
	cmd = _IOC(_IOC_WRITE,0x00,0x00,0x03);
	ret = ioctl(fd,cmd,data);
	if(ret < 0){
		perror("ioctl error");
		return ret;
	}
	return 0;
}
/*********************OLED 设置坐标************************************/
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 
	OLED_WrCmd(0xb0+y);
	OLED_WrCmd(((x&0xf0)>>4)|0x10);
	OLED_WrCmd((x&0x0f)|0x01);
} 
/*********************OLED全屏************************************/
void OLED_Fill(unsigned char bmp_dat) 
{
	unsigned char y,x;
	for(y=0;y<8;y++)
	{
		OLED_WrCmd(0xb0+y);
		OLED_WrCmd(0x01);
		OLED_WrCmd(0x10);
		for(x=0;x<X_WIDTH;x++)
		OLED_WrDat(bmp_dat);
	}
}
/*********************OLED复位************************************/
void OLED_CLS(void)
{
	unsigned char y,x;
	for(y=0;y<8;y++)
	{
		OLED_WrCmd(0xb0+y);
		OLED_WrCmd(0x01);
		OLED_WrCmd(0x10);
		for(x=0;x<X_WIDTH;x++)
		OLED_WrDat(0);
	}
}
/*********************OLED初始化************************************/
void OLED_Init(void)
{
	OLED_WrCmd(0xae);//--turn off oled panel
	OLED_WrCmd(0x00);//---set low column address
	OLED_WrCmd(0x10);//---set high column address
	OLED_WrCmd(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WrCmd(0x81);//--set contrast control register
	OLED_WrCmd(Brightness); // Set SEG Output Current Brightness
	OLED_WrCmd(0xa1);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WrCmd(0xc8);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WrCmd(0xa6);//--set normal display
	OLED_WrCmd(0xa8);//--set multiplex ratio(1 to 64)
	OLED_WrCmd(0x3f);//--1/64 duty
	OLED_WrCmd(0xd3);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WrCmd(0x00);//-not offset
	OLED_WrCmd(0xd5);//--set display clock divide ratio/oscillator frequency
	OLED_WrCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WrCmd(0xd9);//--set pre-charge period
	OLED_WrCmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WrCmd(0xda);//--set com pins hardware configuration
	OLED_WrCmd(0x12);
	OLED_WrCmd(0xdb);//--set vcomh
	OLED_WrCmd(0x40);//Set VCOM Deselect Level
	OLED_WrCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WrCmd(0x02);//
	OLED_WrCmd(0x8d);//--set Charge Pump enable/disable
	OLED_WrCmd(0x14);//--set(0x10) disable
	OLED_WrCmd(0xa4);// Disable Entire Display On (0xa4/0xa5)
	OLED_WrCmd(0xa6);// Disable Inverse Display On (0xa6/a7) 
	OLED_WrCmd(0xaf);//--turn on oled panel
	OLED_Fill(0x00); //初始清屏
	OLED_Set_Pos(0,0);
} 
/***************功能描述:显示6*8一组标准ASCII字符串	显示的坐标(x,y),y为页范围0~7****************/
void OLED_P6x8Str(unsigned char x,unsigned char y,unsigned char ch[])
{
	unsigned char c=0,i=0,j=0;
	while (ch[j]!='\0')
	{
		c =ch[j]-32;
		if(x>126){x=0;y++;}
		OLED_Set_Pos(x,y);
		for(i=0;i<6;i++)
		OLED_WrDat(F6x8[c][i]);
		x+=6;
		j++;
	}
}
/*******************功能描述:显示8*16一组标准ASCII字符串	 显示的坐标(x,y),y为页范围0~7****************/
void OLED_P8x16Str(unsigned char x,unsigned char y,unsigned char ch[])
{
	unsigned char c=0,i=0,j=0;
	while (ch[j]!='\0')
	{
		c =ch[j]-32;
		if(x>120){x=0;y++;}
		OLED_Set_Pos(x,y);
		for(i=0;i<8;i++)
		OLED_WrDat(F8X16[c*16+i]);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<8;i++)
		OLED_WrDat(F8X16[c*16+i+8]);
		x+=8;
		j++;
	}
}
/*****************功能描述:显示16*16点阵  显示的坐标(x,y),y为页范围0~7****************************/
void OLED_P16x16Ch(unsigned char x,unsigned char y,unsigned char N)
{
	unsigned char wm=0;
	unsigned int adder=32*N;
	OLED_Set_Pos(x , y);
	for(wm = 0;wm < 16;wm++)
	{
		OLED_WrDat(F16x16[adder]);
		adder += 1;
	}
	OLED_Set_Pos(x,y + 1);
	for(wm = 0;wm < 16;wm++)
	{
		OLED_WrDat(F16x16[adder]);
		adder += 1;
	} 	  	
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
void Draw_BMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
	unsigned int j=0;
	unsigned char x,y;

  if(y1%8==0) y=y1/8;      
  else y=y1/8+1;
	for(y=y0;y<y1;y++)
	{
		OLED_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
	    {      
	    	OLED_WrDat(BMP[j++]);
	    }
	}
}

int main(){
	int ret,cmd;
	fd = open("/dev/char_test_dev_1",O_RDWR);
	ret = fd;
	if(ret < 0){
		perror("open /dev/char_test_dev_1 error");
		return ret;
	}
	OLED_Init();
	OLED_Fill(0xff); //初始清屏
	close(ret);
	return 0;
}

结果波形图

从波形图看来哈市比较规整的
在这里插入图片描述
都是在SLK低电平中间改变SDL
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_16054639/article/details/106733887