openwrtのtd1030i2cドライバー

Taidou 1030、gpsセンサー、デフォルトのuartインターフェース、読み取りと書き込みのためのi2cインターフェースをサポートします。
ただし、td1030は、レジスタの概念を持つeeprom 24c02、温度および湿度センサーsth12などの通常のi2cデバイスを必要としません。
td1030は読み取りと書き込みのみが必要です。

  • openwrtはi2cシミュレーションを有効にします1、i2cシミュレーションはデバイスツリー定義ポートと他のパラメータを変更する必要があります2
 .config - OpenWrt Configuration
 > Kernel modules > I2C support ────────────────────────────────────────────────────────────────────────
  ┌────────────────────────────────────────── I2C support ───────────────────────────────────────────┐
  │  Arrow keys navigate the menu.  <Enter> selects submenus ---> (or empty submenus ----).          │  
  │  Highlighted letters are hotkeys.  Pressing <Y> includes, <N> excludes, <M> modularizes          │  
  │  features.  Press <Esc><Esc> to exit, <?> for Help, </> for Search.  Legend: [*] built-in  [ ]   │  
  │  excluded  <M> module  < > module capable                                                        │  
  │ ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ │  
  │ │            <*> kmod-i2c-core................................................ I2C support     │ │  
  │ │            -*-   kmod-i2c-algo-bit............................. I2C bit-banging interfaces   │ │  
  │ │            < >   kmod-i2c-algo-pca................................ I2C PCA 9564 interfaces   │ │  
  │ │            < >   kmod-i2c-algo-pcf................................ I2C PCF 8584 interfaces   │ │  
  │ │            <*>   kmod-i2c-gpio.................................. GPIO-based bitbanging I2C   │ │  
  │ │            < > kmod-i2c-gpio-custom........................ Custom GPIO-based I2C device     │ │  
  │ │            < > kmod-i2c-mt7628................................. MT7628/88 I2C Controller     │ │  
  │ │            < > kmod-i2c-mux................................ I2C bus multiplexing support     │ │  
  │ │            < > kmod-i2c-smbus........................... SMBus-specific protocols helper     │ │  
  │ │            < > kmod-i2c-tiny-usb................................... I2C Tiny USB adaptor     │ │  
  │ │                                                                                              │ │  
  │ │                                                                                              │ │  
  │ │                                                                                              │ │  
  │ └──────────────────────────────────────────────────────────────────────────────────────────────┘ │  
  ├──────────────────────────────────────────────────────────────────────────────────────────────────┤  
  │                     <Select>    < Exit >    < Help >    < Save >    < Load >                     │  
  └──────────────────────────────────────────────────────────────────────────────────────────────────┘  
  • 読み取りには2つの方法があります:
    1。ドライバーの書き込み、ロード後の読み取りと書き込み
    2.ドライバーは不要、アプリケーション層で直接i2cバスの読み取りと書き込みを操作


  • td1030.cを書くドライバー

/*
 * gps 
 *
 * Written by: eric
 *
 *
 */


// #define DEBUG 
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/hwmon-sysfs.h>
#include <linux/uaccess.h>

#include <linux/delay.h>


// -------------------------------|-同步头--|--识别码--|---长度---|GGA|端口-|频率|保存|--校验---|
static const uint8_t gga_config[]={
    
    0x23,0x3e,0x03,0x51,0x04,0x00,0x04,0x04,0x01,0x01,0x62,0x86};
static const uint8_t gsv_config[]={
    
    0x23,0x3e,0x03,0x51,0x04,0x00,0x06,0x04,0x01,0x01,0x64,0x8e};
static const uint8_t rmc_config[]={
    
    0x23, 0x3E, 0x03, 0x51,0x04, 0x00, 0x03, 0x04, 0x01, 0x01, 0x61, 0x82};
static const uint8_t gsa_config[]={
    
    0x23, 0x3E, 0x03, 0x51,0x04, 0x00, 0x05, 0x04, 0x01, 0x01, 0x63, 0x8A};
static const uint8_t txt_config[]={
    
    0x23, 0x3E, 0x03, 0x51,0x04, 0x00, 0x0D, 0x04, 0x01, 0x01, 0x6B, 0xAA};

// ACK:233E0101 
// NACK:233E0102
static const uint8_t td1030_ack[]={
    
    0x23,0x3E,0x01,0x01};
static const uint8_t td1030_nack[]={
    
    0x23,0x3E,0x01,0x02};

/* Device registers */
#define TD1030_REG_CONFIG	0x00
#define TD1030_READ_LEN		(256)

/**
 * @brief  hex数据转字符串
 * @note   
 * @param  data[]: hex数据
 * @param  data_len: hex数据长度
 * @param  *string: 输出字符串
 * @param  string_len: 输出字符串缓冲区长度
 * @retval 
 */
static int8_t hex2str(uint8_t data[], uint32_t data_len, uint8_t *string, uint32_t string_len)
{
    
    
    uint32_t i;
    uint8_t high_8bits_string = 0;
    uint8_t low_8bits_string = 0;

    if ( (2 * data_len) > string_len ) {
    
    
        return -1;
    }

    for (i = 0; i < data_len; i++) {
    
    
        /*parse high 8 bits value*/
        high_8bits_string = data[i] / 16;
        if (high_8bits_string <= 9) {
    
    
            string[2 * i] = high_8bits_string + '0';
        } else if (10 <= high_8bits_string && high_8bits_string <= 15) {
    
    
            string[2 * i] = high_8bits_string - 10 + 'A';
        } else {
    
    
            return -2;
        }

        /*parse low 8 bits value*/
        low_8bits_string = data[i] % 16;
        if (low_8bits_string <= 9) {
    
    
            string[2 * i + 1] = low_8bits_string + '0';
        } else if (10 <= low_8bits_string && low_8bits_string <= 15) {
    
    
            string[2 * i + 1] = low_8bits_string - 10 + 'A';
        } else {
    
    
            return -3;
        }
    }

    return 0;
}
static void str_trim(char *src,char c)  
{
    
      
    int i,j;  
    for (i = 0, j = 0; src[i] != '\0'; i++)  
    {
    
      
        if (src[i] != c)  
        {
    
      
            src[j++] = src[i];  
        }  
    }  
    src[j] = '\0'; 
} 
static void show_hex(struct device *dev,char *val,int len)
{
    
    
	char hex[256]={
    
    0};
	memset(hex,0,256);
	hex2str(val,len,hex,256);
	dev_err(dev, "hex=%s\n",hex);
}


/*
 * Generic counter attributes
 */
static ssize_t td1030_show(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
    
    
	struct i2c_client *client = to_i2c_client(dev);
	int rc=0;
	uint8_t *val=NULL;

	if (!(val = kmalloc(TD1030_READ_LEN, GFP_KERNEL)))
		return -ENOMEM;
	memset(val,0,TD1030_READ_LEN);

	rc = i2c_master_recv(client, val, TD1030_READ_LEN-1);
	if (rc < 0) {
    
    
		dev_warn(dev, "i2c read data cmd failed\n");
		goto _err;
	}

	str_trim(val,0xff);
	// show_hex(dev,val,64);
	// dev_dbg(dev, "read val=%s\n",val);

	// char *p = strstr(val,'$');
	// if(!p)
	// 	goto _err;

	rc = sprintf(buf,"%s",val);

	kfree(val);
	return rc;
	// return copy_to_user(buf,val,strlen(val));
_err:
	kfree(val);
	return rc;
}

static ssize_t td1030_store(struct device *dev, struct device_attribute *attr,
			    const char *buf, size_t count)
{
    
    
	struct i2c_client *client = to_i2c_client(dev);
	int rc;
	uint8_t *set_buf=gga_config;
	int set_len=sizeof(gga_config);

	dev_dbg(dev, "td1030_store() called on %s\n", attr->attr.name);
	dev_dbg(dev, "set command:%s.\n",buf);

	
	if (!strcmp(buf,"gga"))	{
    
    
		set_buf=gga_config;
		set_len=sizeof(gga_config);
	}else if (!strcmp(buf,"gsv")){
    
    
		set_buf=gsv_config;
		set_len=sizeof(gsv_config);
	}else if (!strcmp(buf,"rmc")){
    
    
		set_buf=rmc_config;
		set_len=sizeof(rmc_config);
	}else if (!strcmp(buf,"gsa")){
    
    
		set_buf=gsa_config;
		set_len=sizeof(gsa_config);
	}else if (!strcmp(buf,"txt")){
    
    
		set_buf=txt_config;
		set_len=sizeof(txt_config);
	}else{
    
    
		// show_hex(dev,buf,count);
		return -EINVAL;
	}
	
	// 初始化
	rc = i2c_master_send(client, set_buf, set_len);
	if (rc < 0){
    
    
		dev_warn(dev, "command failed.rc=%d\n", rc);
		return -EIO;
	}

	// 申请接收区
	uint8_t *val=NULL;
	if (!(val = kmalloc(16, GFP_KERNEL)))
		return -ENOMEM;
	memset(val,0,16);

	// 读取设置响应
	rc = i2c_master_recv(client, val, 16);
	if (rc < 0) {
    
    
		dev_warn(dev, "i2c read data cmd failed\n");
		goto _err;
	}
	str_trim(val,0xff);

	show_hex(dev,val,16);
	// 判断接收到的数据是否为ACK
	if (memcmp(val,td1030_ack,sizeof(td1030_ack))){
    
    
		dev_err(dev, "read cmd nack!\n");
		goto _err;
	}

	dev_dbg(dev, "td1030 store ok\n");

	kfree(val);
	return count;
_err:
	kfree(val);
	return -1;
}

/*
 * Simple register attributes
 */
static DEVICE_ATTR(get, S_IRUGO, td1030_show, NULL);
static DEVICE_ATTR(set, S_IWUSR, NULL, td1030_store);


static const struct attribute_group td1030_group = {
    
    
	.attrs = (struct attribute *[]) {
    
    
		&dev_attr_get.attr,
		&dev_attr_set.attr,
		NULL,
	},
};



/*
 * Called when a td1030 device is matched with this driver
 */
static int td1030_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
    
    
	int rc;

	if (!i2c_check_functionality(client->adapter,
				     I2C_FUNC_I2C)) {
    
    
		dev_err(&client->dev, "i2c bus does not support the td1030\n");
		rc = -ENODEV;
		goto exit;
	}

	rc = sysfs_create_group(&client->dev.kobj, &td1030_group);
	if (rc)
		goto exit;

	dev_dbg(&client->dev, "td1030 probe ok\n");
	return 0;
	
 exit:
	return rc;
}

static int td1030_remove(struct i2c_client *client)
{
    
    
	sysfs_remove_group(&client->dev.kobj, &td1030_group);
	return 0;
}

static const struct i2c_device_id td1030_id[] = {
    
    
	{
    
     "td1030", 0 },
	{
    
     }
};
MODULE_DEVICE_TABLE(i2c, td1030_id);

static const struct of_device_id td1030_of_match[] = {
    
    
	{
    
     .compatible = "td1030", },
	{
    
    }
};
MODULE_DEVICE_TABLE(of, td1030_of_match);

static struct i2c_driver td1030_driver = {
    
    
	.driver = {
    
    
		.name = "td1030",
		.of_match_table = td1030_of_match,
	},
	.probe = td1030_probe,
	.remove = td1030_remove,
	.id_table = td1030_id,
};

module_i2c_driver(td1030_driver);

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("td1030 GPS driver");
MODULE_LICENSE("GPL");

  • ロードドライブテスト
root@eric:/# ls
bin        init       overlay    root       td1030.ko  var
dev        lib        proc       sbin       tmp        www
etc        mnt        rom        sys        usr
root@eric:/#

...加载驱动
root@eric:/# insmod td1030.ko
root@eric:/# cd sys/bus/i2c/devices/0-0030/
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030# ls
driver     modalias   of_node    subsystem
get        name       set        uevent

...设置gga i2c输出
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030# echo -n gga > set
[ 3950.540987] td1030 0-0030: hex=233E0101020002000351586AFFFFFFFF
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030#

...读取定位数据
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030# cat get
$GNGGA,095516.00,3107.49910,N,12121.62030,E,1,17,0.66,71.0,M,9.8,M,,*4B
$GNGGA,095517.00,3107.49909,N,12121.62029,E,1,16,0.68,71.0,M,9.8,M,,*45
root@eric:/sys/devices/platform/i2c-gpio-0/i2c-0/0-0030#

  • アプリケーション層は、i2cバスの読み取りおよび書き込みモードを直接呼び出します(次の章で紹介します)

  1. mt7688プラットフォームのハードウェア制限により、一度に最大64バイトを読み取ることができますが、td1030は一度に少なくとも256バイトを読み取る必要があります。数回読み取ると、有効なデータが失われます。↩︎

  2. シミュレートI2C設定https://blog.csdn.net/pyt1234567890/article/details/112990777 ↩︎

おすすめ

転載: blog.csdn.net/pyt1234567890/article/details/112993422