gpio是最常用的也是最简单的驱动程序,在龙芯2k0300中,几乎所有的gpio引脚都可以做为gpio引脚使用(多数具备复用功能),此文通过控制gpio的输入,从而控制继电器relay,进而控制后端电路的开断。
硬件电路
gpio
蜂鸟的扩展引脚J9原理图如下所示,多数可用做复用功能,此时使用I2S的引脚来做gpio
继电器
继电器模块常用于控制高电流或高电压的电路
工作原理:
电磁线圈: 继电器内部包含一个电磁线圈,通常由绕制在绝缘芯片上的细导线组成。当通过线圈通电时,产生电磁场。
磁性吸引: 电磁场会使继电器中的铁芯(或磁性材料)受到磁性吸引,导致铁芯在电磁力的作用下移动。
触点操作: 铁芯的移动会导致机械部分的运动,最终使触点(开关)发生动作。继电器通常有常开(Normally Open,NO)和常闭(Normally Closed,NC)两组触点。
常闭触点: 在继电器未通电时处于闭合状态,当电磁线圈通电时,触点打开。
常开触点: 在继电器未通电时处于打开状态,当电磁线圈通电时,触点闭合。
电气隔离: 继电器的主要作用之一是提供电气隔离。通过电磁原理,可以在控制信号与被控制电路之间提供隔离,从而使得不同电路之间的电流不会相互影响。
驱动适配
设备树
配置gpio引脚,如上所述,使用J9-25引脚,由《引脚复用手册》可知,此引脚为gpio76,故配置如下
gpio_relay{
compatible = "lx,gpio-relay";
label = "relay";
gpios = <&gpio 76 GPIO_ACTIVE_LOW>; //J9-25:cpu_i2s_mclk:gpio76
status = "ok";
};
驱动
平台设备
static const struct of_device_id relays_of_match[] = {
{.compatible = "lx,gpio-relay"},
{ }
};
static struct platform_driver lx_relays_driver = {
.driver = {
.name = "relays",
.of_match_table = relays_of_match,
},
.probe = lx_relays_probe,
.remove = lx_relays_remove,
};
probe
注册字符设备,创建class/device,引用设备树
static int lx_relays_probe(struct platform_device *dev)
{
int ret = 0;
pr_info("relays module probe\\\\r\\\\n");
relaysdev.major = 0;
if(relaysdev.major)
{
relaysdev.devid = MKDEV(relaysdev.major,0);
ret = register_chrdev_region(relaysdev.devid,RELAYS_CNT,RELAYS_NAME);
}else
{
ret = alloc_chrdev_region(&relaysdev.devid,0,RELAYS_CNT,RELAYS_NAME);
}
if(ret < 0)
{
pr_info("relays module fail devid\\\\r\\\\n");
goto fail_devid;
}else
{
relaysdev.major = MAJOR(relaysdev.devid);
relaysdev.minor = MINOR(relaysdev.devid);
pr_info("major:%d minor:%d \\\\r\\\\n",relaysdev.major,relaysdev.minor);
}
relaysdev.cdev.owner = THIS_MODULE;
cdev_init(&relaysdev.cdev,&lx_relays_fops);
cdev_add(&relaysdev.cdev,relaysdev.devid,RELAYS_CNT);
relaysdev.class = class_create(THIS_MODULE,RELAYS_NAME);
if(IS_ERR(relaysdev.device))
{
ret = PTR_ERR(relaysdev.device);
goto fail_class;
}
relaysdev.device = device_create(relaysdev.class,NULL,relaysdev.devid,NULL,RELAYS_NAME);
if(IS_ERR(relaysdev.device))
{
ret = PTR_ERR(relaysdev.device);
goto fail_device;
}
relaysdev.nd = of_find_node_by_path("/gpio_relay");
if(relaysdev.nd == NULL)
{
ret = -EINVAL;
pr_info("relays module fail node\\\\r\\\\n");
goto fail_nd;
}
relaysdev.relaysgpio = of_get_named_gpio(relaysdev.nd,"gpios",0);
if(relaysdev.relaysgpio < 0)
{
ret = -EINVAL;
printk("relays module fail gpio\\\\r\\\\n");
goto fail_gpio;
}
gpio_request(relaysdev.relaysgpio,"gpios");
gpio_direction_output(relaysdev.relaysgpio,0);
return 0;
fail_gpio:
fail_nd:
device_destroy(relaysdev.class,relaysdev.devid);
fail_device:
class_destroy(relaysdev.class);
fail_class:
unregister_chrdev_region(relaysdev.devid,RELAYS_CNT);
cdev_del(&relaysdev.cdev);
fail_devid:
return ret;
}
控制
控制引脚输出高低电平
ssize_t lx_relays_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *ppos)
{
struct lx_relays_dev *dev = (struct lx_relays_dev*)filp->private_data;
int retval;
unsigned char datebuf[1];
retval=copy_from_user(datebuf,buf,cnt);
if(retval < 0)
{
pr_info("kernel write failed\\\\r\\\\n");
return -EFAULT;
}
pr_info("write:%d\\\\n",datebuf[0]);
if(datebuf[0] == RELAYS_ON)
{
gpio_set_value(dev->relaysgpio,0);
}else if(datebuf[0] == RELAYS_OFF)
{
gpio_set_value(dev->relaysgpio,1);
}
return 0;
}
应用测试
int main(int argc, char **argv)
{
int fd;
int status;
if (argc != 2)
{
printf("Usage: %s /dev/gpioled \\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
printf("can not open file %s\\n", argv[1]);
return -1;
}
do
{
printf("please input:/0/1/q\\n");
scanf("%d",&status);
printf("%d\\n",status);
if (status == 0)
{
write(fd, &status, 1);
}
else if(status== 1)
{
write(fd, &status, 1);
}
else
{
printf("please error\\n");
}
} while(1);
close(fd);
return 0;
}
验证效果
加载驱动
运行应用
效果示例