SC16IS752驱动编写与调试记录

    xiaoxiao2025-07-11  6

    折腾了几天,终于把spi扩展串口给搞定了,这个芯片有两个通道、可配置波特率、支持IIC和spi通信,支持中断等诸多优良特性。 主cpu是采用AT91SAM9X35平台,linux内核采用的是2.6.39,文件系统是用buildroot生成的。 由于之前用通用驱动来操作752芯片,所以,我们需要先关闭内核通用驱动的配置(不关闭也是可以的,modalias一改,通用驱动就匹配不上了)。好了,先上原理图:   通过查看原理图,可以知道采用的是SPI模式,并且与cpu的spi0相连,IRQ中断和cpu的PB18引脚。 打开board-sam9x5ek.c文件,先把原来通用驱动的板级配置注释掉: /* static struct spi_board_info at91sam9x5_spi_devices[] = { { .modalias= "spidev", .max_speed_hz= 1000000, .bus_num= 0, .chip_select= 0, .mode = SPI_MODE_0, }, }; */然后加上如下的代码: static struct spi_board_info at91sam9x5_spi_devices[] = { { .modalias= "sc16is752", .max_speed_hz= 1000000, .bus_num= 0, .chip_select= 0, .mode = SPI_MODE_0, .irq = AT91_PIN_PB18, }, }; .modalias是设置spi设备名字的,我们设置为“sc16is752”.max_speed_hz是设置spi时钟频率,752最大支持4Mhz。.bus_num是表示采用哪一个控制器的,假如我们采用spi1,则bus_num设置为1,chip_select表示的是高电平还是低电平选择752。Mode表示是使用哪种模式,spi有四种模式,关于四种模式,百度有详细介绍,通过查看752芯片手册,我们知道752工作在模式0。Irq是表示中断号,752irq是和PB18相连接的,所以设置为AT91_PIN_PB18,AT91_PIN_PB18是一个宏,展开后是82,有兴趣的自己去了解smx35的硬件中断。需要注意的是,这不是spi的中断,别搞错了,是752外设FIFO缓冲区有内容的中断。   板级配置好了之后,会通过ek_board_init函数,然后调用at91_add_device_spi(at91sam9x5_spi_devices, ARRAY_SIZE(at91sam9x5_spi_devices)); 将板级文件信息注册到linux内核。 接下来编写驱动,新建文件:sc16is752.c: #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/spi/spi.h> #include <linux/miscdevice.h> #include <linux/device.h> #include <linux/interrupt.h> #include <linux/io.h> #include <asm/uaccess.h> #include <linux/sched.h> #define RHR 0x00 //R #define THR 0x00 //W #define IER 0x01 #define FCR 0x02 //R #define IIR 0x02 //W #define LCR 0x03 #define MCR 0x04 #define LSR 0x05 #define MSR_1 0x06 //本来是直接定义为MSR的,但是这样定义与汇编指令MSR有冲突 #define SPR 0x07 #define TCR 0x06 //These registers are accessible only when EFR[4] = 1, and MCR[2] = 1 #define TLR 0x07 #define TXLVL 0x08 #define RXLVL 0x09 #define IODIR_752 0x0a #define IOSTATE 0x0b #define IOINTENA 0x0c #define RESERVED 0x0d #define IOCONTROL 0x0e #define EFCR 0x0f //special register The Special Register set is accessible only when LCR[7] = 1 and not 0xBF #define DLL 0x00 #define DLH 0x01 //enhanced register Enhanced Features Registers are only accessible when LCR = 0xBF #define EFR 0x02 #define XON1 0x04 #define XON2 0x05 #define XOFF1 0x06 #define XOFF2 0x07 //定义端口 #define channelA 0x0 #define channelB 0x1 //定义spi uart操作幻数 #define SET_channleA _IOW('k', 0, int) #define SET_channleB _IOW('k', 1, int) #define SET_STOP_BIT _IOW('k', 2, int) #define SET_BPS _IOW('k', 3, int) struct sc16is752_dev { struct spi_device *spi; spinlock_t spi_lock; struct mutex lock; unsigned char *buf; unsigned int channel_num; int irq; struct work_struct *sc752_irq_work; wait_queue_head_t sc16is752_q; unsigned int condition; }; static struct sc16is752_dev *sc16is752 = NULL; static char read_752_reg(unsigned int reg, unsigned int channel) { struct spi_transfer t[2]; struct spi_message m; char val = 0; unsigned char cmd = 0x80 | (reg << 3) | (channel << 1); spi_message_init(&m); memset(t, 0, (sizeof t)); t[0].tx_buf = &cmd; t[0].len = 1; spi_message_add_tail(&t[0], &m); t[1].rx_buf = &val; t[1].len = 1; spi_message_add_tail(&t[1], &m); spin_lock_irq(&sc16is752->spi_lock); spi_sync(sc16is752->spi, &m); spin_unlock_irq(&sc16is752->spi_lock); return val; } static ssize_t read_channel(int len) { #if 0 struct spi_transfer t[2]; struct spi_message m; unsigned char cmd; cmd = 0x80 | (THR << 3) | (sc16is752->channel_num<< 1); printk(KERN_INFO "enter read_channel\n"); spi_message_init(&m); memset(t, 0, (sizeof t)); t[0].tx_buf = &cmd; t[0].len = 1; spi_message_add_tail(&t[0], &m); t[1].rx_buf = sc16is752->buf; t[1].len = len; spi_message_add_tail(&t[1], &m); spin_lock_irq(&sc16is752->spi_lock); spi_sync(sc16is752->spi, &m); spin_unlock_irq(&sc16is752->spi_lock); printk(KERN_INFO "m.actual_length = %d\n",m.actual_length); return m.actual_length; #endif unsigned int length = 0; unsigned char status = 0; do{ status = read_752_reg(LSR, sc16is752->channel_num); if(status & 0x01) { sc16is752->buf[length] = read_752_reg(RHR, sc16is752->channel_num); length ++; } }while(status & 0x01); return length; } static void write_752_reg(unsigned int reg, unsigned int channel, unsigned char data) { struct spi_transfer t[2]; struct spi_message m; char val = data; unsigned char cmd; cmd = 0x00 | (reg << 3) | (channel << 1); spi_message_init(&m); memset(t, 0, (sizeof t)); t[0].tx_buf = &cmd; t[0].len = 1; spi_message_add_tail(&t[0], &m); t[1].tx_buf = &val; t[1].len = 1; spi_message_add_tail(&t[1], &m); spin_lock_irq(&sc16is752->spi_lock); spi_sync(sc16is752->spi, &m); spin_unlock_irq(&sc16is752->spi_lock); return; } static void write_channel(unsigned char *buf, int len) { struct spi_transfer t[2]; struct spi_message m; unsigned char status; unsigned char cmd; cmd = 0x00 | (THR << 3) | (sc16is752->channel_num << 1); status = read_752_reg(LSR, sc16is752->channel_num); if (status & 0x20) { spi_message_init(&m); memset(t, 0, (sizeof t)); t[0].tx_buf = &cmd; t[0].len = 1; spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; t[1].len = len; spi_message_add_tail(&t[1], &m); spin_lock_irq(&sc16is752->spi_lock); spi_sync(sc16is752->spi, &m); spin_unlock_irq(&sc16is752->spi_lock); } return; } static int sc16is752_open(struct inode *inode, struct file *filp) { filp->private_data = sc16is752; /*initlizate channel A*/ write_752_reg(LCR, channelA, 0x80); write_752_reg(DLL, channelA, 0x14); write_752_reg(DLH, channelA, 0x00); write_752_reg(LCR, channelA, 0xbf); write_752_reg(EFR, channelA, 0x10); write_752_reg(LCR, channelA, 0x03); write_752_reg(IER, channelA, 0x01); write_752_reg(FCR, channelA, 0xf1); //write_752_reg(FCR, channelA, 0x51); write_752_reg(SPR, channelA, 0x41); write_752_reg(IODIR_752, channelA, 0xff); write_752_reg(IOSTATE, channelA, 0x00); /*initlizate channel B*/ write_752_reg(LCR, channelB, 0x80); write_752_reg(DLL, channelB, 0x14); write_752_reg(DLH, channelB, 0x00); write_752_reg(LCR, channelB, 0xbf); write_752_reg(EFR, channelB, 0x10); write_752_reg(LCR, channelB, 0x03); write_752_reg(IER, channelB, 0x01); write_752_reg(FCR, channelB, 0xf1); //write_752_reg(FCR, channelB, 0x51); write_752_reg(SPR, channelB, 0x41); write_752_reg(IODIR_752, channelB, 0xff); write_752_reg(IOSTATE, channelB, 0x00); //request spi buffer sc16is752->buf = kmalloc(64,GFP_KERNEL); if(!sc16is752->buf) { printk(KERN_INFO "kzallo buf error\n"); return -ENOMEM; } nonseekable_open(inode, filp);//设置为不可随机读取。 printk(KERN_INFO "open and initlizate channel A/B succeed\n "); return 0; } static int sc16is752_release(struct inode *inode, struct file *filp) { if (NULL != sc16is752->buf) kfree(sc16is752->buf); return 0; } static long sc16is752_ioctl(struct file *filp, unsigned int cmd, unsigned long args) { unsigned char tmp = 0; switch (cmd) { case SET_channleA: { sc16is752->channel_num = channelA; break; } case SET_channleB: { sc16is752->channel_num = channelB; break; } case SET_STOP_BIT: { tmp = read_752_reg(LCR, sc16is752->channel_num); if(1 == args) { tmp &= (~(0x01<<2));//设置1位停止位 write_752_reg(LCR, sc16is752->channel_num, tmp); } else if (2 == args) { tmp |= (0x01<<2);//设置1.5/2位停止位 write_752_reg(LCR, sc16is752->channel_num, tmp); } break; } case SET_BPS: { tmp = read_752_reg(IER, sc16is752->channel_num); tmp &= (~(0x01<<4)); //禁止睡眠模式才可以设置波特率。 write_752_reg(IER, sc16is752->channel_num, tmp); if(9600 == args) { write_752_reg(LCR, sc16is752->channel_num, 0x80); write_752_reg(DLL, sc16is752->channel_num, 0x14); write_752_reg(DLH, sc16is752->channel_num, 0x00); write_752_reg(LCR, sc16is752->channel_num, 0xbf); write_752_reg(EFR, sc16is752->channel_num, 0x10); write_752_reg(LCR, sc16is752->channel_num, 0x03); write_752_reg(IER, sc16is752->channel_num, 0x01); write_752_reg(FCR, sc16is752->channel_num, 0xf1); write_752_reg(SPR, sc16is752->channel_num, 0x41); write_752_reg(IODIR_752, sc16is752->channel_num, 0xff); write_752_reg(IOSTATE, sc16is752->channel_num, 0x00); } else if(19200 == args) { write_752_reg(LCR, sc16is752->channel_num, 0x80); write_752_reg(DLL, sc16is752->channel_num, 0x0a); write_752_reg(DLH, sc16is752->channel_num, 0x00); write_752_reg(LCR, sc16is752->channel_num, 0xbf); write_752_reg(EFR, sc16is752->channel_num, 0x10); write_752_reg(LCR, sc16is752->channel_num, 0x03); write_752_reg(IER, sc16is752->channel_num, 0x01); write_752_reg(FCR, sc16is752->channel_num, 0xf1); write_752_reg(SPR, sc16is752->channel_num, 0x41); write_752_reg(IODIR_752, sc16is752->channel_num, 0xff); write_752_reg(IOSTATE, sc16is752->channel_num, 0x00); } else if(38400 == args) { write_752_reg(LCR, sc16is752->channel_num, 0x80); write_752_reg(DLL, sc16is752->channel_num, 0x05); write_752_reg(DLH, sc16is752->channel_num, 0x00); write_752_reg(LCR, sc16is752->channel_num, 0xbf); write_752_reg(EFR, sc16is752->channel_num, 0x10); write_752_reg(LCR, sc16is752->channel_num, 0x03); write_752_reg(IER, sc16is752->channel_num, 0x01); write_752_reg(FCR, sc16is752->channel_num, 0xf1); write_752_reg(SPR, sc16is752->channel_num, 0x41); write_752_reg(IODIR_752, sc16is752->channel_num, 0xff); write_752_reg(IOSTATE, sc16is752->channel_num, 0x00); } break; } default: break; } return 0; } static ssize_t sc16is752_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { struct sc16is752_dev *sc16is752 = filp->private_data; int length = 0; int missing; //wait_event(sc16is752->sc16is752_q,sc16is752->condition);//用此函数会导致进程杀不死。 wait_event_interruptible(sc16is752->sc16is752_q,sc16is752->condition); mutex_lock(&sc16is752->lock); length = read_channel(size); missing = copy_to_user(buf, sc16is752->buf, length); if (missing == length) length = -EFAULT; else length = length - missing; mutex_unlock(&sc16is752->lock); sc16is752->condition = 0; return length; } static ssize_t sc16is752_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { int missing ; struct sc16is752_dev *sc16is752 = filp->private_data; if(size > 64) return -EMSGSIZE; mutex_lock(&sc16is752->lock); missing = copy_from_user(sc16is752->buf, buf, size); write_channel(sc16is752->buf, size); mutex_unlock(&sc16is752->lock); return 0; } struct file_operations sc16is752_fops = { .owner = THIS_MODULE, .open = sc16is752_open, .write = sc16is752_write, .read = sc16is752_read, .unlocked_ioctl = sc16is752_ioctl, .release = sc16is752_release, }; struct miscdevice sc16is752_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "sc16is752", .fops = &sc16is752_fops, }; void sc752_work_func(struct work_struct *work) { unsigned char tmp = 0; tmp = read_752_reg(IIR, sc16is752->channel_num); printk(KERN_INFO "enter sc752_work_func\n"); if(tmp & 0x04) { sc16is752->condition = 1; wake_up(&sc16is752->sc16is752_q); } } irqreturn_t sc16is752_handler(int irq, void *dev_id) { schedule_work(sc16is752->sc752_irq_work); printk(KERN_INFO "enter sc16is752_handler\n"); return IRQ_HANDLED; } static int sc16is752_probe(struct spi_device *spi) { int ret = 0; //printk(KERN_INFO "Spi device sc16is752 is probed!\n"); sc16is752 = kzalloc(sizeof(*sc16is752), GFP_KERNEL); if (!sc16is752) return -ENOMEM; sc16is752->channel_num = channelA;//默认使用channelA sc16is752->buf = NULL; sc16is752->irq = spi->irq; //中断 sc16is752->spi = spi; spin_lock_init(&sc16is752->spi_lock); mutex_init(&sc16is752->lock); ret = misc_register(&sc16is752_misc); if (ret != 0) { printk(KERN_INFO "cannot register miscdev err = %d\n", ret); } //printk(KERN_INFO "sc16is752->irq %d\n",sc16is752->irq); ret = request_threaded_irq(sc16is752->irq, NULL, sc16is752_handler, IRQF_TRIGGER_FALLING, "sc16is752", NULL); if (ret < 0 ) { printk(KERN_INFO "Failed to request IRQ!\n"); } sc16is752->sc752_irq_work = kzalloc(sizeof(struct work_struct),GFP_KERNEL); INIT_WORK(sc16is752->sc752_irq_work, sc752_work_func); init_waitqueue_head(&sc16is752->sc16is752_q); sc16is752->condition = 0;//初始化等待条件为0 spi_set_drvdata(spi, sc16is752); return 0; } static int __devexit sc16is752_remove(struct spi_device *spi) { misc_deregister(&sc16is752_misc); free_irq(sc16is752->irq,NULL); kfree(sc16is752); return 0; } static struct spi_driver sc16is752_driver = { .driver = { .name = "sc16is752", .owner = THIS_MODULE, }, .probe = sc16is752_probe, .remove = __devexit_p(sc16is752_remove), }; static int __init sc16is752_init(void) { return spi_register_driver(&sc16is752_driver); } static void __exit sc16is752_exit(void) { spi_unregister_driver(&sc16is752_driver); } module_init(sc16is752_init); module_exit(sc16is752_exit); MODULE_DESCRIPTION("Driver for most SPI SC16IS752"); MODULE_AUTHOR("tianyu"); MODULE_LICENSE("GPL"); MODULE_ALIAS("sct");上面代码注册了一个spi驱动,然后注册一个混杂设备驱动,实现了常用的系统调用。代码运行成功后,会在板子的/dev/目录下,生成一个sc16is752设备文件。打开该设备,是进行设备的初始化,将752设置为中断模式、1位停止位、波特率9600、无奇偶校验、8位数据位。 当应用程序去read sc16is752时,会判断buf中是否接收到了数据,如果没有数据,则让进程休眠,如果有数据,则通过spi接口读取752 fifo中的数据。 752 fifo中一旦接收到了数据,就会产生中断,在中断处理函数中,提交了一个工作,然后便返回,将复杂的中断处理过程留给工作队列去处理。sc752_work_func将处理下半部分工作,在sc752_work_func中,我们先去读取752的IIR寄存器,判断是不是接收数据FIFO中断,然后把唤醒条件置1,并且唤醒工作队列。唤醒进程之后,便继续执行read函数。 下面分析一些比较重要的代码。 struct sc16is752_dev { struct spi_device *spi;//用于存放spi设备结构 spinlock_t spi_lock;//定义自旋锁 struct mutex lock;//互斥锁 unsigned char *buf;//spi设备缓冲区,之后会申请为64字节 unsigned int channel_num;//设备的通道号,表示我要操作那个通道 int irq;//中断号 struct work_struct *sc752_irq_work;//工作队列 wait_queue_head_t sc16is752_q;//进程等待队列 unsigned int condition;//唤醒条件 }; 为了方便操作该设备,我们定义一个结构体来描述这个设备的一些基本特性,然后声明一个struct sc16is752_dev类型全局指针。Probe函数中,将对这个全局指针进行初始化和赋值。 static int sc16is752_probe(struct spi_device *spi) { int ret = 0; //printk(KERN_INFO "Spi device sc16is752 is probed!\n"); sc16is752 = kzalloc(sizeof(*sc16is752), GFP_KERNEL); if (!sc16is752) return -ENOMEM; sc16is752->channel_num = channelA;//默认使用channelA sc16is752->buf = NULL; sc16is752->irq = spi->irq; //中断 sc16is752->spi = spi; spin_lock_init(&sc16is752->spi_lock); mutex_init(&sc16is752->lock); ret = misc_register(&sc16is752_misc); if (ret != 0) { printk(KERN_INFO "cannot register miscdev err = %d\n", ret); } //printk(KERN_INFO "sc16is752->irq %d\n",sc16is752->irq); ret = request_threaded_irq(sc16is752->irq, NULL, sc16is752_handler, IRQF_TRIGGER_FALLING, "sc16is752", NULL); if (ret < 0 ) { printk(KERN_INFO "Failed to request IRQ!\n"); } sc16is752->sc752_irq_work = kzalloc(sizeof(struct work_struct),GFP_KERNEL); INIT_WORK(sc16is752->sc752_irq_work, sc752_work_func); init_waitqueue_head(&sc16is752->sc16is752_q); sc16is752->condition = 0;//初始化等待条件为0 spi_set_drvdata(spi, sc16is752); return 0; } Probe函数中,首先使用kzalloc,分配一个struct sc16is752_dev空间,kzallc与kmallc的区别在于kzalloc会对这个空间进行初始化为0的操作,其它特性一模一样。 sc16is752->irq = spi->irq;获取中断号,中断号在板级文件中定义了,.irq = AT91_PIN_PB18,  sc16is752->spi = spi;将spi的结构赋值给sc16is752全局指针,后面我们就可以利用sc16is752->spi方便的调用spi core层提供的函数了。 ret = misc_register(&sc16is752_misc);用于注册一个混杂设备,这个混杂设备采用自动分配次设备号。 ret = request_threaded_irq(sc16is752->irq, NULL, sc16is752_handler, IRQF_TRIGGER_FALLING, "sc16is752", NULL);申请中断,因为752 FIFO中有数据是低电平的,所以采用下降沿触发中断的方式。 sc16is752->sc752_irq_work = kzalloc(sizeof(struct work_struct),GFP_KERNEL);分配一个工作结构体空间。 INIT_WORK(sc16is752->sc752_irq_work, sc752_work_func);初始化工作。 init_waitqueue_head(&sc16is752->sc16is752_q);初始化工作队列。 sc16is752->condition = 0;初始化等待条件为0 spi_set_drvdata(spi, sc16is752); 用来存储驱动中要用到的私有数据 sc16is752_misc结构: struct miscdevice sc16is752_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "sc16is752", .fops = &sc16is752_fops, }; 混杂设备操作函数集: struct file_operations sc16is752_fops = { .owner = THIS_MODULE, .open = sc16is752_open, .write = sc16is752_write, .read = sc16is752_read, .unlocked_ioctl = sc16is752_ioctl, .release = sc16is752_release, }; sc16is752_open函数主要是进行752的初始化、将全局指针sc16is752复制给设备的私有数据filp->private_data = sc16is752,然后给spi申请一个64byte的缓存空间sc16is752->buf = kmalloc(64,GFP_KERNEL);最后将此设备文件设置为不可随机读取nonseekable_open(inode, filp)。 sc16is752_release函数就是释放了申请的64byte的buf缓冲区。 sc16is752_ioctl实现了通道的设置、波特率设置、停止位设置。需要注意波特率设置那一块,通过实践发现,不能够直接写DLL和DLH去设置波特率,会不成功。: write_752_reg(DLL, sc16is752->channel_num, 0x14); write_752_reg(DLH, sc16is752->channel_num, 0x00); 需要做一些初始化的步骤: write_752_reg(LCR, sc16is752->channel_num, 0x80); write_752_reg(DLL, sc16is752->channel_num, 0x14);//设置波特率。 write_752_reg(DLH, sc16is752->channel_num, 0x00); write_752_reg(LCR, sc16is752->channel_num, 0xbf); write_752_reg(EFR, sc16is752->channel_num, 0x10); write_752_reg(LCR, sc16is752->channel_num, 0x03); write_752_reg(IER, sc16is752->channel_num, 0x01); write_752_reg(FCR, sc16is752->channel_num, 0xf1); write_752_reg(SPR, sc16is752->channel_num, 0x41); write_752_reg(IODIR_752, sc16is752->channel_num, 0xff); write_752_reg(IOSTATE, sc16is752->channel_num, 0x00); 接下来分析write函数。 static ssize_t sc16is752_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { int missing ; struct sc16is752_dev *sc16is752 = filp->private_data; if(size > 64) return -EMSGSIZE; mutex_lock(&sc16is752->lock); missing = copy_from_user(sc16is752->buf, buf, size); write_channel(sc16is752->buf, size); mutex_unlock(&sc16is752->lock); return 0; } copy_from_user(sc16is752->buf, buf, size);将应用层的buf指针当中size空间的数据复制到sc16is752设备中的buf空间中。注意size是应用空间write传下来的。如:write(fd,”hello”, 6);把hello 6字节数据传到sc16is752->buf中,hello后面还一个‘\0’字符哦。 write_channel(sc16is752->buf, size);将hello字符,写到752中,打开write_channel函数: static void write_channel(unsigned char *buf, int len) { struct spi_transfer t[2]; struct spi_message m; unsigned char status; unsigned char cmd; cmd = 0x00 | (THR << 3) | (sc16is752->channel_num << 1); status = read_752_reg(LSR, sc16is752->channel_num); if (status & 0x20) { spi_message_init(&m); memset(t, 0, (sizeof t)); t[0].tx_buf = &cmd; t[0].len = 1; spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; t[1].len = len; spi_message_add_tail(&t[1], &m); spin_lock_irq(&sc16is752->spi_lock); spi_sync(sc16is752->spi, &m); spin_unlock_irq(&sc16is752->spi_lock); } return; } 以hello字符串分析,参数 buf指向sc16is752->buf空间,也就是存放hello字符串的内核空间,参数len就是应用空间传过来的6。函数开始读取752的LSR寄存器,判断发送FIFO是否为空,如果为空,就开始构造spi_message,根据752 spi时序图可以知道,需要先写一个字节,告诉从设备我主设备是想读还是想写,想操作那个寄存器,想操作哪个通道。 cmd = 0x00 | (THR << 3) | (sc16is752->channel_num << 1);表示写,操作THR寄存器,操作的通道由ioctl设置,未设置的情况下是channelA通道。 t[0].tx_buf存放的是操作码,操作码长度为1个字节,t[1].tx_buf存放的是sc16is752->buf空间“hello”字符串,长度就是6。构造好了spi_message之后,就通过spicore层函数spi_sync(sc16is752->spi, &m);将数据发送出去。 Spi_message中有一个actual_length成员变量,actual_length成员变量表示spi实际发送的字节数,也就是t[0].len+t[1].len = len的值,也就是7. 接下来分析sc16is752_read函数: static ssize_t sc16is752_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { struct sc16is752_dev *sc16is752 = filp->private_data; int length = 0; int missing; //wait_event(sc16is752->sc16is752_q,sc16is752->condition);//用此函数会导致进程杀不死。 wait_event_interruptible(sc16is752->sc16is752_q,sc16is752->condition); mutex_lock(&sc16is752->lock); length = read_channel(size); missing = copy_to_user(buf, sc16is752->buf, length); if (missing == length) length = -EFAULT; else length = length - missing; mutex_unlock(&sc16is752->lock); sc16is752->condition = 0; return length; } 开始时调用wait_event_interruptible,当spi接收的数据位空时,wait_event_interruptible让应用程序进入等待队列中休眠,最好不要用wait_event,因为wait_event是不可中断的休眠。 length = read_channel(size);将读取的数据放到sc16is752->buf空间,然后通过copy_to_user,将数据传给应用空间。注意这个当中的length变量,它是实际读取到spi字节数的长度,是需要非常精确的返回给read的,应用空间read成功之后返回的是读取到的字节数! 读取完毕之后,就将等待条件sc16is752->condition设置为0,这样下次读取数据的时候,sc16is752->buf空间为空时,再次让进程进入休眠状态。 Read_channel函数,非常纠结的函数!! static ssize_t read_channel(int len) { #if 0 struct spi_transfer t[2]; struct spi_message m; unsigned char cmd; cmd = 0x80 | (THR << 3) | (sc16is752->channel_num<< 1); printk(KERN_INFO "enter read_channel\n"); spi_message_init(&m); memset(t, 0, (sizeof t)); t[0].tx_buf = &cmd; t[0].len = 1; spi_message_add_tail(&t[0], &m); t[1].rx_buf = sc16is752->buf; t[1].len = len; spi_message_add_tail(&t[1], &m); spin_lock_irq(&sc16is752->spi_lock); spi_sync(sc16is752->spi, &m); spin_unlock_irq(&sc16is752->spi_lock); printk(KERN_INFO "m.actual_length = %d\n",m.actual_length); return m.actual_length; #endif unsigned int length = 0; unsigned char status = 0; do{ status = read_752_reg(LSR, sc16is752->channel_num); if(status & 0x01) { sc16is752->buf[length] = read_752_reg(RHR, sc16is752->channel_num); length ++; } }while(status & 0x01); return length; }

    当时想一次性把数据读取出来,这样效率快啊,可是,并不知道串口发了多少字节的数据来,所以也无法正确的把接收到的字节长度传送到应用空间。应用空间读取的数据就会是这样:

      多了很多乱码的,这就是因为一次性把数据读取出来,无法判断串口接收到数据的自己长度造成的。 #if 0 注释掉的就是一次性读取数据的代码。 后面的代码是一个一个字节读取,然后查询LSR寄存器的bit1位,直到FIFO全部读完,每读取一个自己数据,length就加一,这样就能知道串口接收到的数据长度了,并且返回给应用空间。虽然效率低一点,但是保证了驱动的正确性。 正确读取的效果:   补充:  出现如上的错误是write_channel函数中t[1].len没有初始化造成的,因为没有设置为应用空间传进来的长度,这里面是个垃圾值,spi_sync独占了资源导致内核崩溃。 通过串口更新内核方法,uboot下操作: loadb 0x22000000

    nand erase 0x200000 0x300000

    nand write 0x22000000 0x200000 0x300000

    附测试代码:

    #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #define SC752 "/dev/sc16is752" #define SET_channleA _IOW('k', 0, int) #define SET_channleB _IOW('k', 1, int) #define SET_STOP_BIT _IOW('k', 2, int) #define SET_BPS _IOW('k', 3, int) #define STOP_BIT_1 0x01 #define STOP_BIT_2 0x02 /**************************************************/ /* 说明:752可以设置1 1.5 2 位停止位,默认设置为1个停止位,字长度默认为8,且不可修改 */ /* 默认波特率设置为9600 */ /* 0-1个停止位(字长度=5,6,7,8) */ /* 1.5个停止位(字长度=5)*/ /* 2个停止位 */ /**************************************************/ int main(void) { char buf[64] = {0}; int length = 0; int i = 0; int fd = open(SC752, O_RDWR); if (fd < 0) { printf("Open %s failure.\n", SC752); return -1; } int rate; char c; while(1) { /*默认是通道A,波特率9600,停止位1位*/ printf("this is default test,stop bit is 1,Baud rate is 9600\n"); write(fd, "hello", 6); sleep(1); length = read(fd, buf, 30); printf("read length %d. read spi_uart is :%s\n",length, buf); memset(buf, 0, 64); sleep(1); } #if 0 /*设置通道测试*/ printf("then test set channel,please input a or b\n"); scanf("%c", &c); if (c == 'a') { ioctl(fd, SET_channleA); } else if(c == 'b') { ioctl(fd, SET_channleB); } write(fd, "hello", 6); sleep(1); length = read(fd, buf, 30); printf("read length %d. read spi_uart is :%s\n",length, buf); memset(buf, 0, 64); sleep(1); /*设置停止位测试*/ printf("then test set stop bits,please input 1 or 2"); scanf("%d", &c); if(c == 1) { ioctl(fd, SET_STOP_BIT, STOP_BIT_1); } else if(c == 2) { ioctl(fd, SET_STOP_BIT, STOP_BIT_2); } write(fd, "hello", 6); sleep(1); length = read(fd, buf, 30); printf("read length %d. read spi_uart is :%s\n",length, buf); memset(buf, 0, 64); sleep(1); while(1) { /*设置波特率测试,设置波特率之后,停止位变为默认的1位,*/ printf("then test set baud rate,please input 9600 19200 or 38400\n"); scanf("%d", &rate); ioctl(fd, SET_BPS, rate); write(fd, "hello", 6); sleep(1); length = read(fd, buf, 30); printf("read length %d\n",length); for(i = 0;i<length; i++) { printf("%d\t", buf[i]); } printf("\n"); memset(buf, 0, 64); sleep(1); } #endif close(fd); return 0; }

    转载请注明原文地址: https://ju.6miu.com/read-1300587.html
    最新回复(0)