前言
上面一个文章配置了普通的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;
}
前言
上面一个文章配置了普通的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;
}
举报