单片机学习小组
直播中

李春梅

7年用户 1765经验值
私信 关注

如何在MSM8909 Android下配置CS1237驱动读取PT100温度传感器?

如何在MSM8909 Android下配置CS1237驱动读取PT100温度传感器?

回帖(1)

谭齐慧

2022-2-15 10:29:02
前言

上面一个文章配置了普通的GPIO口。然后根据在单片机的经验,尝试配置CS1237驱动。
CS1237是一款高精度、低功耗模数转换芯片,一路差分输入通道,内置温度传感器和高精度振荡器。通过 2线SPI接口 通信。
在单片机中是使用模拟IO口的方式实现SPI通信。在Android里也是一样。


实现
1.首先还是配置DTS设备树,修改kernel/arch/arm/boot/dts/qcom/msm8909-mtp.dtsi。
cs1237 {
            compatible = "cs1237";
            //这是一个enable的pin
            cs1237,gpio0 = <&msm_gpio 0 0>;
            //SDA pin
            cs1237,gpio68 = <&msm_gpio 68 0>;
            //SCL pin
                        cs1237,gpio69 = <&msm_gpio 69 0>;
            label = "cs1237";
                };
2.在 kernel/drivers/misc 下添加 cs1237.c文件
(1)IO口函数操作定义
void CLK_H(void){gpio_set_value(CLK_PIN, GPIO_HIGH);}
void CLK_L(void){gpio_set_value(CLK_PIN, GPIO_LOW);}
void SDA_H(void){gpio_set_value(SDA_PIN, GPIO_HIGH);}
void SDA_L(void){gpio_set_value(SDA_PIN, GPIO_LOW);}
int SDA_Read(void){return gpio_get_value(SDA_PIN);}
void SDA_OUT(void){gpio_direction_output(SDA_PIN, 1);}
void SDA_IN(void){gpio_direction_input(SDA_PIN);}
(2)cs1237配置初始化,读取配置函数
unsigned char cs1237_init_config(void)
{
    int count_i = 0; //溢出计时器
    int i = 0;
    unsigned char dat = PGA_2 | SPEED_640 | REF_ON;

    SDA_OUT();//SDA_H();

        SDA_IN();
        CLK_L();

        while(SDA_Read() == 1) { //等待CS237准备好
        mdelay(5);
        count_i++;

        if(count_i > 300) {
            SDA_OUT();
            //SDA_H(); // OUT引脚拉高
            CLK_H(); // CLK引脚拉高
            return -1;//超时,则直接退出程序
        }
    }
    for(i = 0; i < 29; i++) { // 1 - 29
        CLK_H();udelay(1);CLK_L();udelay(1);
    }

    SDA_OUT();

        CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//30
        CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//31
    CLK_H();udelay(1);SDA_L();CLK_L();udelay(1);//32
    CLK_H();udelay(1);SDA_L();CLK_L();udelay(1);//33
    CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//34
    CLK_H();udelay(1);SDA_L();CLK_L();udelay(1);//35
    CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//36

    CLK_H();udelay(1);CLK_L();udelay(1);    //37  写入了0x65


    for(i = 0; i < 8; i++) { // 38 - 45个脉冲了,写8位数据
        CLK_H();
        udelay(1);
        if(dat & 0x80)
            SDA_H();
        else
            SDA_L();
        dat <<= 1;
        CLK_L();
        udelay(1);
    }
    SDA_H();
    CLK_H();udelay(1);CLK_L();udelay(1);

    return 0;
}

// 读取芯片的配置数据
unsigned char Read_Config(void)
{
    unsigned char i;
    unsigned char dat = 0; //读取到的数据
    unsigned int count_i = 0; //溢出计时器
       
    SDA_OUT();
    //SDA_H();
       
        SDA_IN();
    CLK_L();//时钟拉低
    while(SDA_Read() == 1) { //等待芯片准备好数据
        mdelay(1);
        count_i++;
        if(count_i > 300) {
            SDA_OUT();
            CLK_H();    // CLK=1;
            //SDA_H();    // OUT=1;
            return -1;//超时,则直接退出程序
        }
    }

        //CLK引脚拉高拉低即为一个时钟
    for(i = 0; i < 29; i++) { // 产生第1到29个时钟
        CLK_H(); udelay(1);CLK_L();udelay(1);
    }

    SDA_OUT();
    CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//30
    CLK_H();udelay(1);SDA_L();CLK_L();udelay(1);//31
    CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//32
    CLK_H();udelay(1);SDA_L();CLK_L();udelay(1);//33
    CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//34
    CLK_H();udelay(1);SDA_H();CLK_L();udelay(1);//35
    CLK_H();udelay(1);SDA_L();CLK_L();udelay(1);//36
       
    SDA_H();
    CLK_H();udelay(1);CLK_L();udelay(1);//37  写入0x56 即读命令
   
        dat = 0;
    SDA_IN();
    for(i = 0; i < 8; i++) { // 第38 - 45个脉冲了,读取数据
        CLK_H();udelay(1);CLK_L();udelay(1);
               
        dat <<= 1;
        if(SDA_Read())
            dat++;
    }
    //第46个脉冲
    CLK_H();udelay(1);CLK_L();udelay(1);
       
    SDA_OUT();
    //SDA_H();         //OUT引脚拉高
    return dat;
}


(3)probe函数中添加
static int gpio_probe(struct platform_device *pdev)
{
        int  ret = 0;
       
        printk("*************cs1237 probe *************n");
        cs1237_class = class_create(THIS_MODULE, "cs1237");
    if(IS_ERR(cs1237_class))
    {
        ret = PTR_ERR(cs1237_class);
        pr_err("Failed to create class.n");
        return ret;
    }
    cs1237_dev = device_create(cs1237_class, NULL, 0, NULL, "cs1237");
    if (IS_ERR(cs1237_dev))
    {
        ret = PTR_ERR(cs1237_class);
        pr_err("Failed to create device(cs1237)!n");
        return ret;
    }
    ret = device_create_file(cs1237_dev, &cs1237_dev_attr);
    if(ret)
    {
        pr_err("%s: cs1237 creat sysfs failedn",__func__);
        return ret;
    }
    ret = device_create_file(cs1237_dev, &cs1237_config_attr);
    if(ret)
    {
        pr_err("%s: cs1237 creat sysfs failedn",__func__);
        return ret;
    }
    ret = device_create_file(cs1237_dev, &cs1237_init_attr);
    if(ret)
    {
        pr_err("%s: cs1237 creat sysfs failedn",__func__);
        return ret;
    }


        //寻找设备树dts下的 "cs1237,gpio0" 名称对应的GPIO口
        ENABLE_PIN = of_get_named_gpio(pdev->dev.of_node, "cs1237,gpio0", 0);
    if (ENABLE_PIN < 0)
        printk( "ENABLE_PIN is not available n");
    //对应修改的dts的 label
    ret = gpio_request(ENABLE_PIN, "cs1237");
    if(0 != ret) {
        printk( "gpio request %d failed.", CLK_PIN);
        return -EIO;
    }
    gpio_direction_output(ENABLE_PIN, 1);
    gpio_set_value(ENABLE_PIN, GPIO_HIGH);

        SDA_PIN = of_get_named_gpio(pdev->dev.of_node, "cs1237,gpio68", 0);
        if (SDA_PIN < 0)
                printk( "SDA_PIN is not available n");
        //对应修改的dts的 label
        ret = gpio_request(SDA_PIN, "cs1237");
        if(0 != ret) {
                printk( "gpio request %d failed.", SDA_PIN);
                return -EIO;
        }
        gpio_direction_output(SDA_PIN, 1);

        CLK_PIN = of_get_named_gpio(pdev->dev.of_node, "cs1237,gpio69", 0);
        if (CLK_PIN < 0)
                printk( "CLK_PIN is not available n");
        //对应修改的dts的 label
        ret = gpio_request(CLK_PIN, "cs1237");
        if(0 != ret) {
                printk( "gpio request %d failed.", CLK_PIN);
                return -EIO;
        }
        gpio_direction_output(CLK_PIN, 1);
        CLK_H();

        cs1237_init_config();
        CONFIG = Read_Config();

        if(CONFIG == 36){
            printk("cs1237_probe ok , config is %dn",ret);
        }
        else{
            printk("cs1237_probe failed, config is %dn",ret);
        }

        return 0;
}


(4)读取CS1237数值
//读取ADC数据,返回的是一个有符号数据
int Read_CS1237(void)
{
    unsigned char i;
    uint32_t dat = 0; //读取到的数据
    unsigned int count_i = 0; //溢出计时器
    int temp;

    SDA_OUT();
    //SDA_H(); //OUT引脚拉高
    SDA_IN();
    CLK_L();//时钟拉低

    while(SDA_Read()) { //等待芯片准备好数据
        mdelay(1);
        count_i++;
        if(count_i > 300) {
            SDA_OUT();
            CLK_H();    // CLK=1;
            //SDA_H();    // OUT=1;
            return -1;//超时,则直接退出程序
        }
    }

    dat = 0;

    for(i = 0; i < 24; i++) { //获取24位有效转换
        CLK_H();
        udelay(1);
        dat <<= 1;

        if(SDA_Read())
            dat++;

        CLK_L();
        udelay(1);
    }

    for(i = 0; i < 3; i++) { //接着前面的时钟 再来3个时钟
        CLK_H();udelay(1);CLK_L();udelay(1);
    }

    SDA_OUT();
    //SDA_H();
       
        if(dat == 0x00800000)
                return -1;
       
    if(dat & 0x00800000) { // 判断是负数 最高位24位是符号位
        temp = (((~dat) & 0x007FFFFF) + 1); // 补码变源码
    } else
        temp = dat; // 正数的补码就是源码
       
    return temp;
}


3.修改 kernel/drivers/misc 下 Makefile 和 Kconfig 文件(和上篇文章普通IO口配置一样)
4. 修改 kernel/arch/arm/configs 下的 msm8909-1gb_defconfig 文件(和上篇文章普通IO口配置一样)



5. 执行编译、烧录
make bootimage -j6


6. 测试驱动
系统启动后,用adb命令进入系统




可以看到在 /sys/class/ 目录下生成了 cs1237 文件夹,里面有个 cs1237 文件夹。
下面我们就可以控制 value 文件,去读取cs1237读数。


7.java层获取PT100温度数值
jni层调用cat命令获取cs1237读数
    char path[DIRECTION_MAX];
    char value_str[10];
    int fd;

    snprintf(path, DIRECTION_MAX, "/sys/class/cs1237/cs1237/value");
    fd = open(path, O_RDONLY);
    if (fd < 0) {
        LOGE("failed to open gpio value for reading!n");
        return 1003;
    }

    if (read(fd, value_str, 10) < 0) {
        LOGE("failed to read value!n");
        return 1003;
    }

    close(fd);

    return (atoi(value_str));
java层调用jni函数,将读数转成温度数值
public static float getTemp() {
        //获取得到AD数值
        int data = JNIhardware.getTempure();

        //Log.e("Temp","temp ad data =>"+data);

        //根据威廉希尔官方网站 图,将AD数值转成电压
        float vo = (float)data/(float)0x800000 * 3.3f / 2.0f;
        vo = vo/2;
        //将电压转换成pt100电阻值
        float pt100 = 3000 * vo / (3.3f - vo);
        //根据pt100电阻特性,得到温度。offset_tem表示误差
        float tempdat = ( pt100 - 100) / 0.39f + offset_tem;

        Log.e("Temp","temp => "+tempdat);

        return tempdat;
    }
举报

更多回帖

发帖
×
20
完善资料,
赚取积分