ARM Linux IO模拟红外串口驱动实现

文章目录

  • 红外串口示意图
  • IO模拟红外串口设计思路
    • 接收功能实现
    • 发射功能实现
  • IO模拟红外串口驱动代码
    • 数据结构及相关宏定义
    • 驱动资源以及变量信息
    • 驱动的入口与出口
    • iouart_probe驱动探测函数
    • iouart_remove驱动移除函数
    • iouart_ops文件类操作函数
  • IO模拟红外串口功能代码
    • 硬件初始化配置
    • 发送与接收启动
    • 发送与接收定时器中断

当我们用的ARM芯片UART串口数量不足时,有时需要使用IO口来模拟实现红外串口的发送与接收,关于串口的时序以及工作特点,这篇文章不做介绍,本篇文章主要描述如何实现IO口模拟实现串口功能。
硬件平台:ATMEL SAM9G25
kernel版本:Linux2.6.39

红外串口示意图

以下是红外串口的电路图,有图可知,要想用ATMEL芯片的IO口模拟实现串口功能,需要以下4个条件:
1、38K调制方波
2、RXD接收引脚
3、TXD发射引脚
4、定时器,用于bit位计时
ARM Linux IO模拟红外串口驱动实现_第1张图片

IO模拟红外串口设计思路

串口时序参照上图所示。这里我们以红外串口通信波特率为1200bps为例,进行实现。
波特率为1200bps时,每个比特位的占用的时间为833us

接收功能实现

  • 1、RXD引脚必须作为IRQ中断引脚,当红外接收头接收到起始信号(下降沿)时,产生中断,进入接收状态,此时可以关闭RXD引脚的中断功能
  • 2、在RXD引脚中断服务程序中开启一个定时器,从第一个下降沿产生的中断开始计时,时长为416us,为什么是416us呢,这个时长大约是1个bit位时长的一半,这样在416us计时完毕时,产生一个计时中断,我可以再次检测RXD引脚是否为低电平,我这样做是为了检测一个确切的起始信号,滤除一些干扰引起的下降沿中断;
  • 3、第一次计时完毕时,正好处于start位的中间,这样以后检测bit0—bit7以及parity、stop数据位时,正好计时1个bit位对应的833us,检测每个数据位都处于bit位的中间位置。
  • 4、接收其它位时,每计时833us,产生一个计时中断,根据接收位计数,按照计时中断连续推进,直到接收到停止位,停止位接收完毕之后可以判断parity位于计算出的校验位是否匹配。
  • 5、1个字节的接收完毕之后,再次开启RXD引脚的下降沿中断,等待中断接收下一个字节;进入步骤1

发射功能实现

  • 1、首先要配置一个定时器,通过一个PWM口输出38KHz方波信号,可以在发送数据前打开,发送完毕后关闭。
  • 2、进入发送状态后,开启一个bit位计时器,计时时长为833us,即1个bit位的时长,先发送start位,低电平;然后根据待发送字节的bit位,按照bit0—bit7 的顺序发送,bit位为0时输出低电平,否则输出高电平
  • 3、根据计时器中断,以及发送bit位计数连续推进,直到发送完所有的数据位,根据计算出的parity位,输出相应的电平信号
  • 4、输出parity校验位后,最后一个计时输出stop位,高电平;计时完毕时,即发送完1个字节,若还有剩余字节,则回到步骤2,发送剩余的字节;
  • 5、所有字节发送完毕后退出发送状态,关闭38KHz方波信号。

IO模拟红外串口驱动代码

驱动代码实现采用platform_device与platform_driver那一套,字符设备驱动框架。

数据结构及相关宏定义

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "at91sam9g25_pwm.h"

#define AT91_PWM_MR					0x00	/*Mode Register*/
#define PWM_CLK_DIVA(n)				(n)
#define	PWM_PREA(n)					(n << 8)
#define AT91_PWM_ENA				0x04	/*Enable Register*/
#define AT91_PWM_DIS				0x08	/*Disable Register*/
#define AT91_PWM_SR					0x0C	/*Status Register*/
#define AT91_PWM_IER				0x10	/*Interrupt Enable Register*/
#define AT91_PWM_IDR				0x14	/*Interrupt Disable Register*/
#define AT91_PWM_IMR				0x18	/*Interrupt Mask Register*/
#define AT91_PWM_ISR				0x1C	/*Interrupt Status Register*/
#define AT91_PWM_CMR(ch_num)		(0x200 + ch_num * 0x20 + 0x00)	/*Channel Mode Register*/
#define PWM_CPRE_MCKDIV1			0x00
#define PWM_CALG_LEFT				0x00
#define	PWM_CPOL_HIGH				(1 << 9)
#define PWM_CPD						(1 << 10)
#define AT91_PWM_CDTY(ch_num)		(0x200 + ch_num * 0x20 + 0x04)	/*Channel Duty Cycle Register*/
#define AT91_PWM_CPRD(ch_num)		(0x200 + ch_num * 0x20 + 0x08)	/*Channel Period Register*/
#define AT91_PWM_CCNT(ch_num)		(0x200 + ch_num * 0x20 + 0x0C)	/*Channel Counter Register*/
#define AT91_PWM_CUPD(ch_num)		(0x200 + ch_num * 0x20 + 0x10)	/*Channel Update Register*/

/*错误类型*/
#define DRV_NO_ERROR				(0)			//正常
#define DRV_ERROR_DEV				(-1)		//设备打开失败
#define DRV_ERROR_INPUT_ARGS		(-2)		//输入参数错误
#define DRV_ERROR_OPT_FAILED		(-3)		//操作失败
#define DRV_ERROR_OPT_NOT_OVER 		(-4)		//操作未结束
#define DRV_ERROR_DEV_BUSY			(-5)		//设备忙

/*操作类型*/
#define DRV_OPTTYPE_SET				(0x1000)		//设置
#define DRV_OPTTYPE_GET				(0x1100)		//读取
#define DRV_OPTTYPE_INIT			(0x1200)		//初始化

/*常用类型*/
#define DRV_TRUE		1
#define DRV_FALSE		0

/*设备状态定义*/
#define DRV_STATUS_IDLE		0x10
#define DRV_STATUS_BUSY		0x11
#define DRV_STATUS_TX		0x12
#define DRV_STATUS_RX		0x13

/*其他*/
#ifndef NULL
#define NULL	((void*)0)
struct iouart_platform_data
{
	u32 tx_pin;
	u32 rx_pin;
	u32 tx_pin_base;
	u32 rx_pin_base;
	u32 tx_pin_port;
	u32 rx_pin_port;

	u32 rx_pin_irq;
	u32 tc_irq;

	u32 flags;
	u32 pwm_pin;
	u32 pwm_port;

	void __iomem *tc_base;
	void __iomem *pwm_base;
	struct resource *tc_res;
	struct resource *pwm_res;
};

#define IOUART_BUF_LEN		(4*1024)	//发送、接收缓冲区最大长度
struct ring_t
{
	unsigned char buf[IOUART_BUF_LEN];
	unsigned int index;		//对于rx来说,index表示待读取数据首地址索引,对于tx来说,表示待发送数据首地址索引
	unsigned int len;		//对于rx来说,len表示当前未读取的数据长度,对于tx来说,表示未发送的数据长度
	unsigned char bitStep;
	unsigned char parity;
	unsigned char byte;
};
struct iouart_dev_t
{
	struct ring_t rx;					//接收缓冲区
	struct ring_t tx;					//发送缓冲区
	spinlock_t lock;					//自旋锁
	wait_queue_head_t tx_waitqueue;		//发送等待队列
	int trans_status;					//通讯状态
	struct platform_device *pdev;
};

驱动资源以及变量信息

/*****************irda设备定义****************/
static struct resource irda_uart_resource[] =
{
	[0] =
	{
		.start = AT91SAM9X5_BASE_TC3,
		.end = AT91SAM9X5_BASE_TC3 + 0x40 - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] =
	{
		.start = AT91SAM9X5_BASE_PWMC,
		.end = AT91SAM9X5_BASE_PWMC + SZ_16K - 1,
		.flags = IORESOURCE_MEM,
	},
};
static struct iouart_platform_data irda_uart_data =
{
		.tx_pin = AT91_PIN_PC2,
		.rx_pin = AT91_PIN_PB13,
		.tx_pin_base = AT91_PIN_PC0,
		.rx_pin_base = AT91_PIN_PB0,
		.tx_pin_port = AT91_PIOC,
		.rx_pin_port = AT91_PIOB,

		.flags = TX_ENABLE | RX_ENABLE | PWM_ENABLE ,//| TX_PIN_OPENDRAIN,
		.pwm_pin = AT91_PIN_PB18,
		.pwm_port = PWM1,

		.rx_pin_irq = AT91SAM9X5_ID_PIOAB,
		.tc_irq = AT91SAM9X5_ID_TCB,

};
/****************平台设备与驱动************************/
static struct platform_device iouart_device[] =
{
	[0] =
		{
			.name 		= 		"irda",
			 .id 		= 		0,
			 .dev 		=
			 {
					 .platform_data = &irda_uart_data,
					 .release = iouart_release,
			 },
			 .resource	= irda_uart_resource,
			 .num_resources	= ARRAY_SIZE(irda_uart_resource),
		},
};

static struct platform_driver iouart_driver[] =
{
	 [0] =
		{
			.probe	=	iouart_probe,
			.remove	=	iouart_remove,
			.driver	=
			{
			  .name	= 	"irda",
			  .owner=	THIS_MODULE,
			},
		},
};

static struct file_operations iouart_ops =
{
	 .owner		=		THIS_MODULE,
	 .open		=		iouart_open,
	 .release	=		iouart_close,
	 .read		=		iouart_read,
	 .write		=		iouart_write,
};

static int major = 0;						//主设备号
static struct class *iouart_class;			//class
static long l_rxIrqIsr[4] = {0,0,0,0};		//针对rx pin中断的isr保留值?

驱动的入口与出口

static int __init iouart_init(void)
{
	int ret = 0,m,n,k;
	/*注册字符设备*/
	ret = register_chrdev(major,DEV_NAME,&iouart_ops);
	if(ret < 0)
	{
		printk(KERN_INFO "iouart_init: register char dev failed !\n");
		return -1;
	}

	/*获取动态分配主设备号*/
	if(major == 0)
	{
		major = ret;
	}

	/*注册dev class*/
	iouart_class = class_create(THIS_MODULE,DEV_NAME);
	if(IS_ERR(iouart_class))
	{
		printk(KERN_INFO "iouart_init: class create failed !\n");
		goto fail0;
	}

	/*注册platform_device*/
	for(m = 0; m < ARRAY_SIZE(iouart_device); m++)
	{
		ret = platform_device_register(&iouart_device[m]);
		if(ret < 0)
		{
			printk(KERN_INFO "iouart_init: platform register device failed !\n");
			goto fail1;
		}
	}

	/*注册platform driver*/
	for(n = 0; n < ARRAY_SIZE(iouart_driver); n++)
	{
		ret = platform_driver_register(&iouart_driver[n]);
		if(ret < 0)
		{
			printk(KERN_INFO "iouart_init: platform register driver failed !\n");
			goto fail2;
		}
	}
	return 0;
fail2:
	for(k = 0; k < n; k++)
	{
		platform_driver_unregister(&iouart_driver[k]);
	}
fail1:
	for(k = 0; k < m; k++)
	{
		platform_device_unregister(&iouart_device[k]);
	}
	class_destroy(iouart_class);
fail0:
	unregister_chrdev(major,DEV_NAME);
	return -1;
}
static void __exit iouart_exit(void)
{
	int i;
	for(i = 0; i < ARRAY_SIZE(iouart_driver); i++)
	{
		platform_driver_unregister(&iouart_driver[i]);
	}
	for(i = 0; i < ARRAY_SIZE(iouart_device); i++)
	{
		platform_device_unregister(&iouart_device[i]);
	}
	class_destroy(iouart_class);
	unregister_chrdev(major,DEV_NAME);
}
module_init(iouart_init);
module_exit(iouart_exit);
MODULE_AUTHOR("luke.zhao");
MODULE_LICENSE("Dual BSD/GPL");

iouart_probe驱动探测函数

struct platform_device *get_pdev_by_iminor(unsigned int minor)
{
	return &iouart_device[minor];
}

static int iouart_probe(struct platform_device *pdev)
{
	struct device *dev;
	struct iouart_platform_data *pdata;
	dev_t dev_no;
	int ret = 0;
	pdata = pdev->dev.platform_data;
	/*获取资源及分配*/
	pdata->tc_res = platform_get_resource(pdev,IORESOURCE_MEM,0);
	if(!pdata->tc_res)
	{
		printk(KERN_INFO "iouart_probe : platform_get_resource : tc_res failed !\n");
		return -ENXIO;
	}

	if(!request_mem_region(pdata->tc_res->start,resource_size(pdata->tc_res),pdev->name))
	{
		printk(KERN_INFO "iouart_probe : request_mem_region : tc_res failed !\n");
		return -EBUSY;
	}

	pdata->tc_base = ioremap(pdata->tc_res->start,resource_size(pdata->tc_res));
	if(!pdata->tc_base)
	{
		printk(KERN_INFO "iouart_probe : tc_base ioremap :tc_res failed !\n");
		ret = -ENOMEM;
		goto fail0;
	}

	if((pdata->flags & PWM_ENABLE) > 0)
	{
		pdata->pwm_res = platform_get_resource(pdev,IORESOURCE_MEM,1);
		if(!pdata->pwm_res)
		{
			printk(KERN_INFO "iouart_probe : platform_get_resource : pwm_res failed !\n");
			ret = -ENXIO;
			goto fail0;
		}

		if(!request_mem_region(pdata->pwm_res->start,resource_size(pdata->pwm_res),pdev->name))
		{
			printk(KERN_INFO "iouart_probe : request_mem_region : pwm_res failed !\n");
			ret =  -EBUSY;
			goto fail0;
		}

		pdata->pwm_base = ioremap(pdata->pwm_res->start,resource_size(pdata->pwm_res));
		if(!pdata->pwm_base)
		{
			printk(KERN_INFO "iouart_probe : pwm_base ioremap : pwm_res failed !\n");
			ret = -EACCES;
			goto fail1;
		}
	}

	/*硬件设备初始化*/
	iouart_hw_enable(pdata);
	/*创建dev*/
	dev_no = MKDEV(major,pdev->id);
	dev = device_create(iouart_class,&pdev->dev,dev_no,NULL,pdev->name);
	if(IS_ERR(dev))
	{
		printk(KERN_INFO "iouart_probe: device create failed !\n");
		ret = -EACCES;
		goto fail2;
	}
	return 0;
fail2:
	iounmap(pdata->tc_base);
	if((pdata->flags & PWM_ENABLE) > 0)
	{
		iounmap(pdata->pwm_base);
	}
fail1:
	if((pdata->flags & PWM_ENABLE) > 0)
	{
		release_mem_region(pdata->pwm_res->start,resource_size(pdata->pwm_res));
	}
fail0:
	release_mem_region(pdata->tc_res->start,resource_size(pdata->tc_res));
	return ret;
}

iouart_remove驱动移除函数

static int iouart_remove(struct platform_device *pdev)
{
	struct iouart_platform_data *pdata;
	dev_t dev_no;
	pdata = pdev->dev.platform_data;
//	printk(KERN_INFO "iouart_remove start !\n");
	/*注销设备*/
	dev_no = MKDEV(major,pdev->id);
	device_destroy(iouart_class,dev_no);
	/*硬件禁止*/
	iouart_hw_disable(pdata);
	/*释放资源*/
	iounmap(pdata->tc_base);
	if((pdata->flags & PWM_ENABLE) > 0)
	{
		iounmap(pdata->pwm_base);
	}
	if((pdata->flags & PWM_ENABLE) > 0)
	{
		release_mem_region(pdata->pwm_res->start,resource_size(pdata->pwm_res));
	}
	release_mem_region(pdata->tc_res->start,resource_size(pdata->tc_res));
//	printk(KERN_INFO "iouart_remove end !\n");
	return 0;
}

iouart_ops文件类操作函数

  • iouart_open函数
static int iouart_open(struct inode *inode, struct file *filp)
{
	int ret;
	long rxIrqIsr;
	unsigned int minor = iminor(inode);
	struct iouart_dev_t *iouart_dev;
	struct platform_device *pdev;
	struct iouart_platform_data *pdata;

	pdev = get_pdev_by_iminor(minor);
	if(pdev == NULL)
	{
		printk(KERN_INFO "iouart_open : get_pdev_by_iminor failed !\n");
		return -EACCES;
	}

	/*分配esam dev数据结构体内存*/
	iouart_dev = kmalloc(sizeof(struct iouart_dev_t),GFP_KERNEL);
	if(!iouart_dev)
	{
		printk(KERN_INFO "iouart_open : kmalloc iouart_dev failed !\n");
		return -ENOMEM;
	}

	/*初始化esam dev数据结构体*/
	memset(&iouart_dev->rx,0,sizeof(struct ring_t));
	memset(&iouart_dev->tx,0,sizeof(struct ring_t));

	iouart_dev->pdev = pdev;
	iouart_dev->trans_status = DRV_STATUS_RX;
	pdata = iouart_dev->pdev->dev.platform_data;

	spin_lock_init(&iouart_dev->lock);
	init_waitqueue_head(&iouart_dev->tx_waitqueue);

	/*申请中断*/
	ret = request_irq(pdata->tc_irq,iouart_func_timer_interrupt,IRQF_SHARED,iouart_dev->pdev->name,iouart_dev);
	if(ret < 0)
	{
		printk(KERN_INFO "iouart_open : request_irq_timer failed ! ret = %d\n",ret);
		kfree(iouart_dev);
		return ret;
	}
	ret = request_irq(pdata->rx_pin_irq,iouart_func_rxio_interrupt,IRQF_SHARED,iouart_dev->pdev->name,iouart_dev);
	if(ret < 0)
	{
		printk(KERN_INFO "iouart_open : request_irq_rxio failed ! ret = %d\n",ret);
		free_irq(pdata->tc_irq,iouart_dev);
		kfree(iouart_dev);
		return ret;
	}

	/*存储iouart_dev结构体到private_data*/
	filp->private_data = iouart_dev;

	/*清中断标志*/
	rxIrqIsr = get_rxirq_isr(pdata->rx_pin_port);
	rxIrqIsr &=~(1 << (pdata->rx_pin - pdata->rx_pin_base));
	set_rxirq_isr(pdata->rx_pin_port,rxIrqIsr);

	/*触发接收中断*/
	at91_sys_write((pdata->rx_pin_port+PIO_IER),(1 << (pdata->rx_pin - pdata->rx_pin_base)));
	return 0;
}
  • iouart_close函数
static int iouart_close(struct inode *inode, struct file *filp)
{
	struct iouart_dev_t *iouart_dev = (struct iouart_dev_t*)(filp->private_data);
	struct iouart_platform_data *pdata;
	pdata = iouart_dev->pdev->dev.platform_data;
	free_irq(pdata->tc_irq,iouart_dev);
	free_irq(pdata->rx_pin_irq,iouart_dev);
	/*禁止接收中断*/
	at91_sys_write((pdata->rx_pin_port+PIO_IDR),(1 << (pdata->rx_pin - pdata->rx_pin_base)));
	kfree(iouart_dev);
	return 0;
}
  • iouart_read函数
static ssize_t iouart_read(struct file *filp, char __user *buf, size_t len, loff_t *loffp)
{
	int cpyLen,i;
	struct iouart_dev_t *iouart_dev = (struct iouart_dev_t*)filp->private_data;

	/*计算读取长度*/
	cpyLen = (iouart_dev->rx.len > len) ? len : iouart_dev->rx.len;

	/*从接收缓冲区中读取数据到buf*/
	for(i = 0; i < cpyLen; i++)
	{
		put_user(iouart_dev->rx.buf[(iouart_dev->rx.index + i) % IOUART_BUF_LEN],&buf[i]);
	}

	/*重新计算接收缓冲区参数*/
	spin_lock_irq(&iouart_dev->lock);
	iouart_dev->rx.index += cpyLen;
	iouart_dev->rx.index %= IOUART_BUF_LEN;
	iouart_dev->rx.len -= cpyLen;
	spin_unlock_irq(&iouart_dev->lock);
	return cpyLen;
}
  • iouart_write函数
static ssize_t iouart_write(struct file *filp, const char __user *buf, size_t len, loff_t *loffp)
{
	int validLen,i,currentLen,currentIndex,currentMaxBufLen;
	struct iouart_dev_t *iouart_dev = (struct iouart_dev_t*)filp->private_data;

	/*计算当前能写的字节长度*/
	currentLen = iouart_dev->tx.len;
	currentIndex = iouart_dev->tx.index;
	currentMaxBufLen = IOUART_BUF_LEN;

	validLen = currentMaxBufLen - currentLen;
	if(len > validLen)
		goto exit0;
	/*循环将buf中的数据写入发送缓冲区*/
	for(i = 0; i < len; i++)
	{
		get_user(iouart_dev->tx.buf[(iouart_dev->tx.index + iouart_dev->tx.len+i)% IOUART_BUF_LEN],&buf[i]);
	}

	/*重新计算发送缓冲区的参数*/
	spin_lock_irq(&iouart_dev->lock);
	iouart_dev->tx.len += len;
	spin_unlock_irq(&iouart_dev->lock);

	if(iouart_dev->tx.len == 0)
		goto exit0;
	/*启动发送*/
	if(iouart_dev->trans_status == DRV_STATUS_RX)
	{
		iouart_start_send(iouart_dev);
	}
	/*等待数据发送完成*/
//	interruptible_sleep_on(&iouart_dev->tx_waitqueue);
	return len;
exit0:
	return 0;
}

IO模拟红外串口功能代码

这里的代码主要是实现IO模拟串口收发数据的过程,主要是硬件初始化、中断、定时器等。

硬件初始化配置

static void iouart_hw_enable(struct iouart_platform_data *pdata)
{
	/*PWM使能*/
	if((pdata->flags & PWM_ENABLE) > 0)
	{
		at91_sys_write(AT91_PMC_PCER, 1 << 18);		//使能PWM的CLOCK
		at91_set_C_periph(pdata->pwm_pin,1);		//配置PWM1
		at91_pwm_write(pdata->pwm_base,AT91_PWM_CMR(pdata->pwm_port),PWM_CPRE_MCKDIV1 | PWM_CALG_LEFT);	//MCK=100M
		at91_pwm_write(pdata->pwm_base,AT91_PWM_CDTY(pdata->pwm_port),1316);
		at91_pwm_write(pdata->pwm_base,AT91_PWM_CPRD(pdata->pwm_port),2631);	//period = MCK / 2603 = 38K
		at91_pwm_write(pdata->pwm_base,AT91_PWM_ENA,(1 << pdata->pwm_port));	//使能PWM1
	}

	/*IO管脚配置*/
	if((pdata->flags & TX_ENABLE) > 0)
	{
		if((pdata->flags & TX_PIN_OPENDRAIN) > 0)
			at91_set_multi_drive(pdata->tx_pin,((pdata->flags & TX_PIN_OPENDRAIN) > 0 ? 1 : 0));
		at91_set_gpio_output(pdata->tx_pin,1);		//TX pin
	}

	if((pdata->flags & RX_ENABLE) > 0)
	{
		at91_set_gpio_input(pdata->rx_pin,1);		//RX pin
		at91_set_deglitch(pdata->rx_pin,1);
	}
	at91_sys_write((pdata->rx_pin_port+PIO_AIMER),(1 << (pdata->rx_pin-pdata->rx_pin_base)));
	at91_sys_write((pdata->rx_pin_port+PIO_FELLSR),(1 << (pdata->rx_pin-pdata->rx_pin_base)));	//data pin下降沿触发中断
	at91_sys_write((pdata->rx_pin_port+PIO_IDR),(1 << (pdata->rx_pin-pdata->rx_pin_base)));		//禁止io中断
	/*enable transfer timer*/
	at91_sys_write(AT91_PMC_PCER, 1 << 17);		//使能TC的CLOCK
	at91_tc_write(pdata->tc_base,AT91_TC_CMR,AT91_TC_TIMER_CLOCK3 | AT91_TC_CPCSTOP | \
				   AT91_TC_WAVE | AT91_TC_WAVESEL_UP );		// MCK/32= 100/32 = 3.125MHZ
	at91_tc_write(pdata->tc_base,AT91_TC_IER,AT91_TC_CPCS);	//中断使能
	at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_CLKEN);	//定时器使能
}

static void iouart_hw_disable(struct iouart_platform_data *pdata)
{
	if((pdata->flags & PWM_ENABLE) > 0)
	{
		/*disable pwm clk*/
		at91_pwm_write(pdata->pwm_base,AT91_PWM_DIS,(1 << pdata->pwm_port));	//禁止PWM
	}
	/*disable esam rxio*/
	at91_sys_write((pdata->rx_pin_port+PIO_IDR),(1 << (pdata->rx_pin-pdata->rx_pin_base)));		//接收中断禁止
	/*disable transfer timer*/
	at91_tc_write(pdata->tc_base,AT91_TC_IDR,AT91_TC_CPCS);	//中断禁止
	at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_CLKDIS);	//定时器禁止
}

static unsigned int get_rxirq_isr(int pin_port)
{
	unsigned int retValue;

	retValue = at91_sys_read(pin_port+PIO_ISR);
	retValue &= at91_sys_read(pin_port+PIO_IMR);
	switch(pin_port)
	{
		case AT91_PIOA:
		{
			l_rxIrqIsr[0] |= retValue;
			return l_rxIrqIsr[0];
		}
		case AT91_PIOB:
		{
			l_rxIrqIsr[1] |= retValue;
			return l_rxIrqIsr[1];
		}
		case AT91_PIOC:
		{
			l_rxIrqIsr[2] |= retValue;
			return l_rxIrqIsr[2];
		}
		case AT91_PIOD:
		{
			l_rxIrqIsr[3] |= retValue;
			return l_rxIrqIsr[3];
		}
		default:
		{
			break;
		}
	}
	return 0;
}

static void set_rxirq_isr(int pin_port,long rxIrqIsr)
{
	switch(pin_port)
	{
		case AT91_PIOA:
		{
			l_rxIrqIsr[0] = rxIrqIsr;
			break;
		}
		case AT91_PIOB:
		{
			l_rxIrqIsr[1] = rxIrqIsr;
			break;
		}
		case AT91_PIOC:
		{
			l_rxIrqIsr[2] = rxIrqIsr;
			break;
		}
		case AT91_PIOD:
		{
			l_rxIrqIsr[3] = rxIrqIsr;
			break;
		}
		default:
		{
			break;
		}
	}
}

发送与接收启动

static void iouart_start_send(struct iouart_dev_t *dev)
{
	struct iouart_dev_t *iouart_dev = dev;
	struct iouart_platform_data *pdata = iouart_dev->pdev->dev.platform_data;

	/*禁止IO中断*/
	at91_sys_write((pdata->rx_pin_port+PIO_IDR),(1 << (pdata->rx_pin-pdata->rx_pin_base)));;

	spin_lock_irq(&iouart_dev->lock);
	iouart_dev->trans_status = DRV_STATUS_TX;
	iouart_dev->tx.byte = iouart_dev->tx.buf[iouart_dev->tx.index];

	iouart_dev->tx.index++;
	iouart_dev->tx.index %= IOUART_BUF_LEN;
	iouart_dev->tx.len--;

	iouart_dev->tx.bitStep = 0;
	iouart_dev->tx.parity = 0;
	spin_unlock_irq(&iouart_dev->lock);

	/*启动发送*/
	at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
	at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
}

//注意,此处由于是io中断,所有的中断号共享一个isr,所以所有的rx_pin中断有可能全在这里处理了。
static irqreturn_t iouart_func_rxio_interrupt(int irq, void *dev_id)
{
	unsigned int rxIrqIsr;
	struct iouart_dev_t *iouart_dev = (struct iouart_dev_t *)dev_id;
	struct iouart_platform_data *pdata = iouart_dev->pdev->dev.platform_data;

	/*读取中断标志*/
	rxIrqIsr = get_rxirq_isr(pdata->rx_pin_port);
	if((rxIrqIsr & (1 << (pdata->rx_pin - pdata->rx_pin_base))) > 0)
	{
		rxIrqIsr &=~(1 << (pdata->rx_pin - pdata->rx_pin_base));
		if(iouart_dev->trans_status == DRV_STATUS_RX)
		{
			/*禁止IO中断*/
			at91_sys_write((pdata->rx_pin_port+PIO_IDR),(1 << (pdata->rx_pin-pdata->rx_pin_base)));		//禁止io中断
			/*设置检查状态*/
			iouart_dev->rx.bitStep = 0x00;
			iouart_dev->tx.parity = 0;
			iouart_dev->rx.byte = 0x00;

			/*开启检查起始位的定时器*/
			at91_tc_read(pdata->tc_base,AT91_TC_SR);
			at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT/2);
			at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
		}
	}
	/*保存中断值,共下一个rx中断查询使用*/
	set_rxirq_isr(pdata->rx_pin_port,rxIrqIsr);
	return IRQ_NONE;
}

发送与接收定时器中断

static irqreturn_t iouart_func_timer_interrupt(int irq, void *dev_id)
{
	long retval;
	struct iouart_dev_t *iouart_dev = (struct iouart_dev_t *)dev_id;
	struct iouart_platform_data *pdata = iouart_dev->pdev->dev.platform_data;

	/*读取中断状态*/
	retval = at91_tc_read(pdata->tc_base,AT91_TC_SR);
	retval &= at91_tc_read(pdata->tc_base,AT91_TC_IMR);

	if((retval & AT91_TC_CPCS) > 0)
	{
		if(iouart_dev->trans_status == DRV_STATUS_RX)
		{
			iouart_timer_rx(iouart_dev);
		}
		else if(iouart_dev->trans_status == DRV_STATUS_TX)
		{
			iouart_timer_tx(iouart_dev);
		}
		else
		{
			printk(KERN_INFO "iouart_func_timer_interrupt : wrong status !\n");
		}
	}
	return IRQ_NONE;
}

void iouart_timer_tx(struct iouart_dev_t *iouart_dev)
{
	unsigned int rxIrqIsr;
	struct iouart_platform_data *pdata = iouart_dev->pdev->dev.platform_data;
	switch(iouart_dev->tx.bitStep)
	{
		case 0:		//发送起始位
		{
			at91_set_gpio_value(pdata->tx_pin,0);
			iouart_dev->tx.bitStep++;
			/*timer start*/
			at91_tc_read(pdata->tc_base,AT91_TC_SR);
			at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
			at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			break;
		}
		case 9:		//发送校验位,本口是固定偶校验
		{
			if(iouart_dev->tx.parity == 0)
			{
				at91_set_gpio_value(pdata->tx_pin,0);
			}
			else
			{
				at91_set_gpio_value(pdata->tx_pin,1);
			}
			iouart_dev->tx.bitStep++;
			/*timer start*/
			at91_tc_read(pdata->tc_base,AT91_TC_SR);
			at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
			at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			break;
		}
		case 10:	//发送固定1位停止位
		{
			at91_set_gpio_value(pdata->tx_pin,1);
			iouart_dev->tx.bitStep++;
			/*timer start*/
			at91_tc_read(pdata->tc_base,AT91_TC_SR);
			at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
			at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			break;
		}
		case 11:	//表示1字节发送完成
		{
			if(iouart_dev->tx.len > 0)
			{
				iouart_dev->tx.byte = iouart_dev->tx.buf[iouart_dev->tx.index];
				iouart_dev->tx.index++;
				iouart_dev->tx.index %= IOUART_BUF_LEN;
				iouart_dev->tx.len--;
				iouart_dev->tx.bitStep = 0;
				iouart_dev->tx.parity = 0;
				at91_set_gpio_value(pdata->tx_pin,1);
				/*timer start*/
				at91_tc_read(pdata->tc_base,AT91_TC_SR);
				at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
				at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			}
			else
			{
				iouart_dev->trans_status = DRV_STATUS_RX;
				/*清中断标志*/
				rxIrqIsr = get_rxirq_isr(pdata->rx_pin_port);
				rxIrqIsr &=~(1 << (pdata->rx_pin - pdata->rx_pin_base));
				set_rxirq_isr(pdata->rx_pin_port,rxIrqIsr);
				/*允许IO中断*/
				at91_sys_write((pdata->rx_pin_port+PIO_IER),(1 << (pdata->rx_pin-pdata->rx_pin_base)));
				/*唤醒发送等待队列*/
//				wake_up_interruptible(&iouart_dev->tx_waitqueue);
			}
			break;
		}
		default:	//发送BIT0~BIT8
		{
			if((iouart_dev->tx.byte & (1 << (iouart_dev->tx.bitStep - 1))) > 0)
			{
				at91_set_gpio_value(pdata->tx_pin,1);
				iouart_dev->tx.parity = 1 - iouart_dev->tx.parity;
			}
			else
			{
				at91_set_gpio_value(pdata->tx_pin,0);
			}
			iouart_dev->tx.bitStep++;
			/*timer start*/
			at91_tc_read(pdata->tc_base,AT91_TC_SR);
			at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
			at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			break;
		}
	}
}

void iouart_timer_rx(struct iouart_dev_t *iouart_dev)
{
	unsigned int rxIrqIsr;
	struct iouart_platform_data *pdata = iouart_dev->pdev->dev.platform_data;

	switch(iouart_dev->rx.bitStep)
	{
		case 0:						//检查起始位是否正常
		{
			if(at91_get_gpio_value(pdata->rx_pin) == 0)
			{
				iouart_dev->rx.bitStep++;
				iouart_dev->rx.parity = 0;
				iouart_dev->rx.byte = 0;

				/*timer start*/
				at91_tc_read(pdata->tc_base,AT91_TC_SR);
				at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
				at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			}
			else
			{
//				printk("接收起始位错误!\n");
				goto exit1;
			}
			break;
		}
		case 9:			//接收校验位
		{
			if(at91_get_gpio_value(pdata->rx_pin) == 0)	 		//检测到低电平
			{
				if(iouart_dev->rx.parity > 0)
				{
//					printk("校验位错误!\n");
					goto exit1;
				}
			}
			else			 					//检测到高电平
			{
				 if(iouart_dev->rx.parity == 0)
				 {
//					printk("校验位错误!\n");
					 goto exit1;
				 }

			}
			iouart_dev->rx.bitStep++;
			/*timer start*/
			at91_tc_read(pdata->tc_base,AT91_TC_SR);
			at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
			at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			break;
		}
		case 10:			//接收停止位,1字节接收完成
		{
			if(at91_get_gpio_value(pdata->rx_pin) > 0 && iouart_dev->rx.len < IOUART_BUF_LEN)	 //检测到高电平
			{
				iouart_dev->rx.buf[(iouart_dev->rx.index + iouart_dev->rx.len)%IOUART_BUF_LEN] = iouart_dev->rx.byte;
				iouart_dev->rx.len++;
			}
			goto exit1;
		}
		default:						//接收数据位
		{
			if(at91_get_gpio_value(pdata->rx_pin) == 0)	//检测到低电平
			{
				iouart_dev->rx.byte &= ~(1 << (iouart_dev->rx.bitStep - 1));
			}
			else		   				//检测到高电平
			{
				iouart_dev->rx.byte |= (1 << (iouart_dev->rx.bitStep - 1));
				iouart_dev->rx.parity = 1 - iouart_dev->rx.parity;
			}
			iouart_dev->rx.bitStep++;
			/*timer start*/
			at91_tc_read(pdata->tc_base,AT91_TC_SR);
			at91_tc_write(pdata->tc_base,AT91_TC_RC,IOUART_TIMER_COUNT);
			at91_tc_write(pdata->tc_base,AT91_TC_CCR,AT91_TC_SWTRG);
			break;
		}
	}
	return;
exit1:
	iouart_dev->rx.bitStep = 0x00;
	iouart_dev->rx.parity = 0x00;
	iouart_dev->rx.byte = 0x00;

	/*清中断标志*/
	rxIrqIsr = get_rxirq_isr(pdata->rx_pin_port);
	rxIrqIsr &=~(1 << (pdata->rx_pin - pdata->rx_pin_base));
	set_rxirq_isr(pdata->rx_pin_port,rxIrqIsr);
	/*允许IO接收中断*/
	at91_sys_write((pdata->rx_pin_port+PIO_IER),(1 << (pdata->rx_pin-pdata->rx_pin_base)));
}

好了红外串口IO模拟实现代码就是这样,以上代码发送与接收使用的是同一个定时器中断,因此只能实现半双工通信,要想实现全双工通信,则必须使用两个定时器,不过半双工一般就够用了。希望这个代码对小伙伴们有点用,此代码写于4年前。

你可能感兴趣的:(嵌入式,Linux)