1.TQ2440按键资源
TXD1 -- GPH4
RXD1 -- GPH5
2.内核介绍
本驱动基于linux-tq2440,也即天嵌基于TQ2440移植的内核
3.原理
基于字符设备驱动的一般方法,采用查询法实现串口驱动以及测试。
4.重要数据结构
-
struct cdev {
-
struct kobject kobj;
-
struct module *owner;
-
const struct file_operations *ops;
-
struct list_head list;
-
dev_t dev;
-
unsigned int count;
-
};
该结构用于描述一个字符设备。同时还有好多与之相关的函数(比如cdev_init用于初始化cdev结构等),具体查阅相关文档
5.驱动程序uart_mod.c
-
#include "linux/module.h"
-
#include "linux/kernel.h"
-
#include "asm/uaccess.h"
-
#include "linux/serial_core.h"
-
#include "asm/io.h"
-
#include "linux/cdev.h"
-
#include "plat/regs-serial.h"
-
-
#define BUFF_SIZE 100
-
#define UART_ULCON1 0x50004000
-
#define UART_UCON1 0x50004004
-
#define UART_UFCON1 0x50004008
-
#define UART_UTRSTAT1 0x50004010
-
#define UART_UTXH1 0x50004020
-
#define UART_URXH1 0x50004024
-
#define UART_UBRDIV1 0x50004028
-
-
#define GPHCON 0x56000070
-
#define GPHDAT 0x56000074
-
#define GPHUP 0x56000078
-
-
-
static int gMajorUart1 = 240;
-
module_param(gMajorUart1,int,0);
-
-
static void __iomem *gUlcon1;
-
static void __iomem *gUcon1;
-
static void __iomem *gUfcon1;
-
static void __iomem *gUtrstat1;
-
static void __iomem *gUtxh1;
-
-
static void __iomem *gUrxh1;
-
static void __iomem *gUbrdiv1;
-
-
static void __iomem *gGphcon;
-
static void __iomem *gGphup;
-
-
-
int tq2440_uart_open(struct inode *inode,struct file *filp){
-
uint32_t conDat;
-
int i;
-
//ioremap
-
gUlcon1 = ioremap(UART_ULCON1,0x04);
-
gUcon1 = ioremap(UART_UCON1,0x04);
-
gUfcon1 = ioremap(UART_UFCON1,0x04);
-
gUtrstat1 = ioremap(UART_UTRSTAT1,0x04);
-
gUtxh1 = ioremap(UART_UTXH1,0x04);
-
gUrxh1 = ioremap(UART_URXH1,0x04);
-
gUbrdiv1 = ioremap(UART_UBRDIV1,0x04);
-
-
gGphcon = ioremap(GPHCON,0x04);
-
gGphup = ioremap(GPHUP,0x04);
-
-
-
iowrite32(0x00faaa, gGphcon);
-
-
-
conDat = ioread32(gGphup);
-
conDat |= (3<<4);
-
iowrite32(conDat, gGphup);
-
-
-
iowrite32(0x51, gUfcon1);
-
//iowrite32(0x0, gUfcon1);
-
iowrite32(0x3, gUlcon1);
-
iowrite32(0x245,gUcon1);
-
iowrite32(19,gUbrdiv1); //115200bps
-
-
for(i=0;i<100;i++);
-
return 0;
-
}
-
-
ssize_t tq2440_uart_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops){
-
char wbuf[BUFF_SIZE] = {0};
-
int state;
-
int i;
-
-
-
i=0;
-
copy_from_user(wbuf,buf,count);
-
-
while(wbuf[i]){
-
state = ioread32(gUtrstat1);
-
//while(!(state & 0x2));
-
if((state & 0x2) == 2){
-
iowrite8(wbuf[i],gUtxh1);
-
//printk("send dat:%c.\n",wbuf[i]);
-
i++;
-
}
-
}
-
-
return count;
-
}
-
-
ssize_t tq2440_uart_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops){
-
char rbuf[BUFF_SIZE] = {0};
-
char tmpDat;
-
int i = 0;
-
int state;
-
-
printk("uart rcv start....\n");
-
-
while(1){
-
state = ioread32(gUtrstat1);
-
if((state & 0x1) == 1){
-
tmpDat=ioread8(gUrxh1);
-
-
// if you want to print info. you must enable fifo for uart,if not,you may lost some data.
-
//printk("uart rcv dat[%d]: %c-%02x\n",i,tmpDat,tmpDat);
-
-
if(i < 100){
-
rbuf[i++] = tmpDat;
-
if(tmpDat == '\r'){
-
printk("count = %d\n",i-1);
-
copy_to_user(buf,rbuf,i);
-
break;
-
}
-
}
-
else{
-
printk("count = %d\n",i-1);
-
copy_to_user(buf,rbuf,100);
-
break;
-
}
-
-
}
-
}
-
-
return i-1;
-
-
}
-
-
int tq2440_uart_release(struct inode *inode,struct file *filp){
-
return 0;
-
}
-
-
struct file_operations tty_fops =
-
{
-
.owner = THIS_MODULE,
-
.open = tq2440_uart_open,
-
.write = tq2440_uart_write,
-
.read = tq2440_uart_read,
-
.release = tq2440_uart_release,
-
};
-
-
static struct cdev gUartDev;
-
-
static void uart_setup_cdev(struct cdev *dev,int minor, struct file_operations *fops){
-
int err;
-
int devno = MKDEV(gMajorUart1,minor);
-
cdev_init(dev,fops);
-
dev->owner = THIS_MODULE;
-
dev->ops = fops;
-
err = cdev_add(dev,devno,1);
-
if(err)
-
printk("error %d adding UART_UTRSTAT1 %d\n",err,minor);
-
}
-
-
static int tq2440_tty_init(void){
-
int ret;
-
dev_t dev = MKDEV(gMajorUart1,0);
-
-
if(gMajorUart1){
-
ret = register_chrdev_region(dev,1,"Mytty1");
-
}
-
else{
-
ret = alloc_chrdev_region(&dev,0,1,"Mytty1");
-
gMajorUart1 = MAJOR(dev);
-
}
-
-
if(ret < 0){
-
printk("unable to get major %d \n",gMajorUart1);
-
return ret;
-
}
-
-
-
uart_setup_cdev(&gUartDev,0,&tty_fops);
-
-
printk("tq2440 tty module installed,with major %d.\n",gMajorUart1);
-
return 0;
-
}
-
-
static void tq2440_tty_cleanup(void){
-
iounmap(gUlcon1);
-
iounmap(gUcon1);
-
iounmap(gUfcon1);
-
iounmap(gUtrstat1);
-
iounmap(gUtxh1);
-
iounmap(gUrxh1);
-
iounmap(gUbrdiv1);
-
-
iounmap(gGphcon);
-
iounmap(gGphup);
-
-
-
cdev_del(&gUartDev);
-
unregister_chrdev_region(MKDEV(gMajorUart1,0),1);
-
printk("tq2440 tty module uninstalled\n");
-
}
-
-
module_init(tq2440_tty_init);
-
module_exit(tq2440_tty_cleanup);
-
-
MODULE_AUTHOR("frankey");
-
MODULE_LICENSE("Dual BSD/GPL");
主要是实现cdev结构的fops函数,同时使用remap进行寄存器访问。串口都是采用查询法收发数据。
6.测试程序
-
#include "stdio.h"
-
#include "string.h"
-
#include "fcntl.h"
-
-
#define BUFF_SIZE 100
-
-
int main(int argc,char **argv){
-
int fd,ret;
-
-
char buff[BUFF_SIZE];
-
char *msg = "my tty1 test.\n";
-
fd = open("/dev/mytty1",O_RDWR);
-
if(fd<0){
-
printf("Error:open /dev/Mytty1 error!\n");
-
return 1;
-
}
-
-
printf("open file success. ready for reading...\n");
-
-
ret = write(fd,msg,strlen(msg));
-
if(ret<0){
-
printf("Error:write msg error\n");
-
return 1;
-
}
-
printf("write tty ok.\n");
-
-
memset(buff,0,sizeof(buff));
-
ret = read(fd,buff,BUFF_SIZE);
-
if(fd<0){
-
printf("Error:readread device error\n");
-
return 1;
-
}
-
printf("read device ok.[%d]\n",ret);
-
buff[ret+1] = 0;
-
if(buff[0]){
-
printf("Rcv:%s\n",buff);
-
}
-
else{
-
printf("Rcv:no data\n");
-
}
-
-
return 0;
-
-
}
测试程序采用阻塞方式操作设备文件。
7.测试结果
(1)加载模块并创建设备文件
(2)运行测试程序,交互
运行测试程序后,测试程序发送“my tty test”给pc电脑。电脑通过串口工具发送“hello world”字串给开发板,同时字串结尾发送'\r',表明字串结束。从上图可以看出,开发板通过串口接收到了正确的字串。
8.注意
由于本驱动采用的是查询法实现数据接收的,故最开始我在read实现这里,为了进行调试,增加了printk打印函数,导致每次接收的数据都是不完整的,存在很严重的丢数据现场,进过多次排查。重要定位到了问题,我的初始化里头,初始化串口字符设备的时候,使用的是
iowrite32
(0x0
, gUfcon1
);禁止了fifo功能,所以在接收数据的时候,又调用了printk函数,导致数据接收不正常,在此次去掉打印函数就可以正常,同时另一个方法,要想实现打印调试,可以使用
iowrite32
(0x53
, gUfcon1
);来使能串口的硬件FIFO功能,就可以在此处做打印调试了。
同时串口初始化波特率也是最开始的时候出现了问题,数据接收不对。
iowrite32(19,gUbrdiv1); //115200bps
主要是这句话不对,这个波特率的设置要根据具体的内核初始化之后的pclk的时钟来计算ubrdiv寄存器的值。最后经过源码分析,内核初始化pclk的结果为37.5MHz,故在115200bps下,ubrdiv寄存器赋值应该为19.
阅读(4799) | 评论(0) | 转发(0) |