一、前言
又是一个熬夜的夜晚,已经不知道连续多少天没有正常休息了,每天早期都要下定决心晚上早点睡觉,可是一到了晚上就是来思路学习或者干点什么的冲动,赶紧写完睡觉。
这是我第一个基于全志a40i平台写的Linux驱动,可能不是特别标准,也未使用到设备树,不是特别完美,万事开头难,开了头后续我会继续更新A40i使用笔记系列的。
二、环境
全志A40i
linux3.10
三、正文
本文主要就是记录一下代码把,函数的详细描述我也就不述说了,网上大把教学视频,都比较专业。
为什么一定要写底层驱动呢,主要原因是,再涉及到界面开发的项目,已经使用到qt界面开发,就相当于转战linux平台,不再去使用stm32做界面开发,也不是说32就不用了,在无界面开发或者小屏oled或者点阵屏,一些控制类还是使用的到的。既然使用了Linux做上位机,那么对应就有一些底层的ic需要驱动读取信息,这里我就用到了hx711读取重量,但是还不想附加一个32再采用串口去通讯,主要就是不想多余占用板子空间,因为linux本身就可以驱动ic,为什么就只因为我不会驱动开发就放弃而去使用32呢,所以就学习,到目前位置差不多一个礼拜,看视频+实际敲代码验证,也算是搞出来了基础框架了。
下面正文,hx711驱动使用一个c文件,还有一个应用层文件
驱动文件核心代码如下:
//入口:insmod
static int hx711_init(void)
{
int err;
int i;
printk(KERN_INFO "hx711_drv_init\n");
//注册字符设备
major = register_chrdev(0, "hx711_drv", &hx711_drv); //参数1:0动态分配设备号,参数2:设备名称,参数3:文件操作结构体//返回申请的设备号
//创建class并将class注册到内核中
hx711_class = class_create(THIS_MODULE, "hx711_class");//参数1:owner,参数2:class名称//返回class结构体指针
err = PTR_ERR(hx711_class);
if (IS_ERR(hx711_class)) {
printk(KERN_ERR "hx711_class err\n");
//卸载注册的设备
unregister_chrdev(major, "hx711_drv");//参数1:分配的设备号,参数2:设备名称
return -1;
}
//创建设备文件,通知用户在“/dev/”目录下创件名字为hx711_class-x的设备文件
device_create(hx711_class, NULL, MKDEV(major, 0), NULL, "hx711_class-0", 0); //参数1:class结构体指针,参数3:设备号,参数5:节点名称,参数6:次设备号
//初始化io函数
for(i = 0; i < ARRAY_SIZE(led_info); i++) {//遍历led_info结构体
int result = gpio_request(led_info[i].gpio,led_info[i].name);//先向内核申请GPIO硬件资源;
if(result!=0){//判断申请的GPIO资源是否成功,失败返回提示信息
printk(KERN_ERR "GPIO%d has used...led init err!\n",led_info[i].gpio);//打印错误信息
return -1;
}
}
gpio_direction_output(led_info[0].gpio, 0);//设置 GPIO 为输出功能,输出0 //sclk
gpio_direction_input(led_info[1].gpio);//设置 GPIO 为输人功能 //sda
printk(KERN_INFO "hx711_drv_init ok...\n");
return 0;
}
//出口:rmmod
static void hx711_exit(void)
{
int i;
//释放gpio
for(i = 0; i < ARRAY_SIZE(led_info); i++) {//遍历led_info结构体
gpio_free(led_info[i].gpio);//释放GPIO硬件资源
}
//删除设备类
device_destroy(hx711_class, MKDEV(major, 0));//参数1:class结构体指针,参数2:设备号
//注销class
class_destroy(hx711_class);//参数:class结构体指针
//卸载注册的设备
unregister_chrdev(major, "hx711_drv");//参数1:分配的设备号,参数2:设备名称
printk(KERN_INFO "hx711_drv_exit\n");
}
unsigned long HX711_Read(void) //读取711读到的数据
{
unsigned long val;
unsigned char i;
HX711_SCL1_L();
val = 0;
while(HX711_SDA1_IN());
udelay(1);
for(i=0;i<24;i++){
HX711_SCL1_H();
val=val<<1;
udelay(1);
HX711_SCL1_L();
if(HX711_SDA1_IN())
val++;
udelay(1);
}
HX711_SCL1_H();
val = val^0x800000;
udelay(1);
HX711_SCL1_L();
udelay(1);
return val;
}
int hx711_buffer=0;//定义hx711_buffer来接受hx711read读取的信息
int weight_maopi=0;//毛皮重量
int weight_shiwu=0;//实物重量
//获取毛皮重量,并记录
int Get_Maopi(void)
{
hx711_buffer=HX711_Read();
weight_maopi=hx711_buffer;
return weight_maopi;
}
//获取净重量,并记录
int Get_Weight(void)
{
hx711_buffer=HX711_Read();
if(hx711_buffer>weight_maopi)
weight_shiwu=hx711_buffer-weight_maopi;
// weight_shiwu=weight_shiwu/100;
// weight_shiwu=(unsigned int)((float)weight_shiwu/4.30+0.05);//每一个传感器需要矫正4.30这个除数。当发现测试出来的重量偏大时,增加该数值。如果测试出来的重量偏小时,减小改数值.该数值一般在4.0-5.0之间。因传感器线性斜率不同而定。
return weight_shiwu;
}
应用文件代码如下:
char *hx711_PATH = "/dev/hx711_class-0";
int main(int argc, char **argv)
{
int fd;
int weight_int;
double weight_double;
/* 打开文件 */
fd = open(hx711_PATH, O_RDWR);
if (fd == -1){
printf("can not open file %s\n", hx711_PATH);
return -1;
}
/* 读文件 */
weight_int = 1;
read(fd, &weight_int, sizeof(int));//读取毛皮
printf("maopi = %d\r\n",weight_int);
while(1){
usleep(100000);//延时100ms
weight_int = 2;
read(fd, &weight_int, sizeof(int));//读取净重
weight_double = weight_int;
weight_double = weight_double/100;
weight_double=(unsigned int)((float)weight_double/4.30+0.05);//每一个传感器需要矫正4.30这个除数。当发现测试出来的重量偏大时,增加该数值。如果测试出来的重量偏小时,减小改数值.该数值一般在4.0-5.0之间。因传感器线性斜率不同而定。
printf("weight = %.4fg\r\n",weight_double);
}
close(fd);
return 0;
}
再前文介绍了如何编译驱动文件以及交叉编译应用文件,最后编译完毕生成ko文件和可执行程序文件,如下所示,资源里下载就是
使用到了如下几个命令:
- lsmod查看已经加载的内核模块
- insmod加载内核模块
- rmmod卸载内核模块
- dmesg查看日志信息
- dmesg -c清除日志信息
- ls /dev查看设备节点
- cat /proc/devices查看设备的设备号
最后测试效果如下所示:
四、结语
速战速决,下一个驱动,准备驱动AD7606,搞起~