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


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

红外串口示意图

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

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 <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <asm/types.h>
#include <asm/uaccess.h>
#include <mach/at91_pio.h>
#include <mach/at91sam9x5.h>
#include <mach/at91_pmc.h>
#include <mach/at91_tc.h>
#include <mach/gpio.h>
#include <mach/io.h>
#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(ioua
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值