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