S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动
目錄:一. 說明
    二. 驅動程序說明及問題
    三. 案例一
????? 四. 案例二
一. 說明
????? mini210開發板上帶了at24c08, 看了linux內核自帶的at24.c的驅動程序,編譯下載到看發板,讀寫都行;通過增加一些調試信息,對linux i2c驅動其中的編寫方法之一有了一定了解,在我的另外一篇博文有詳細說明。但同時對在linux下GPIO模擬i2c產生了興趣,于是就寫這篇博文來記錄驅動編寫過程中遇到的問題。如果想了解了i2c時序,請google或百度一下。
本篇博文通過misc驅動模型來編寫模擬i2c設備驅動:比如at24c08, pcf8591(AD/DA)。
本博文是本人原創,如有不足之處,請斧正;
二. 驅動程序說明及議題
1.1 本驅動使用s3c_gpio_cfgpin來配制硬件引腳寄存器,其實可以使用ioremap函數后,來對硬件寄存器地址訪問;如果想詳細了解,請看linux驅動初級開發第01篇--I/O內存控制硬件buzzer。
1.2 pcf8591(AD/DA)驅動程序,基本上沒遇到什么問題;但是編寫出來的atc08驅動程序,出現了些現在無法解決的問題;
問題1:只能先讀一個字節,再寫一個字節;
  問題2: 如果先寫一個字節,再讀一個字節會讀出現錯誤;但是去掉write_set_at24xx函數中i2c_send_byte(dat)就不會出現讀出錯,但是不會寫入數據。
  問題3:一次寫多個字節,就會出現一些字節寫出錯。
  問題4: 只讀就不個會出錯,但只寫就會出現一些字節寫出錯
?
三. 案例一
pcf8591.c:
運行結果:
代碼:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <asm/uaccess.h>? //其中copy_to*在其中
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
spinlock_t lock;? //加不加這都 可以
#define DEVICE_NAME "PCF8591"
#define uchar unsigned char
int askflag;
#define? NUM? 4 ?? ???????????? //接收和發送緩存區的深度
uchar? receivebuf[NUM];??? //數據接收緩沖區
uchar??? SystemError;??????????????? //從機錯誤標志位
void ?? ?set_conOUT_sda(void);
void?? ?set_conOUT_scl(void);
void ?? ?set_conIN_sda(void);
void?? ?set_conIN_scl(void);
void ?? ?set_data_sda(int i);
void?? ?set_data_scl(int i);
?? ?
//-------------------------------------------------------------------
// 函數名稱: i2c_start()
// 函數功能: 啟動I2C總線子程序
//-------------------------------------------------------------------
void i2c_start(void)
{ //時鐘保持高,數據線從高到低一次跳變,I2C通信開始
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(1);
?? ?set_data_scl(1);
?? ?__udelay(5);
?? ?set_data_sda(0);
?? ?__udelay(5);
?? ?set_data_scl(0);
}
//-------------------------------------------------------------------
// 函數名稱: i2c_stop()
// 函數功能: 停止I2C總線數據傳送子程序
//-------------------------------------------------------------------
void i2c_stop(void)
{ ??? ?
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(0);//時鐘保持高,數據線從低到高一次跳變,I2C通信停止
?? ?set_data_scl(1);
?? ?__udelay(5);
?? ?set_data_sda(1);
?? ?__udelay(5);
?? ?set_data_scl(0);
}
//------------------------------------------------------------------
// 函數名稱: i2c_Init()
// 函數功能: 初始化I2C總線子程序
//------------------------------------------------------------------
void i2c_init(void)
{
? ??? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_scl(0);
?? ?i2c_stop();?? ?
} ?
//-------------------------------------------------------------------
// 函數名稱: slave_ack
// 函數功能: 從機發送應答位子程序
//-------------------------------------------------------------------
void slave_ack(void)
{
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(0);
?? ?set_data_scl(1);
?? ?__udelay(5);
?? ?set_data_scl(0);
?? ?
}
//-------------------------------------------------------------------
// 函數名稱: slave_noack
// 函數功能: 從機發送非應答位子程序,迫使數據傳輸過程結束
//-------------------------------------------------------------------
void slave_noack(void)
{ 
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(1);
?? ?set_data_scl(1);
?? ?__udelay(5);
?? ?set_data_sda(0);
?? ?set_data_scl(0);
}
//-------------------------------------------------------------------
// 函數名稱: check_ack
// 函數功能: 主機應答位檢查子程序,迫使數據傳輸過程結束
//-------------------------------------------------------------------
void check_ack(void)
{ ?? ?
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(1); // 將p1.1設置成輸入,必須先向端口寫1
?? ?set_data_scl(1);
?? ?askflag = 0;
?? ?__udelay(5);
?? ?set_conIN_sda();
?? ?__udelay(3);
?? ?if ( gpio_get_value(S5PV210_GPE0(3)) == 1)
?? ??? ?askflag = 1;
?? ??? ?// 若SDA=1表明非應答,置位非應答標志askflag
?? ?set_data_scl(0);
? ?
}
//-------------------------------------------------------------------
// 函數名稱: i2c_send_byte
// 入口參數: ch
// 函數功能: 發送一個字節
//-------------------------------------------------------------------
void i2c_send_byte(uchar ch)
?
{
? ?? ?unsigned char? n=8;???? // 向SDA上發送一位數據字節,共八位
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?spin_lock(&lock);
?? ?while(n--) { 
?? ??? ?if((ch&0x80) == 0x80)??? // 若要發送的數據最高位為1則發送位1
?? ???? {
?? ?? ??? ??? ?set_data_sda(1);? // 傳送位1
?? ??? ??? ?set_data_scl(1); 
?? ??? ???? __udelay(5);
?? ??? ??? ?set_data_scl(0);
?? ???? }
?? ??? ?else
?? ??? ?{ ?
?? ??? ??? ?set_data_sda(0); // 否則傳送位0
?? ??? ??? ?set_data_scl(1);
?? ??? ??? ?__udelay(5);
?? ??? ??? ?
?? ??? ??? ?set_data_scl(0);
?? ??? ?}
?? ??? ??? ?ch = ch<<1;??? // 數據左移一位
?? ?}
?? ?spin_unlock(&lock);
}
//-------------------------------------------------------------------
// 函數名稱: i2c_receive_byte
// 返回接收的數據
// 函數功能: 接收一字節子程序
//-------------------------------------------------------------------
uchar i2c_receive_byte(void)
{
?? ?uchar? n=8;???? // 從SDA線上讀取一上數據字節,共八位
?? ?uchar tdata=0;
?? ?int temp;
?? ?spin_lock(&lock);
?? ?
?? ?while(n--) {
?? ??? ?set_conOUT_sda();
?? ??? ?set_conOUT_scl();
?? ??? ?__udelay(1);
?? ??? ?
?? ??? ?set_data_sda(1);
?? ??? ?set_data_scl(1);
?? ??? tdata =tdata<<1;?? ??? ???? //左移一位
?? ??? set_conIN_sda();
?? ??? __udelay(3);
?? ?? ?
?? ??? temp = gpio_get_value(S5PV210_GPE0(3));
?? ?? ??? ?if( temp == 1)
?? ??? ?? tdata = tdata|0x01;?? // 若接收到的位為1,則數據的最后一位置1
?? ??? ?else 
?? ??? ?? tdata = tdata&0xfe;?? // 否則數據的最后一位置0
?? ??? ?//printk("read_data_bit: %d", temp); //++
?? ??? ?set_data_scl(0);
?? ? }
?? ? spin_unlock(&lock);
?? ? printk("read_data: %d\n", tdata);
?? ? return(tdata);
}
//-------------------------------------------------------------------
// 函數名稱: adc_pcf8591
// 入口參數: controlbyte控制字
// 函數功能: 連續讀入4路通道的A/D轉換結果到receivebuf
//-------------------------------------------------------------------
void adc_pcf8591(uchar controlbyte)
{ 
??? uchar? receive_da,i=0;
?? ?i2c_start();
?? ?i2c_send_byte(0x90);?? ?//控制字
?? ?check_ack();
?? ?__udelay(5);
?? ?if(askflag == 1)? //++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?return;
?? ?}
?? ?i2c_send_byte(controlbyte);?? ?//控制字
?? ?check_ack();
?? ?__udelay(5);
?? ?if(askflag == 1)//++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?return;
?? ?}
??? i2c_start();??????????????? //重新發送開始命令
? ??? ?i2c_send_byte(0x91);?? ?//控制字
?? ?check_ack();
?? ?__udelay(5);
?? ?if(askflag == 1) // ++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?return;
?? ?}
?? ? 
??? i2c_receive_byte();?? //空讀一次,調整讀順序
??? slave_ack();??????? //收到一個字節后發送一個應答位
?? ?__udelay(5);
?? ?while(i<4)
?? ?{ ?
?? ?? receive_da=i2c_receive_byte();
?? ?? receivebuf[i++]=receive_da;
?? ?? slave_ack();?????? //收到一個字節后發送一個應答位
?? ?}
?? ?slave_noack();?????? //收到最后一個字節后發送一個非應答位
?? ?i2c_stop();
}
void set_conIN_sda(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_INPUT);
?? ?//gpio_set_value(S5PV210_GPE0(3), 1);?? ?
}
void set_conIN_scl(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_INPUT);
?? ?//gpio_set_value(S5PV210_GPE0(3), 1);?? ?
}
void set_conOUT_sda(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_OUTPUT);
}
void set_conOUT_scl(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_OUTPUT);
}
void set_data_sda(int i)
{
?? ?if (i == 0) {
?? ??? ?gpio_set_value(S5PV210_GPE0(3), 0);?? ?
?? ?}?? ?else if(i == 1) {
?? ??? ?gpio_set_value(S5PV210_GPE0(3), 1);?? ?
?? ?}
}
void set_data_scl(int i)
{
?? ?if (i == 0) {
?? ??? ?gpio_set_value(S5PV210_GPE0(4), 0);?? ?
?? ?}?? ?else if(i == 1) {
?? ??? ?gpio_set_value(S5PV210_GPE0(4), 1);?? ?
?? ?}
}
static int pcf8591_open(struct inode *inode, struct file *filp)
{
?? ?printk (KERN_INFO "Device opened\n");
?? ?spin_lock_init(&lock);
?? ?return 0;
}
?/*讀取數據*/
static int pcf8591_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
?? ?printk("pcf8591_read\n");
?? ?int i;
?? ?int v[4]; //存實際的電壓值
?? ?i2c_init();?? ?????????????? //I2C總線初始化
??? adc_pcf8591(0x04);
?? ?if(SystemError == 1)?? ?? //有錯誤,重新來
?? ?{
?? ? ??? ?printk("SystemError: %d\n", SystemError);
?? ? ??? ?i2c_init();?? ??? ??? ??? ?? //I2C總線初始化
?? ???? adc_pcf8591(0x04);
?? ?}? ?
?? ? ?
?? ?for(i=0;i<4;i++)?? ?
?? ?{
?? ??? ?printk("AD_%d: %d\t\n", i, receivebuf[i]); //顯示通道0?? ?
?? ??? ?v[2]=receivebuf[i]/51;?? //AD值轉換為3位BCD碼,最大為5.00V。
?? ???? v[2]=v[2];?? ? //轉換為ACSII碼
?? ???? v[3]=receivebuf[i]%51;?? //余數暫存
?? ??? ?
?? ???? v[3]=v[3]*10;??? //計算小數第一位
?? ???? v[1]=v[3]/51;
?? ???? v[1]=v[1];?? ? //轉換為ACSII碼
?? ??? ?
?? ???? v[3]=v[3]%51;
?? ???? v[3]=v[3]*10;??? //計算小數第二位
?? ???? v[0]=v[3]/51;???????????????????????????????????????????????????????????????????????????? //
?? ???? v[0]=v[0];? //轉換為ACSII碼
?? ??? ?
?? ??? ?printk("AD_%d: %d.%d%dV\t\n", i, v[2], v[1], v[0]);
?? ?}
?? ?copy_to_user(buffer, receivebuf, sizeof(receivebuf));
?? ?return 0;
}
/*寫命令,在此置空*/
static int pcf8591_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
?? ?return 0;
}
static int pcf8591_release(struct inode *inode,struct file *filp)
{
?? ?printk (KERN_INFO "device closed\n");
?? ?return 0;
}
static long pcf8591_ioctl(struct file *filp, unsigned int cmd,
?? ??? ?unsigned long arg)
{
?? ?return 0;
}
static struct file_operations pcf8591_fops = {
?? ?.owner?? ??? ??? ?= THIS_MODULE,
?? ?.open?? ?=?? ?pcf8591_open,
?? ?.read = pcf8591_read,
?? ?.write = pcf8591_write,
?? ?.unlocked_ioctl?? ?= pcf8591_ioctl,
?? ?.release = pcf8591_release,
};
static struct miscdevice pcf8591_dev = {
?? ?.minor?? ??? ??? ?= MISC_DYNAMIC_MINOR,
?? ?.name?? ??? ??? ?= DEVICE_NAME,
?? ?.fops?? ??? ??? ?= &pcf8591_fops,
};
static int __init pcf8591_dev_init(void) {
?? ?int ret;
?? ?int i;
?? ?for (i = 3; i < 5; i++) {
?? ??? ?ret = gpio_request(S5PV210_GPE0(i), "PCF8591");
?? ??? ?if (ret) {
?? ??? ??? ?printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
?? ??? ??? ??? ?S5PV210_GPE0(i), ret);
?? ??? ??? ?return ret;
?? ??? ??? ?s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_OUTPUT);
?? ??? ??? ?gpio_set_value(S5PV210_GPE0(i), 1);
?? ??? ?}
?? ?}
?? ?ret = misc_register(&pcf8591_dev);
?? ?printk(DEVICE_NAME"\tinitialized\n");
?? ?return ret;
}
static void __exit pcf8591_dev_exit(void) {
?? ?int i;
?? ?for (i = 0; i < 1; i++) {
?? ??? ?gpio_free(S5PV210_GPE0(3));
?? ??? ?gpio_free(S5PV210_GPE0(4));
?? ?}
?? ?misc_deregister(&pcf8591_dev);
}
module_init(pcf8591_dev_init);
module_exit(pcf8591_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
?
四. 案例二
at24c08.c:
運行結果:
代碼:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <asm/uaccess.h>? //其中copy_to*在其中
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
spinlock_t lock;? //加不加這都 可以
#define DEVICE_NAME "at24xx"
#define uchar unsigned char
int askflag;
#define? NUM? 4 ?? ???????????? //接收和發送緩存區的深度
uchar? receivebuf[NUM];??? //數據接收緩沖區
uchar??? SystemError;??????????????? //從機錯誤標志位
void ?? ?set_conOUT_sda(void);
void?? ?set_conOUT_scl(void);
void ?? ?set_conIN_sda(void);
void?? ?set_conIN_scl(void);
void ?? ?set_data_sda(int i);
void?? ?set_data_scl(int i);
?? ?
//-------------------------------------------------------------------
// 函數名稱: i2c_start()
// 函數功能: 啟動I2C總線子程序
//-------------------------------------------------------------------
void i2c_start(void)
{ //時鐘保持高,數據線從高到低一次跳變,I2C通信開始
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(1);
?? ?set_data_scl(1);
?? ?__udelay(1);
?? ?set_data_sda(0);
?? ?__udelay(5);
?? ?set_data_scl(0);
?? ?__udelay(1);
}
//-------------------------------------------------------------------
// 函數名稱: i2c_stop()
// 函數功能: 停止I2C總線數據傳送子程序
//-------------------------------------------------------------------
void i2c_stop(void)
{ ??? ?
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(0);//時鐘保持高,數據線從低到高一次跳變,I2C通信停止
?? ?set_data_scl(1);
?? ?__udelay(5);
?? ?set_data_sda(1);
?? ?__udelay(5);
?? ?set_data_sda(0);
?? ?set_data_scl(0);
}
//------------------------------------------------------------------
// 函數名稱: i2c_Init()
// 函數功能: 初始化I2C總線子程序
//------------------------------------------------------------------
void i2c_init(void)
{
? ??? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_scl(0);
?? ?i2c_stop();?? ?
} ?
//-------------------------------------------------------------------
// 函數名稱: slave_ack
// 函數功能: 從機發送應答位子程序
//-------------------------------------------------------------------
void slave_ack(void)
{
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(0);
?? ?set_data_scl(1);
?? ?__udelay(5);
?? ?set_data_scl(0);
?? ?
}
//-------------------------------------------------------------------
// 函數名稱: slave_noack
// 函數功能: 從機發送非應答位子程序,迫使數據傳輸過程結束
//-------------------------------------------------------------------
void slave_noack(void)
{ 
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(1);
?? ?set_data_scl(1);
?? ?__udelay(5);
?? ?set_data_sda(0);
?? ?set_data_scl(0);
}
//-------------------------------------------------------------------
// 函數名稱: check_ack
// 函數功能: 主機應答位檢查子程序,迫使數據傳輸過程結束
//-------------------------------------------------------------------
void check_ack(void)
{ ?? ?
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?
?? ?set_data_sda(1); // 將p1.1設置成輸入,必須先向端口寫1
?? ?set_data_scl(1);
?? ?askflag = 0;
?? ?__udelay(5);
?? ?set_conIN_sda();
?? ?__udelay(3);
?? ?if ( gpio_get_value(S5PV210_GPE0(3)) == 1)
?? ??? ?askflag = 1;
?? ??? ?// 若SDA=1表明非應答,置位非應答標志askflag
?? ?set_data_scl(0);
? ?
}
//-------------------------------------------------------------------
// 函數名稱: i2c_send_byte
// 入口參數: ch
// 函數功能: 發送一個字節
//-------------------------------------------------------------------
void i2c_send_byte(uchar ch)
?
{
? ?? ?unsigned char? n=8;???? // 向SDA上發送一位數據字節,共八位
?? ?set_conOUT_sda();
?? ?set_conOUT_scl();
?? ?__udelay(1);
?? ?spin_lock(&lock);
?? ?while(n--) { 
?? ??? ?if((ch&0x80) == 0x80)??? // 若要發送的數據最高位為1則發送位1
?? ???? {
?? ?? ??? ??? ?set_data_sda(1);? // 傳送位1
?? ??? ??? ?set_data_scl(1); 
?? ??? ???? __udelay(5);
?? ??? ??? ?set_data_scl(0);
?? ???? }
?? ??? ?else
?? ??? ?{ ?
?? ??? ??? ?set_data_sda(0); // 否則傳送位0
?? ??? ??? ?set_data_scl(1);
?? ??? ??? ?__udelay(5);
?? ??? ??? ?
?? ??? ??? ?set_data_scl(0);
?? ??? ?}
?? ??? ??? ?ch = ch<<1;??? // 數據左移一位
?? ?}
?? ?spin_unlock(&lock);
}
//-------------------------------------------------------------------
// 函數名稱: i2c_receive_byte
// 返回接收的數據
// 函數功能: 接收一字節子程序
//-------------------------------------------------------------------
uchar i2c_receive_byte(void)
{
?? ?uchar? n=8;???? // 從SDA線上讀取一上數據字節,共八位
?? ?uchar tdata=0;
?? ?int temp;
?? ?spin_lock(&lock);
?? ?
?? ?while(n--) {
?? ??? ?set_conOUT_sda();
?? ??? ?set_conOUT_scl();
?? ??? ?__udelay(1);
?? ??? ?
?? ??? ?set_data_sda(1);
?? ??? ?set_data_scl(1);
?? ??? tdata =tdata<<1;?? ??? ???? //左移一位
?? ??? set_conIN_sda();
?? ??? __udelay(3);
?? ?? ?
?? ??? temp = gpio_get_value(S5PV210_GPE0(3));
?? ?? ??? ?if( temp == 1)
?? ??? ?? tdata = tdata|0x01;?? // 若接收到的位為1,則數據的最后一位置1
?? ??? ?else 
?? ??? ?? tdata = tdata&0xfe;?? // 否則數據的最后一位置0
?? ??? ?//printk("read_data_bit: %d", temp); //++
?? ??? ?set_data_scl(0);
?? ? }
?? ? spin_unlock(&lock);
?? ? printk("read_data: %d\n", tdata);
?? ? return(tdata);
}
//***************************************************
//函數功能:向AT24xx中指定地址寫入數據
//入口參數:add(地址), dat(儲存待寫入的數據)
//***************************************************
void write_set_at24xx(uchar add, uchar dat)
{
?? ?i2c_start();
?? ?i2c_send_byte(0xA0);
?? ?__udelay(1);
?? ?check_ack();
?? ?if(askflag == 1)? //++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?printk("write_set_01_SystemError: %d\n", SystemError);
?? ??? ?return;
?? ?} else {
?? ??? ?SystemError = 0;
?? ?}
?? ?i2c_send_byte(add);
?? ?__udelay(1);
?? ?check_ack();
?? ?if(askflag == 1)? //++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?printk("write_set_02_SystemError: %d\n", SystemError);
?? ??? ?return;
?? ?} else {
?? ??? ?SystemError = 0;
?? ?}
?? ?i2c_send_byte(dat);
?? ?__udelay(1);
?? ?check_ack();
?? ?if(askflag == 1)? //++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?printk("write_set_03_SystemError: %d\n", SystemError);
?? ??? ?return;
?? ?} else {
?? ??? ?SystemError = 0;
?? ?}
?? ?i2c_stop();
?? ?__udelay(5);
}
//***************************************************
//函數功能:從AT24Cxx中的當前地址讀取數據
//出口參數:x (儲存讀出的數據) 
//***************************************************
uchar read_current_at24xx()
{
?? ?unsigned char x;
?? ?i2c_start();?????????????? //開始數據傳遞
?? ?i2c_send_byte(0xA1);?? //選擇要操作的AT24Cxx芯片,并告知要讀其數據
?? ?__udelay(5);
?? ?check_ack();
?? ?
?? ?if(askflag == 1)? //++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?printk("read_current_01_SystemError: %d\n", SystemError);
?? ??? ?return;
?? ?} else {
?? ??? ?SystemError = 0;
?? ?}
?? ?x=i2c_receive_byte();???????? //將讀取的數據存入x
?? ?__udelay(3);
?? ?
?? ?i2c_stop();??????????????? //停止數據傳遞
?? ?return x;????????????? //返回讀取的數據
}
//***************************************************
//函數功能:從AT24Cxx中的指定地址讀取數據
//入口參數:set_addr
//出口參數:x 
//***************************************************
unsigned char read_set_at24xx(unsigned char set_addr)
// 在指定地址讀取
{
?? ?uchar temp;
?? ?i2c_start();????????????????????? //開始數據傳遞
?? ?i2c_send_byte(0xA0);?????? //選擇要操作的AT24Cxx芯片,并告知要對其寫入數據
?? ?__udelay(5);
?? ?check_ack();
?? ?
?? ?if(askflag == 1)? //++
?? ?{
?? ??? ?SystemError = 1;
?? ??? ?printk("read_set_01_SystemError: %d\n", SystemError);
?? ??? ?return;
?? ?} else {
?? ??? ?SystemError = 0;
?? ?}
?? ?i2c_send_byte(set_addr);?????? //寫入指定地址
?? ?__udelay(1);
?? ?check_ack();
?? ?
?? ?temp = read_current_at24xx();
?? ?
?? ?return(temp);??????? //從指定地址讀出數據并返回
?? ?
}
void set_conIN_sda(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_INPUT);
?? ?//gpio_set_value(S5PV210_GPE0(3), 1);?? ?
}
void set_conIN_scl(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_INPUT);
?? ?//gpio_set_value(S5PV210_GPE0(3), 1);?? ?
}
void set_conOUT_sda(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(3), S3C_GPIO_OUTPUT);
}
void set_conOUT_scl(void)
{
?? ?s3c_gpio_cfgpin(S5PV210_GPE0(4), S3C_GPIO_OUTPUT);
}
void set_data_sda(int i)
{
?? ?if (i == 0) {
?? ??? ?gpio_set_value(S5PV210_GPE0(3), 0);?? ?
?? ?}?? ?else if(i == 1) {
?? ??? ?gpio_set_value(S5PV210_GPE0(3), 1);?? ?
?? ?}
}
void set_data_scl(int i)
{
?? ?if (i == 0) {
?? ??? ?gpio_set_value(S5PV210_GPE0(4), 0);?? ?
?? ?}?? ?else if(i == 1) {
?? ??? ?gpio_set_value(S5PV210_GPE0(4), 1);?? ?
?? ?}
}
static int at24xx_open(struct inode *inode, struct file *filp)
{
?? ?printk (KERN_INFO "Device opened\n");
?? ?spin_lock_init(&lock);
?? ?return 0;
}
?/*讀取數據*/
static int at24xx_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
?? ?printk("at24xx_read\n");
?? ?int i;
?? ?uchar read_dat;
?? ?//i2c_init();
?? ?//write_set_at24xx(5, 'A');
?? ?i2c_init();
?? ?read_dat = read_set_at24xx(5);
?? ?printk("add_%d: %d\t", 5, read_dat);
?? ?
?? ?//i2c_init();
?? ?for (i='a'; i<'z'; i++) {
?? ??? ?i2c_init();
?? ??? ?write_set_at24xx(i, i+1); //在24c08的地址2中寫入數據sec?? ?
?? ??? ?if (SystemError == 1) {
?? ??? ??? ?i2c_init();
?? ??? ??? ?write_set_at24xx(i, i);?? ?
?? ??? ?}/*
?? ??? ?if (SystemError == 1) {
?? ??? ??? ?i2c_init();
?? ??? ??? ?write_set_at24xx(i, i);?? ?
?? ??? ?}
?? ??? ?if (SystemError == 1) {
?? ??? ??? ?i2c_init();
?? ??? ??? ?write_set_at24xx(i, i);?? ?
?? ??? ?}*/
?? ??? ?__udelay(5);
?? ?}
?? ?__udelay(9);
?? ?for (i='a'; i<'z'; i++) {
?? ??? ?i2c_init();
?? ??? ?read_dat = read_set_at24xx(i);
?? ??? ?printk("add_%d: %c\t",i, read_dat);
?? ??? ?__udelay(5);
?? ?}
?? ?printk("\n");
?? ?for (i=2; i< 10; i++) {
?? ??? ?i2c_init();
?? ??? ?read_dat = read_set_at24xx(i);
?? ??? ?printk("add_%d: %c\t",i, read_dat);
?? ??? ?__udelay(5);
?? ?}
?? ?
?? ?copy_to_user(buffer, receivebuf, sizeof(receivebuf));
?? ?return 0;
?? ?/*printk("at24xx_read\n");
?? ?int i;
?? ?int v[4]; //存實際的電壓值
?? ?i2c_init();?? ?????????????? //I2C總線初始化
??? adc_at24xx(0x04);
?? ?if(SystemError == 1)?? ?? //有錯誤,重新來
?? ?{
?? ? ??? ?printk("SystemError: %d\n", SystemError);
?? ? ??? ?i2c_init();?? ??? ??? ??? ?? //I2C總線初始化
?? ???? adc_at24xx(0x04);
?? ?}? ?
?? ? ?
?? ?for(i=0;i<4;i++)?? ?
?? ?{
?? ??? ?printk("AD_%d: %d\t\n", i, receivebuf[i]); //顯示通道0?? ?
?? ??? ?v[2]=receivebuf[i]/51;?? //AD值轉換為3位BCD碼,最大為5.00V。
?? ???? v[2]=v[2];?? ? //轉換為ACSII碼
?? ???? v[3]=receivebuf[i]%51;?? //余數暫存
?? ??? ?
?? ???? v[3]=v[3]*10;??? //計算小數第一位
?? ???? v[1]=v[3]/51;
?? ???? v[1]=v[1];?? ? //轉換為ACSII碼
?? ??? ?
?? ???? v[3]=v[3]%51;
?? ???? v[3]=v[3]*10;??? //計算小數第二位
?? ???? v[0]=v[3]/51;???????????????????????????????????????????????????????????????????????????? //
?? ???? v[0]=v[0];? //轉換為ACSII碼
?? ??? ?
?? ??? ?printk("AD_%d: %d.%d%dV\t\n", i, v[2], v[1], v[0]);
?? ?}
?? ?copy_to_user(buffer, receivebuf, sizeof(receivebuf));
?? ?return 0;
?? ?*/
}
/*寫命令,在此置空*/
static int at24xx_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
?? ?printk("at24xx_write\n");
?? ?char b[10];
?? ?char *a = b;//kmalloc(100, GFP_KERNEL);
?? ?int i;
?? ?int len;
?? ?len = sizeof(buffer);
?? ?copy_from_user(a,buffer, count);
?? ?for (i=0; i < count; i++) {
?? ??? ?i2c_init();
?? ??? ?write_set_at24xx(i+2, *(a++));
?? ?}
?? ?printk("len: %s\n", a);
?? ?return count;
}
static int at24xx_release(struct inode *inode,struct file *filp)
{
?? ?printk (KERN_INFO "device closed\n");
?? ?return 0;
}
static long at24xx_ioctl(struct file *filp, unsigned int cmd,
?? ??? ?unsigned long arg)
{
?? ?return 0;
}
static struct file_operations at24xx_fops = {
?? ?.owner?? ??? ??? ?= THIS_MODULE,
?? ?.open?? ?=?? ?at24xx_open,
?? ?.read = at24xx_read,
?? ?.write = at24xx_write,
?? ?.unlocked_ioctl?? ?= at24xx_ioctl,
?? ?.release = at24xx_release,
};
static struct miscdevice at24xx_dev = {
?? ?.minor?? ??? ??? ?= MISC_DYNAMIC_MINOR,
?? ?.name?? ??? ??? ?= DEVICE_NAME,
?? ?.fops?? ??? ??? ?= &at24xx_fops,
};
static int __init at24xx_dev_init(void) {
?? ?int ret;
?? ?int i;
?? ?for (i = 3; i < 5; i++) {
?? ??? ?ret = gpio_request(S5PV210_GPE0(i), "PCF8591");
?? ??? ?if (ret) {
?? ??? ??? ?printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
?? ??? ??? ??? ?S5PV210_GPE0(i), ret);
?? ??? ??? ?return ret;
?? ??? ??? ?s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_OUTPUT);
?? ??? ??? ?gpio_set_value(S5PV210_GPE0(i), 1);
?? ??? ?}
?? ?}
?? ?ret = misc_register(&at24xx_dev);
?? ?printk(DEVICE_NAME"\tinitialized\n");
?? ?return ret;
}
static void __exit at24xx_dev_exit(void) {
?? ?int i;
?? ?for (i = 0; i < 1; i++) {
?? ??? ?gpio_free(S5PV210_GPE0(3));
?? ??? ?gpio_free(S5PV210_GPE0(4));
?? ?}
?? ?misc_deregister(&at24xx_dev);
}
module_init(at24xx_dev_init);
module_exit(at24xx_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
轉載于:https://www.cnblogs.com/minglicnblogs/p/3967431.html
總結
以上是生活随笔為你收集整理的S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: DDL
- 下一篇: MinGW编译wxWidget
