lowermachine/source/linux_driver/encoder.c
Miaow 71f8e672d5 机器已经部署到淮安
8月底出差告一段落,运行一切正常
1. 添加了喷阀数据队列走空计数和自动补偿功能
2. 喷阀数据队列发生不可恢复的异常时向上位机报告错误
3. 取消双FIFO
2022-09-01 21:40:16 +08:00

290 lines
8.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#define ENCODER_CNT 1 /* 主设备号 */
#define ENCODER_NAME "encoder" /* 设备名字 */
#define ENCODER_CMD_FUNCTION_CLEAR 1
#define ENCODER_CMD_FUNCTION_VIRT_INPUT 2
#define ENCODER_CMD_INPUT_MODE_EXTERNEL 100
#define ENCODER_CMD_INPUT_MODE_INTERNEL 101
/*
* 相关寄存器地址定义
*/
#define ENCODER_REG_BASE 0x43C10000
#define ENCODER_REG_0_OFFSET 0x00000000
#define ENCODER_REG_1_OFFSET 0x00000004
#define ENCODER_REG_2_OFFSET 0x00000008
#define ENCODER_REG_3_OFFSET 0x0000000C
#define ENCODER_CR_VTS_MASK ((u32)(1 << 2)) // 内部触发信号 (Virtual Triggle Signal) MOD位置1时由上位机软件写入将该位信号作为触发信号
#define ENCODER_CR_MOD_MASK ((u32)(1 << 1)) // 模式选择 (Mode) 0: 外部触发模式,外部触发编码器转动, 1: 内部触发模式,上位机软件模拟触发信号
#define ENCODER_CR_CLR_MASK ((u32)(1 << 0)) // 清除缓存 (Clear) 清除编码和分频控制器内部的分频计数值不影响VDIV和CDIV
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *encoder_cr_addr;
static void __iomem *encoder_vdivr_addr;
static void __iomem *encoder_cdivr_addr;
static void __iomem *encoder_reg3_addr;
struct encoder_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
typedef struct
{
u32 valve_divide_value;
u32 camera_divide_value;
} kernelbuf_typedef;
static struct encoder_dev encoder;
/*
* @description : 打开设备
* @param inode : 传递给驱动的inode
* @param - filp : 设备文件file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int encoder_open(struct inode *inode, struct file *filp)
{
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t encoder_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
u32 data;
kernelbuf_typedef kern_buf = {
.valve_divide_value = 0,
.camera_divide_value = 0,
};
if (cnt != sizeof(kern_buf))
{
printk(KERN_ERR "encoder write: cnt error, cnt=%d", cnt);
return -EFAULT;
}
ret = copy_from_user(&kern_buf, buf, cnt); // 得到应用层传递过来的数据
if (ret < 0)
{
printk(KERN_ERR "kernel write failed!\r\n");
return -EFAULT;
}
// 最小分频值为4
if (kern_buf.valve_divide_value < 2 || kern_buf.camera_divide_value < 4)
{
return -EFAULT;
}
// 写入0后清除ENCODER内部计数器缓存清除
data = readl(encoder_cr_addr);
writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr);
if (kern_buf.valve_divide_value != 0)
writel(kern_buf.valve_divide_value, encoder_vdivr_addr);
if (kern_buf.camera_divide_value != 0)
writel(kern_buf.camera_divide_value, encoder_cdivr_addr);
// 写入1退出清除状态使得ENCODER内部计数器能正常工作内部计数器在正常工作前已经被清零
writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr);
return cnt;
}
/*
* @description : 关闭/释放设备
* @param filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int encoder_release(struct inode *inode, struct file *filp)
{
return 0;
}
static long encoder_ioctl(struct file *fp, unsigned int cmd, unsigned long tmp)
{
u32 data, cmd_parsed;
if (_IOC_TYPE(cmd) != 'D' || _IOC_DIR(cmd) != _IOC_WRITE)
{
printk(KERN_ERR "IOC_TYPE or IOC_WRITE error: IOC_TYPE=%c, IOC_WRITE=%d\r\n", _IOC_TYPE(cmd), _IOC_DIR(cmd));
return -EINVAL;
}
cmd_parsed = _IOC_NR(cmd);
data = readl(encoder_cr_addr);
if (cmd_parsed == ENCODER_CMD_FUNCTION_CLEAR)
{
writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr); // 写入0后清除ENCODER内部计数器缓存清除
writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr); // 写入1退出清除状态使得ENCODER内部计数器能正常工作内部计数器在正常工作前已经被清零
}
else if (cmd_parsed == ENCODER_CMD_INPUT_MODE_EXTERNEL)
{
// 设为外部触发模式
writel(data & ~ENCODER_CR_MOD_MASK, encoder_cr_addr);
}
else if (cmd_parsed == ENCODER_CMD_INPUT_MODE_INTERNEL)
{
// 设为内部触发模式
writel(data | (u32)(1 << 1), encoder_cr_addr);
}
else if (cmd_parsed == ENCODER_CMD_FUNCTION_VIRT_INPUT)
{
int i;
// 虚拟触发tmp为周期数
// 1. 设为内部触发模式
writel(data | (u32)(1 << 1), encoder_cr_addr);
// 2. 产生虚拟的高低电平
for (i = 0; i < tmp; i++)
{
writel(data & ~ENCODER_CR_VTS_MASK, encoder_cr_addr);
writel(data | ENCODER_CR_VTS_MASK, encoder_cr_addr);
}
// 3. 恢复为原来的状态和模式
writel(data, encoder_cr_addr);
}
return 0;
}
/* 设备操作函数 */
static struct file_operations encoder_fops = {
.owner = THIS_MODULE,
.open = encoder_open,
.write = encoder_write,
.release = encoder_release,
.unlocked_ioctl = encoder_ioctl,
};
static int __init encoder_init(void)
{
int ret;
u32 data;
/* 寄存器地址映射 */
encoder_cr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_0_OFFSET, 4);
encoder_vdivr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_1_OFFSET, 4);
encoder_cdivr_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_2_OFFSET, 4);
encoder_reg3_addr = ioremap(ENCODER_REG_BASE + ENCODER_REG_3_OFFSET, 4);
/* 注册字符设备驱动 */
//(1)创建设备号
if (encoder.major)
{
encoder.devid = MKDEV(encoder.major, 0);
ret = register_chrdev_region(encoder.devid, ENCODER_CNT, ENCODER_NAME);
if (ret)
goto FAIL_REGISTER_CHR_DEV;
}
else
{
ret = alloc_chrdev_region(&encoder.devid, 0, ENCODER_CNT, ENCODER_NAME);
if (ret)
goto FAIL_REGISTER_CHR_DEV;
encoder.major = MAJOR(encoder.devid);
encoder.minor = MINOR(encoder.devid);
}
//(2)初始化cdev
encoder.cdev.owner = THIS_MODULE;
cdev_init(&encoder.cdev, &encoder_fops);
//(3)添加cdev
ret = cdev_add(&encoder.cdev, encoder.devid, ENCODER_CNT);
if (ret)
goto FAIL_ADD_CDEV;
//(4)创建类
encoder.class = class_create(THIS_MODULE, ENCODER_NAME);
if (IS_ERR(encoder.class))
{
ret = PTR_ERR(encoder.class);
goto FAIL_CREATE_CLASS;
}
//(5)创建设备
encoder.device = device_create(encoder.class, NULL, encoder.devid, NULL, ENCODER_NAME);
if (IS_ERR(encoder.device))
{
ret = PTR_ERR(encoder.device);
goto FAIL_CREATE_DEV;
}
//默认分频系数1000
data = readl(encoder_cr_addr);
writel(data & ~ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除硬件计数器缓存
writel(1000, encoder_vdivr_addr); // 设置阀触发分频
writel(1000, encoder_cdivr_addr); // 设置相机触发分频
writel(data | ENCODER_CR_CLR_MASK, encoder_cr_addr); // 清除完毕
return 0;
FAIL_CREATE_DEV:
class_destroy(encoder.class);
FAIL_CREATE_CLASS:
cdev_del(&encoder.cdev);
FAIL_ADD_CDEV:
unregister_chrdev_region(encoder.devid, ENCODER_CNT);
FAIL_REGISTER_CHR_DEV:
iounmap(encoder_cr_addr);
iounmap(encoder_vdivr_addr);
iounmap(encoder_cdivr_addr);
iounmap(encoder_reg3_addr);
return ret;
}
static void __exit encoder_exit(void)
{
//(1)注销设备
device_destroy(encoder.class, encoder.devid);
//(2)注销类
class_destroy(encoder.class);
//(3)删除cdev
cdev_del(&encoder.cdev);
//(4)注销设备号
unregister_chrdev_region(encoder.devid, ENCODER_CNT);
//(5)取消内存映射
iounmap(encoder_cr_addr);
iounmap(encoder_vdivr_addr);
iounmap(encoder_cdivr_addr);
iounmap(encoder_reg3_addr);
}
/* 驱动模块入口和出口函数注册 */
module_init(encoder_init);
module_exit(encoder_exit);
MODULE_AUTHOR("DingKun");
MODULE_DESCRIPTION("driver for hardware encoder in the platform");
MODULE_LICENSE("GPL");