红外发送的驱动代码
2 E币
成为会员,免费下载资料
文件大小:5.75 KB
上传者:ngswfx
时间:2017-12-14 09:06:03
下载量:7
本帖最后由 ngswfx 于 2017-12-14 09:12 编辑
你是想通过3516去控制别的设备还是接受控制,如果是接受控制,这个海斯应该有,如果想控制别的设备,估计要自己想办法了
也就是说,如果你弄个IPC吸顶,顺便还想开空调或者电视的话,估计要自己弄个程序写个驱动让红外管子按照你的指令发控制码,这有点像小米的手机这种应用
不过这种c代码,网上应该很多,到CSDN找吧,只要是C的,应该都可以借鉴,你需要做的就是怎么让海斯芯片的GPIO口去输出相应的指令
我估计海斯没考虑这种发射应用,要是接收,应该有例子
我看了一下3516的SDK,里面的驱动,发现发射的接口在,但没实现,估计应该可以直接改一下就可以了
static int hiir_write(struct file* filp, const char __user* buf, size_t count, loff_t* f_pos)
{
return 0;
}
//我从网上下载了一个,说是linux用的,也没看太懂,你研究一下,看能不能借鉴,记得弄好,共享一下,这种功能有时候可能可以用上
//里面有个static ssize_t IR_Transmit(struct file *filp, const char *buf, size_t count, loff_t *f_pos),看怎么衔接海斯驱动里面的hirr_write,估计流程应该可以借鉴
[code]
1.硬件测试程序
#include "..\Inc\2410addr.h"
#include "..\Inc\2410lib.h"
#include "..\Inc\option.h"
#define IR_Addr (*(volatile unsigned char *)0x080000c8)
#define IR_NUM_MAX 68
//红外脉冲的最大输入个数,正常:2(Lead code)+32(16bit custom*2)+32(16bit data*2)+1(end)=67
#define IR_NUM_MIN 30
unsigned char gSending; //红外发射标志,1 为发射
unsigned char gLearning;
//红外学习标志,0:未学习状态,1:表示进入红外学习,正等待红外信号输入 2:红外学习过程中
unsigned short gCount;
//红外学习过程中,对红外数据进行计数(定时器溢出时递增,输入脉冲跳沿中断是也递增)
unsigned short gLearnTime; //红外学习时间,即输入脉冲高你电平的总个数
unsigned char gState;
unsigned short gIRData[100]; //红外学习时,数据缓冲区,用于存放红外脉冲的高低电平时间,第 1
个数据为低电平时间
unsigned short *pIRData; //红外发送时,指向数据缓冲区
unsigned char gTimeoutCount; //用于红外学习时的定时器溢出计数器
unsigned char gState_Study;
unsigned char gPWMEn;
unsigned short gLen;
void LearnOK(void);
//38KHz 载波输出使能,1 为开定时器 1 输入输出载波信号,0 为停止
//要发送红外的数据长度
void __irq Int_IRStudy(void);
void __irq Int_Timer0(void);
void __irq Int_Timer1(void);
/*********************************************************************************
函数原形:void Init_IR(void)
功 能:初始化红外相关的 I/O,中断及定时器等
*********************************************************************************/
void Init_IR(void){
rGPFCON = rGPFCON & ~(0x3 << 0) | (0x2 << 0); //EINT0=GPF0
rGPFUP = rGPFUP | ~(0x1 << 0); //disable
rGPBCON = rGPBCON & ~(0x3 << 2) | (0x2 << 2); //TOUT1
rEXTINT0 = rEXTINT0 & ~(0x7 << 0) | (0x6 << 0);//EINT0:Both edge triggered
pISR_EINT0 = (int)Int_IRStudy;
ClearPending(BIT_EINT0);
pISR_TIMER0 = (int)Int_Timer0;
rTCFG0 = rTCFG0 & ~(0xff << 0) | (0x01 << 0); //prescale value = 1
rTCFG1 = rTCFG1 & ~(0xf << 0) | (0x2 << 0); //divider value = 8
rTCNTB0 = 0xffff;
rTCFG1 = rTCFG1 & ~(0xf << 4) | (0x2 << 4);//divider value = 8,50MHz/(1+1)/8=3.125MHz
rTCNTB1 = 0x52;
//3.125MHz/38K=82=0x52;
rTCMPB1 = 0x29; //0x52/2=0x29
rTCON = rTCON & ~(0xf<<8)|(0xa<<8);//Timer1:auto reload; Update TCNTB1,TCMPB1; Stop
pISR_TIMER1 = (int)Int_Timer1;
rINTMSK &= ~(BIT_EINT0);
gState = 1;
gLearning = 0;
}
//打开外部中断 0 开始红外学习
/*********************************************************************************
函数原形:void __irq Eint3Isr(void)
功 能:IR 输入脉冲双边沿中断,记录电平时间宽度
*********************************************************************************/
void __irq Int_IRStudy(void){
rGPFCON = rGPFCON & ~(0x3 << 0) | (0x0 << 0); //GPF0=IN
if (gLearning == 0){
if (!(rGPFDAT & 0x1)){ //确认是否为低电平
gLearning = 1;
rTCON = rTCON & ~(0x1f << 0) | 0x2; //Updata TCNTB0
rTCON = rTCON & ~(0x1f << 0) | 0x1; //start for timer0
rINTMSK &= ~(BIT_TIMER0);
//打开定时器中断
}
}
gState = 0; //保存当前脉冲电平值
pIRData = gIRData; //初始化红外数据缓冲区
else if (gLearning == 1){ //确认红外引导码
*pIRData = rTCNTO0;
if ((rGPFDAT & 0x1) && (gState == 0) && (*pIRData < 45535) && (*pIRData > 25535)){
//Lead Code 9ms, > 6.4ms, < 12.8ms
rTCON = rTCON & ~(0x1f << 0) | 0x2;
//Updata TCNTB0
}
rTCON = rTCON & ~(0x1f << 0) | 0x1; //start for timer0
gLearning = 2;
gState = 1;
pIRData++;
else{
rINTMSK |= BIT_TIMER0;
//Lead Code check fail
//关定时器中断
rTCON = rTCON & ~(0x1f << 0) | 0x2; //stop for timer0
gLearning = 0;
}
}
else{
if (((rGPFDAT & 0x1) && gState == 0) || ((!(rGPFDAT & 0x1)) && gState == 1)){
*pIRData++ = rTCNTO0;//记录计数器值,第 1 个数据为低电平时间,第 2 为高电平时间。。。
gState = gState ? 0 : 1; //乒乓开关用于状态翻转判定
if (gLearning > IR_NUM_MAX){
LearnOK();
}
else{
rTCON = rTCON & ~(0x1f << 0) | 0x2;
rTCON = rTCON & ~(0x1f << 0) | 0x1;
gLearning++;
}
}
}
rGPFCON = rGPFCON & ~(0x3 << 0) | (0x2 << 0); //GPF0=EINT0
ClearPending(BIT_EINT0);
}
/***************************************************************************************
函数原形:void __irq Timer0Done(void)
功 能:红外学习定时器溢出中断处理,根据红外学习脉冲数来确定学习是否成功结束
***************************************************************************************/
void __irq Int_Timer0(void){
if (gSending == 1){
if(gCount >= gLen){
gSending = 0;
gPWMEn = 0;
rTCON = rTCON & 0xffff000;
//发送结束
//stop for timer0 timer1
rINTMSK |= BIT_TIMER1 | BIT_TIMER0; //关定时器中断
}
else{
if (gPWMEn == 0) { //上一个数据为低电平,则该数据为高电平
gPWMEn = 1;
rTCNTB0 = 0xffff - *pIRData;
rTCON = (rTCON&0xffffff0) | 0x2;
rTCON = (rTCON&0xffffff0) | 0x1;
rTCON = (rTCON&0xffff0ff) | 0xa00;
//高电平时间
rTCON = (rTCON&0xffff0ff) | 0x900; //重新启动 38K 载波输出
}
else{
gPWMEn = 0;
rTCNTB0 = 0xffff - *pIRData;
rTCON = (rTCON & 0xffffff0) | 0x2;
rTCON = (rTCON & 0xffffff0) | 0x1;
}
pIRData++;
gCount++;
}
}
else if (gLearning > IR_NUM_MIN) {
LearnOK();
}
else{
rINTMSK |= BIT_TIMER0;
//低电平时间
//红外学习时间溢出结束
//关定时器中断
rTCON = rTCON & ~(0x1f << 0) | 0x0; //stop for timer0
gLearning = 0;
}
ClearPending(BIT_TIMER0);
}
/*********************************************************************************
函数原形:void __irq Timer1Done(void)
功 能:产生红外的 38KHz 载波
参 数:gPWMEn -- 1 为输出 38K 载波,0 为停止输出
********************************************************************************/
void __irq Int_Timer1(void){
if (!gPWMEn){
rTCON = rTCON & 0xffff0ff;
}
ClearPending(BIT_TIMER1);
}
//stop for Timer 1
/*********************************************************************************
函数原形:void LearnOK(void)
功 能:红外学习成功
*********************************************************************************/
void LearnOK(void){
rTCON = rTCON & ~(0x1f << 0) | 0x2; //stop timer0
rINTMSK |= BIT_EINT0 | BIT_TIMER0;
gLen = gLearning - 1; //保存红外学习的数据个数
gLearning = 0xff;
}
/*********************************************************************************
函数原形:void SendIR(void)
功 能:启动红外发送
*********************************************************************************/
void SendIR(void){
pIRData = gIRData;
gSending = 1;
gCount = 0;
gPWMEn = 1; //发送高电平, 与红外学习时电平相反
rTCNTB0 = 0xffff - *pIRData;
pIRData++;
gCount++;
rTCON = (rTCON & 0xffffff0) | 0x2; //Update TCNTB0, TCMPB0
rTCON = (rTCON & 0xffffff0) | 0x1; //Start for Timer 0
rTCON = (rTCON & 0xffff0ff) | 0xa00;//Timer1:auto reload;Update TCNTB1,TCMPB1; Stop
rTCON = (rTCON & 0xffff0ff) | 0x900; //Start for Timer 1
rINTMSK &= ~(BIT_TIMER0 | BIT_TIMER1);
}
/*********************************************************************************
函数原形:void LearnIR(unsigned char which)
功 能:置 IR 学习开始条件,即进入红外学习
*********************************************************************************/
void LearnIR(void){
unsigned char i = 0;
IR_Addr = 0x0f; //使能所有 IR 通道输出
while (1){
Init_IR();
while (gLearning != 0xff); //等待红外学习完成
SendIR();
while (gSending);
}
}
2.Linux 驱动程序
#define DEVICE_NAME "PGM_IR"
#define BUZZ_MAJOR 236
#define IRQ_IRInput IRQ_EINT0
//等待红外发射完成
#define IR_NUM_MAX 68 //2(Lead code)+32(16bit custom*2)+32(16bit data*2)+1(end)=67
#define IR_NUM_MIN 30
#define Wait_Time 1800 //IR study wait times 18s
#define pIR_ADDR 0x080000c8
void * vIR_ADDR;
unsigned char Learning, IO_State;
unsigned char PWMEn, Transmit_flag, TLen, Transmit_Index;
static DECLARE_WAIT_QUEUE_HEAD(Wait_Study);
static DECLARE_WAIT_QUEUE_HEAD(Wait_Transmit);
//IR study use
//IR Transmits
static void pwm_irq_handle(int irq, void *dev_id, struct pt_regs *regs) {//38K
if(!PWMEn){
TCON = TCON & (~(0xf << 8)) | (0xa << 8);
//Timer1:auto reload;Inverter off;Update TCNTB1,TCMPB1;stop
}
}
static void timer0_Transmit_irq_handle(int irq, void *dev_id, struct pt_regs *regs){
unsigned short *pIRData = dev_id;
if (Transmit_flag == 1) { //IR Transmit
if(Transmit_Index >= TLen){
Transmit_flag = 0;
PWMEn = 0;
TCON = TCON & 0xffff000; //stop for timer0 timer1
wake_up_interruptible(&Wait_Transmit);
}
else{
if (PWMEn == 0){
PWMEn = 1;
TCNTB0 = 0xffff - *(pIRData + Transmit_Index);
TCON = (TCON & 0xffffff0) | 0x2;
TCON = (TCON & 0xffffff0) | 0x1;
TCON = (TCON & 0xffff0ff) | 0xa00;
TCON = (TCON & 0xffff0ff) | 0x900;
}
else{
PWMEn = 0;
TCNTB0 = 0xffff - *(pIRData + Transmit_Index);
TCON = (TCON & 0xffffff0) | 0x2;
TCON = (TCON & 0xffffff0) | 0x1;
}
Transmit_Index++;
}
}
}
static void timer0_Study_irq_handle(int irq, void *dev_id, struct pt_regs *regs){
TCON = TCON & ~(0x1f << 0); //stop for timer0
if (Learning > IR_NUM_MIN){
wake_up_interruptible(&Wait_Study);
}
else{
Learning = 0;
}
}
static void IRInput_irq_handle(int irq, void *dev_id, struct pt_regs *regs){
unsigned short *pIRData = dev_id;
set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_DIS | GPIO_MODE_IN); //INPUT
if (Learning == 0){
if (!read_gpio_bit(GPIO_F0)) {
Learning = 1;
//check Lead code
TCON = TCON & ~(0x1f << 0) | 0x2; //Updata TCNTB0
TCON = TCON & ~(0x1f << 0) | 0x1; //start for timer0
IO_State = 0;
}
}
else if (Learning == 1){ //affirm Lead code
*(pIRData) = TCNTO0;
if (read_gpio_bit(GPIO_F0) && (IO_State == 0) && (*pIRData < 45535) && (*pIRData >
25535)) { //Lead Code 9ms, > 6.4ms, < 12.8ms
TCON = TCON & ~(0x1f << 0) | 0x2; //Updata TCNTB0
TCON = TCON & ~(0x1f << 0) | 0x1; //start for timer0
Learning = 2;
IO_State = 1;
}
else {
TCON = TCON & ~(0x1f << 0);
Learning = 0;
}
}
else{
//Lead Code check fail
//stop for timer0
if((read_gpio_bit(GPIO_F0) && IO_State == 0) || ((!read_gpio_bit(GPIO_F0)) &&
IO_State == 1)) {
*(pIRData+Learning-1) = TCNTO0;
IO_State=IO_State?0:1;
if (Learning > IR_NUM_MAX){
wake_up_interruptible(&Wait_Study);
}
else{
TCON = TCON & ~(0x1f << 0) | 0x2;
TCON = TCON & ~(0x1f << 0) | 0x1;
Learning++;
}
}
}
set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_DIS | GPIO_MODE_EINT); //EINT0
}
static ssize_t IR_Study(struct file *filp, char *buf, size_t count, loff_t *f_pos){
int ret, i;
unsigned short * Study_Buf;
if ((count < IR_NUM_MIN*2) || (count > 256)){
printk("<1> buf size invalidation\n");
return -EINVAL;
}
if (!(Study_Buf = kmalloc(count, GFP_KERNEL))){
printk("<1> kmalloc fail\n");
return -EINVAL;
}
ret
=
request_irq(IRQ_IRInput,
IRInput_irq_handle,
SA_INTERRUPT,
DEVICE_NAME"IRQ_EINT0", Study_Buf);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Study_Buf);
return -EBUSY;
}
ret = request_irq(IRQ_TIMER0,
DEVICE_NAME"IRQ_TIMER0_Study", NULL);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Study_Buf);
free_irq(IRQ_IRInput, Study_Buf);
}
Learning = 0;
timer0_Study_irq_handle,
SA_INTERRUPT,
set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_DIS | GPIO_MODE_EINT);
interruptible_sleep_on_timeout(&Wait_Study, Wait_Time);
free_irq(IRQ_IRInput, Study_Buf);
free_irq(IRQ_TIMER0, NULL);
if (Learning > IR_NUM_MIN){
/* for (i = 0; i < Learning - 1; i++)
{
if (!(i%10)) printk("\n<1>");
printk("%04x,", *(Study_Buf+i));
}
printk("\nLearning is %d\n", Learning);*/
copy_to_user(buf, Study_Buf, (ret = (Learning - 1) * 2));
}
else{
ret = 0;
}
kfree(Study_Buf);
// free_irq(IRQ_IRInput, Study_Buf);
// free_irq(IRQ_TIMER0, NULL);
return ret;
}
static ssize_t IR_Transmit(struct file *filp, const char *buf, size_t count, loff_t *f_pos){
int ret;
unsigned short * Transmit_Buf;
if ((count < IR_NUM_MIN*2) || (count > 256)){
printk("<1> buf size invalidation\n");
return -EINVAL;
}
if (!(Transmit_Buf = kmalloc(count, GFP_KERNEL))){
printk("<1> kmalloc fail\n");
return -EINVAL;
}
copy_from_user(Transmit_Buf, buf, count);
ret = request_irq(IRQ_TIMER0, timer0_Transmit_irq_handle,
DEVICE_NAME"IRQ_TIMER0", Transmit_Buf);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Transmit_Buf);
return -EBUSY;
}
SA_INTERRUPT,
ret = request_irq(IRQ_TIMER1, pwm_irq_handle, SA_INTERRUPT, DEVICE_NAME"IRQ_TIMER1",
NULL);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Transmit_Buf);
free_irq(IRQ_TIMER0, Transmit_Buf);
return -EBUSY;
}
Transmit_flag = 1;
PWMEn = 1;
TLen = count/2;
TCNTB0 = 0xffff - *Transmit_Buf;
Transmit_Index = 1;
TCON = (TCON & 0xffffff0) | 0x2; //Update TCNTB0, TCMPB0
TCON = (TCON & 0xffffff0) | 0x1; //Start for Timer 0
TCON = (TCON & 0xffff0ff) | 0xa00;
//Timer1:auto reload; Inverter off; Update TCNTB1,TCMPB1; Stop
TCON = (TCON & 0xffff0ff) | 0x900; //Start for Timer 1
interruptible_sleep_on_timeout(&Wait_Transmit,20);//9+4.5+2.24*32+0.56=85.74
TCON &= 0xffff000;
free_irq(IRQ_TIMER0, Transmit_Buf);
free_irq(IRQ_TIMER1, NULL);
kfree(Transmit_Buf);
return count;
}
static int IR_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long
arg){
static unsigned char channel = 0;
if (arg < 4){
switch (cmd){
case 0:
channel &= ~(0x1 << arg);
break;
case 1:
channel |= (0x1 << arg);
break;
case 2:
channel = 0;
break;
case 3:
channel = 0xf;
break;
default:
break;
}
writeb(channel, vIR_ADDR);
}
}
static struct file_operations IR_fops = {
owner:THIS_MODULE,
write:IR_Transmit,
read: IR_Study,
ioctl:IR_ioctl,
};
static devfs_handle_t devfs_handle;
static int __init IR_init(void){
devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT, BUZZ_MAJOR, 0,
S_IFCHR | S_IRUSR | S_IWUSR, &IR_fops, NULL);
set_gpio_ctrl(GPIO_B1 | GPIO_PULLUP_DIS | GPIO_MODE_TOUT);//TOUT1
set_external_irq(IRQ_IRInput, EXT_BOTH_EDGES, GPIO_PULLUP_DIS);
TCFG0 |= TCFG0_PRE0(1); //Timer0,1 prescale value = 1
TCFG1 = TCFG1 & (~(0xff << 0)) | (0x2 << 0) | (0x2 << 4);
//Timer0,1 divider value = 8, 50MHz/(1+1)/8=3.125MHz
TCNTB0 = 0xffff; //21ms
TCNTB1 = 0x52; //3.125MHz/38K=82=0x52;
TCMPB1 = 0x29; //0x52/2=0x29
TCON &= ~(0xfff << 0); //Timer0,1 Stop
Learning = 0;
Transmit_flag = 0;
PWMEn = 0;
vIR_ADDR = ioremap(pIR_ADDR, 1);
printk(KER F DEVICE_NAME ": init OK\n");
return(0);
}
static void __exit IR_cleanup(void){
iounmap(vIR_ADDR);
devfs_unregister(devfs_handle);
}
module_init(IR_init);
module_exit(IR_cleanup);
MODULE_LICENSE("GPL");
3.驱动测试程序
#include
#include
#include
#include
#include
int main(int argc,char *argv[]){
int fd, IR_fd;
int ret, i;
unsigned short buf[100], TBuf[100];
fd = open("/dev/PGM_IR", O_RDWR);
if (fd < 0){
perror("open PGM_IR fail");
exit(1);
}
ioctl(fd, 3, 0);
if (argc == 2){
IR_fd = open("/tmp/IR_fd0", O_RDONLY);
if (IR_fd < 0){
perror("open IR_fd0");
}
else{
ret = read(IR_fd, TBuf, sizeof(TBuf));
if (ret > 0){
for (i = 0; i < ret/2; i++){
if (!(i%10)) printf("\n");
printf("%04x,", TBuf);
}
}
write(fd, TBuf, ret);
}
}
ret = read(fd, buf, sizeof(buf)) / 2;
if (ret > 0){
for (i = 0; i < ret; i++){
if (!(i%10)){
printf("\n");
}
printf("%04x,", buf);
}
IR_fd = creat("/tmp/IR_fd0", O_RDWR);
if (IR_fd > 0){
printf("creat IR_fd sucess\n");
write(IR_fd, buf, sizeof(short) * ret);
}
else{
perror("IR_fd");
}
}
else{
perror("read PGM_IR");
}
close(fd);
return 0;
}
测试方法:
1> 测试红外学习时直接运行可执行文件即可,然后按住红外遥控,学习完后,将打印出红外学习数据,
然后创建"IR_fd0"文件,将数据保存。
2> 测试红外发射,在执行文件时,需给传送任一参数,如"# ./IR_test 1"。它将 IR_fd0 中的红外
数据发射出去。
4.出现的问题
用
"
request_irq(IRQ_TIMER0,
timer0_Transmit_irq_handle,
SA_INTERRUPT,
DEVICE_NAME"IRQ_TIMER0", Transmit_Buf);"申请中断时,但用"free_irq(IRQ_TIMER0, NULL);"不能
释放中断(可用"#cat /proc/interrupts"命令观察)。
必须使 free_irq 中的 dev_id 参数与 request_irq 中的该参数保持一至,即:free_irq(IRQ_TIMER0,
Transmit_Buf);
5.总结
此驱动与上面几个驱动有几点不同,主要表现在对中断的处理
1> 申请中断和释放中断都直接在 read(红外学习)、write(红外发射)系统函数里完成。原因主要考虑
到红外学习与发射都需要阻塞等待(sleep_on 的系列函数),但在阻塞的过程中又不允许其它进程也对红外
进行操作(因为它们都占用了相同的硬件资源,如定时器 0 等)。所以为了实现红外驱动的互斥,当进行应
用用户调用红外学习或发射时才申请中断,如果中断申请失败,说明已被占用,则出错退出。
2> 同 一 中 断 有 两 个 不 同 的 中 断 处 理 程 序 。 红 外 学 习 时 , 调 用 request_irq(IRQ_TIMER0,
timer0_Study_irq_handle, SA_INTERRUPT, DEVICE_NAME"IRQ_TIMER0_Study", NULL); 红 外 发 射 时 调 用
request_irq(IRQ_TIMER0, timer0_Transmit_irq_handle, SA_INTERRUPT, DEVICE_NAME"IRQ_TIMER0",
Transmit_Buf);这样比共用一个处理程序的可读性和效率更高。
3> 在申请中断时 request_irq 中的 void *dev_id 参数作为私有数据(红外数据指针)传递给中断处
理程序的该参数,参考上面各中断处理程序。
[/code]
你是想通过3516去控制别的设备还是接受控制,如果是接受控制,这个海斯应该有,如果想控制别的设备,估计要自己想办法了
也就是说,如果你弄个IPC吸顶,顺便还想开空调或者电视的话,估计要自己弄个程序写个驱动让红外管子按照你的指令发控制码,这有点像小米的手机这种应用
不过这种c代码,网上应该很多,到CSDN找吧,只要是C的,应该都可以借鉴,你需要做的就是怎么让海斯芯片的GPIO口去输出相应的指令
我估计海斯没考虑这种发射应用,要是接收,应该有例子
我看了一下3516的SDK,里面的驱动,发现发射的接口在,但没实现,估计应该可以直接改一下就可以了
static int hiir_write(struct file* filp, const char __user* buf, size_t count, loff_t* f_pos)
{
return 0;
}
//我从网上下载了一个,说是linux用的,也没看太懂,你研究一下,看能不能借鉴,记得弄好,共享一下,这种功能有时候可能可以用上
//里面有个static ssize_t IR_Transmit(struct file *filp, const char *buf, size_t count, loff_t *f_pos),看怎么衔接海斯驱动里面的hirr_write,估计流程应该可以借鉴
[code]
1.硬件测试程序
#include "..\Inc\2410addr.h"
#include "..\Inc\2410lib.h"
#include "..\Inc\option.h"
#define IR_Addr (*(volatile unsigned char *)0x080000c8)
#define IR_NUM_MAX 68
//红外脉冲的最大输入个数,正常:2(Lead code)+32(16bit custom*2)+32(16bit data*2)+1(end)=67
#define IR_NUM_MIN 30
unsigned char gSending; //红外发射标志,1 为发射
unsigned char gLearning;
//红外学习标志,0:未学习状态,1:表示进入红外学习,正等待红外信号输入 2:红外学习过程中
unsigned short gCount;
//红外学习过程中,对红外数据进行计数(定时器溢出时递增,输入脉冲跳沿中断是也递增)
unsigned short gLearnTime; //红外学习时间,即输入脉冲高你电平的总个数
unsigned char gState;
unsigned short gIRData[100]; //红外学习时,数据缓冲区,用于存放红外脉冲的高低电平时间,第 1
个数据为低电平时间
unsigned short *pIRData; //红外发送时,指向数据缓冲区
unsigned char gTimeoutCount; //用于红外学习时的定时器溢出计数器
unsigned char gState_Study;
unsigned char gPWMEn;
unsigned short gLen;
void LearnOK(void);
//38KHz 载波输出使能,1 为开定时器 1 输入输出载波信号,0 为停止
//要发送红外的数据长度
void __irq Int_IRStudy(void);
void __irq Int_Timer0(void);
void __irq Int_Timer1(void);
/*********************************************************************************
函数原形:void Init_IR(void)
功 能:初始化红外相关的 I/O,中断及定时器等
*********************************************************************************/
void Init_IR(void){
rGPFCON = rGPFCON & ~(0x3 << 0) | (0x2 << 0); //EINT0=GPF0
rGPFUP = rGPFUP | ~(0x1 << 0); //disable
rGPBCON = rGPBCON & ~(0x3 << 2) | (0x2 << 2); //TOUT1
rEXTINT0 = rEXTINT0 & ~(0x7 << 0) | (0x6 << 0);//EINT0:Both edge triggered
pISR_EINT0 = (int)Int_IRStudy;
ClearPending(BIT_EINT0);
pISR_TIMER0 = (int)Int_Timer0;
rTCFG0 = rTCFG0 & ~(0xff << 0) | (0x01 << 0); //prescale value = 1
rTCFG1 = rTCFG1 & ~(0xf << 0) | (0x2 << 0); //divider value = 8
rTCNTB0 = 0xffff;
rTCFG1 = rTCFG1 & ~(0xf << 4) | (0x2 << 4);//divider value = 8,50MHz/(1+1)/8=3.125MHz
rTCNTB1 = 0x52;
//3.125MHz/38K=82=0x52;
rTCMPB1 = 0x29; //0x52/2=0x29
rTCON = rTCON & ~(0xf<<8)|(0xa<<8);//Timer1:auto reload; Update TCNTB1,TCMPB1; Stop
pISR_TIMER1 = (int)Int_Timer1;
rINTMSK &= ~(BIT_EINT0);
gState = 1;
gLearning = 0;
}
//打开外部中断 0 开始红外学习
/*********************************************************************************
函数原形:void __irq Eint3Isr(void)
功 能:IR 输入脉冲双边沿中断,记录电平时间宽度
*********************************************************************************/
void __irq Int_IRStudy(void){
rGPFCON = rGPFCON & ~(0x3 << 0) | (0x0 << 0); //GPF0=IN
if (gLearning == 0){
if (!(rGPFDAT & 0x1)){ //确认是否为低电平
gLearning = 1;
rTCON = rTCON & ~(0x1f << 0) | 0x2; //Updata TCNTB0
rTCON = rTCON & ~(0x1f << 0) | 0x1; //start for timer0
rINTMSK &= ~(BIT_TIMER0);
//打开定时器中断
}
}
gState = 0; //保存当前脉冲电平值
pIRData = gIRData; //初始化红外数据缓冲区
else if (gLearning == 1){ //确认红外引导码
*pIRData = rTCNTO0;
if ((rGPFDAT & 0x1) && (gState == 0) && (*pIRData < 45535) && (*pIRData > 25535)){
//Lead Code 9ms, > 6.4ms, < 12.8ms
rTCON = rTCON & ~(0x1f << 0) | 0x2;
//Updata TCNTB0
}
rTCON = rTCON & ~(0x1f << 0) | 0x1; //start for timer0
gLearning = 2;
gState = 1;
pIRData++;
else{
rINTMSK |= BIT_TIMER0;
//Lead Code check fail
//关定时器中断
rTCON = rTCON & ~(0x1f << 0) | 0x2; //stop for timer0
gLearning = 0;
}
}
else{
if (((rGPFDAT & 0x1) && gState == 0) || ((!(rGPFDAT & 0x1)) && gState == 1)){
*pIRData++ = rTCNTO0;//记录计数器值,第 1 个数据为低电平时间,第 2 为高电平时间。。。
gState = gState ? 0 : 1; //乒乓开关用于状态翻转判定
if (gLearning > IR_NUM_MAX){
LearnOK();
}
else{
rTCON = rTCON & ~(0x1f << 0) | 0x2;
rTCON = rTCON & ~(0x1f << 0) | 0x1;
gLearning++;
}
}
}
rGPFCON = rGPFCON & ~(0x3 << 0) | (0x2 << 0); //GPF0=EINT0
ClearPending(BIT_EINT0);
}
/***************************************************************************************
函数原形:void __irq Timer0Done(void)
功 能:红外学习定时器溢出中断处理,根据红外学习脉冲数来确定学习是否成功结束
***************************************************************************************/
void __irq Int_Timer0(void){
if (gSending == 1){
if(gCount >= gLen){
gSending = 0;
gPWMEn = 0;
rTCON = rTCON & 0xffff000;
//发送结束
//stop for timer0 timer1
rINTMSK |= BIT_TIMER1 | BIT_TIMER0; //关定时器中断
}
else{
if (gPWMEn == 0) { //上一个数据为低电平,则该数据为高电平
gPWMEn = 1;
rTCNTB0 = 0xffff - *pIRData;
rTCON = (rTCON&0xffffff0) | 0x2;
rTCON = (rTCON&0xffffff0) | 0x1;
rTCON = (rTCON&0xffff0ff) | 0xa00;
//高电平时间
rTCON = (rTCON&0xffff0ff) | 0x900; //重新启动 38K 载波输出
}
else{
gPWMEn = 0;
rTCNTB0 = 0xffff - *pIRData;
rTCON = (rTCON & 0xffffff0) | 0x2;
rTCON = (rTCON & 0xffffff0) | 0x1;
}
pIRData++;
gCount++;
}
}
else if (gLearning > IR_NUM_MIN) {
LearnOK();
}
else{
rINTMSK |= BIT_TIMER0;
//低电平时间
//红外学习时间溢出结束
//关定时器中断
rTCON = rTCON & ~(0x1f << 0) | 0x0; //stop for timer0
gLearning = 0;
}
ClearPending(BIT_TIMER0);
}
/*********************************************************************************
函数原形:void __irq Timer1Done(void)
功 能:产生红外的 38KHz 载波
参 数:gPWMEn -- 1 为输出 38K 载波,0 为停止输出
********************************************************************************/
void __irq Int_Timer1(void){
if (!gPWMEn){
rTCON = rTCON & 0xffff0ff;
}
ClearPending(BIT_TIMER1);
}
//stop for Timer 1
/*********************************************************************************
函数原形:void LearnOK(void)
功 能:红外学习成功
*********************************************************************************/
void LearnOK(void){
rTCON = rTCON & ~(0x1f << 0) | 0x2; //stop timer0
rINTMSK |= BIT_EINT0 | BIT_TIMER0;
gLen = gLearning - 1; //保存红外学习的数据个数
gLearning = 0xff;
}
/*********************************************************************************
函数原形:void SendIR(void)
功 能:启动红外发送
*********************************************************************************/
void SendIR(void){
pIRData = gIRData;
gSending = 1;
gCount = 0;
gPWMEn = 1; //发送高电平, 与红外学习时电平相反
rTCNTB0 = 0xffff - *pIRData;
pIRData++;
gCount++;
rTCON = (rTCON & 0xffffff0) | 0x2; //Update TCNTB0, TCMPB0
rTCON = (rTCON & 0xffffff0) | 0x1; //Start for Timer 0
rTCON = (rTCON & 0xffff0ff) | 0xa00;//Timer1:auto reload;Update TCNTB1,TCMPB1; Stop
rTCON = (rTCON & 0xffff0ff) | 0x900; //Start for Timer 1
rINTMSK &= ~(BIT_TIMER0 | BIT_TIMER1);
}
/*********************************************************************************
函数原形:void LearnIR(unsigned char which)
功 能:置 IR 学习开始条件,即进入红外学习
*********************************************************************************/
void LearnIR(void){
unsigned char i = 0;
IR_Addr = 0x0f; //使能所有 IR 通道输出
while (1){
Init_IR();
while (gLearning != 0xff); //等待红外学习完成
SendIR();
while (gSending);
}
}
2.Linux 驱动程序
#define DEVICE_NAME "PGM_IR"
#define BUZZ_MAJOR 236
#define IRQ_IRInput IRQ_EINT0
//等待红外发射完成
#define IR_NUM_MAX 68 //2(Lead code)+32(16bit custom*2)+32(16bit data*2)+1(end)=67
#define IR_NUM_MIN 30
#define Wait_Time 1800 //IR study wait times 18s
#define pIR_ADDR 0x080000c8
void * vIR_ADDR;
unsigned char Learning, IO_State;
unsigned char PWMEn, Transmit_flag, TLen, Transmit_Index;
static DECLARE_WAIT_QUEUE_HEAD(Wait_Study);
static DECLARE_WAIT_QUEUE_HEAD(Wait_Transmit);
//IR study use
//IR Transmits
static void pwm_irq_handle(int irq, void *dev_id, struct pt_regs *regs) {//38K
if(!PWMEn){
TCON = TCON & (~(0xf << 8)) | (0xa << 8);
//Timer1:auto reload;Inverter off;Update TCNTB1,TCMPB1;stop
}
}
static void timer0_Transmit_irq_handle(int irq, void *dev_id, struct pt_regs *regs){
unsigned short *pIRData = dev_id;
if (Transmit_flag == 1) { //IR Transmit
if(Transmit_Index >= TLen){
Transmit_flag = 0;
PWMEn = 0;
TCON = TCON & 0xffff000; //stop for timer0 timer1
wake_up_interruptible(&Wait_Transmit);
}
else{
if (PWMEn == 0){
PWMEn = 1;
TCNTB0 = 0xffff - *(pIRData + Transmit_Index);
TCON = (TCON & 0xffffff0) | 0x2;
TCON = (TCON & 0xffffff0) | 0x1;
TCON = (TCON & 0xffff0ff) | 0xa00;
TCON = (TCON & 0xffff0ff) | 0x900;
}
else{
PWMEn = 0;
TCNTB0 = 0xffff - *(pIRData + Transmit_Index);
TCON = (TCON & 0xffffff0) | 0x2;
TCON = (TCON & 0xffffff0) | 0x1;
}
Transmit_Index++;
}
}
}
static void timer0_Study_irq_handle(int irq, void *dev_id, struct pt_regs *regs){
TCON = TCON & ~(0x1f << 0); //stop for timer0
if (Learning > IR_NUM_MIN){
wake_up_interruptible(&Wait_Study);
}
else{
Learning = 0;
}
}
static void IRInput_irq_handle(int irq, void *dev_id, struct pt_regs *regs){
unsigned short *pIRData = dev_id;
set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_DIS | GPIO_MODE_IN); //INPUT
if (Learning == 0){
if (!read_gpio_bit(GPIO_F0)) {
Learning = 1;
//check Lead code
TCON = TCON & ~(0x1f << 0) | 0x2; //Updata TCNTB0
TCON = TCON & ~(0x1f << 0) | 0x1; //start for timer0
IO_State = 0;
}
}
else if (Learning == 1){ //affirm Lead code
*(pIRData) = TCNTO0;
if (read_gpio_bit(GPIO_F0) && (IO_State == 0) && (*pIRData < 45535) && (*pIRData >
25535)) { //Lead Code 9ms, > 6.4ms, < 12.8ms
TCON = TCON & ~(0x1f << 0) | 0x2; //Updata TCNTB0
TCON = TCON & ~(0x1f << 0) | 0x1; //start for timer0
Learning = 2;
IO_State = 1;
}
else {
TCON = TCON & ~(0x1f << 0);
Learning = 0;
}
}
else{
//Lead Code check fail
//stop for timer0
if((read_gpio_bit(GPIO_F0) && IO_State == 0) || ((!read_gpio_bit(GPIO_F0)) &&
IO_State == 1)) {
*(pIRData+Learning-1) = TCNTO0;
IO_State=IO_State?0:1;
if (Learning > IR_NUM_MAX){
wake_up_interruptible(&Wait_Study);
}
else{
TCON = TCON & ~(0x1f << 0) | 0x2;
TCON = TCON & ~(0x1f << 0) | 0x1;
Learning++;
}
}
}
set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_DIS | GPIO_MODE_EINT); //EINT0
}
static ssize_t IR_Study(struct file *filp, char *buf, size_t count, loff_t *f_pos){
int ret, i;
unsigned short * Study_Buf;
if ((count < IR_NUM_MIN*2) || (count > 256)){
printk("<1> buf size invalidation\n");
return -EINVAL;
}
if (!(Study_Buf = kmalloc(count, GFP_KERNEL))){
printk("<1> kmalloc fail\n");
return -EINVAL;
}
ret
=
request_irq(IRQ_IRInput,
IRInput_irq_handle,
SA_INTERRUPT,
DEVICE_NAME"IRQ_EINT0", Study_Buf);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Study_Buf);
return -EBUSY;
}
ret = request_irq(IRQ_TIMER0,
DEVICE_NAME"IRQ_TIMER0_Study", NULL);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Study_Buf);
free_irq(IRQ_IRInput, Study_Buf);
}
Learning = 0;
timer0_Study_irq_handle,
SA_INTERRUPT,
set_gpio_ctrl(GPIO_F0 | GPIO_PULLUP_DIS | GPIO_MODE_EINT);
interruptible_sleep_on_timeout(&Wait_Study, Wait_Time);
free_irq(IRQ_IRInput, Study_Buf);
free_irq(IRQ_TIMER0, NULL);
if (Learning > IR_NUM_MIN){
/* for (i = 0; i < Learning - 1; i++)
{
if (!(i%10)) printk("\n<1>");
printk("%04x,", *(Study_Buf+i));
}
printk("\nLearning is %d\n", Learning);*/
copy_to_user(buf, Study_Buf, (ret = (Learning - 1) * 2));
}
else{
ret = 0;
}
kfree(Study_Buf);
// free_irq(IRQ_IRInput, Study_Buf);
// free_irq(IRQ_TIMER0, NULL);
return ret;
}
static ssize_t IR_Transmit(struct file *filp, const char *buf, size_t count, loff_t *f_pos){
int ret;
unsigned short * Transmit_Buf;
if ((count < IR_NUM_MIN*2) || (count > 256)){
printk("<1> buf size invalidation\n");
return -EINVAL;
}
if (!(Transmit_Buf = kmalloc(count, GFP_KERNEL))){
printk("<1> kmalloc fail\n");
return -EINVAL;
}
copy_from_user(Transmit_Buf, buf, count);
ret = request_irq(IRQ_TIMER0, timer0_Transmit_irq_handle,
DEVICE_NAME"IRQ_TIMER0", Transmit_Buf);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Transmit_Buf);
return -EBUSY;
}
SA_INTERRUPT,
ret = request_irq(IRQ_TIMER1, pwm_irq_handle, SA_INTERRUPT, DEVICE_NAME"IRQ_TIMER1",
NULL);
if (ret != 0){
printk("<1> IR busy\n");
kfree(Transmit_Buf);
free_irq(IRQ_TIMER0, Transmit_Buf);
return -EBUSY;
}
Transmit_flag = 1;
PWMEn = 1;
TLen = count/2;
TCNTB0 = 0xffff - *Transmit_Buf;
Transmit_Index = 1;
TCON = (TCON & 0xffffff0) | 0x2; //Update TCNTB0, TCMPB0
TCON = (TCON & 0xffffff0) | 0x1; //Start for Timer 0
TCON = (TCON & 0xffff0ff) | 0xa00;
//Timer1:auto reload; Inverter off; Update TCNTB1,TCMPB1; Stop
TCON = (TCON & 0xffff0ff) | 0x900; //Start for Timer 1
interruptible_sleep_on_timeout(&Wait_Transmit,20);//9+4.5+2.24*32+0.56=85.74
TCON &= 0xffff000;
free_irq(IRQ_TIMER0, Transmit_Buf);
free_irq(IRQ_TIMER1, NULL);
kfree(Transmit_Buf);
return count;
}
static int IR_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long
arg){
static unsigned char channel = 0;
if (arg < 4){
switch (cmd){
case 0:
channel &= ~(0x1 << arg);
break;
case 1:
channel |= (0x1 << arg);
break;
case 2:
channel = 0;
break;
case 3:
channel = 0xf;
break;
default:
break;
}
writeb(channel, vIR_ADDR);
}
}
static struct file_operations IR_fops = {
owner:THIS_MODULE,
write:IR_Transmit,
read: IR_Study,
ioctl:IR_ioctl,
};
static devfs_handle_t devfs_handle;
static int __init IR_init(void){
devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT, BUZZ_MAJOR, 0,
S_IFCHR | S_IRUSR | S_IWUSR, &IR_fops, NULL);
set_gpio_ctrl(GPIO_B1 | GPIO_PULLUP_DIS | GPIO_MODE_TOUT);//TOUT1
set_external_irq(IRQ_IRInput, EXT_BOTH_EDGES, GPIO_PULLUP_DIS);
TCFG0 |= TCFG0_PRE0(1); //Timer0,1 prescale value = 1
TCFG1 = TCFG1 & (~(0xff << 0)) | (0x2 << 0) | (0x2 << 4);
//Timer0,1 divider value = 8, 50MHz/(1+1)/8=3.125MHz
TCNTB0 = 0xffff; //21ms
TCNTB1 = 0x52; //3.125MHz/38K=82=0x52;
TCMPB1 = 0x29; //0x52/2=0x29
TCON &= ~(0xfff << 0); //Timer0,1 Stop
Learning = 0;
Transmit_flag = 0;
PWMEn = 0;
vIR_ADDR = ioremap(pIR_ADDR, 1);
printk(KER F DEVICE_NAME ": init OK\n");
return(0);
}
static void __exit IR_cleanup(void){
iounmap(vIR_ADDR);
devfs_unregister(devfs_handle);
}
module_init(IR_init);
module_exit(IR_cleanup);
MODULE_LICENSE("GPL");
3.驱动测试程序
#include
#include
#include
#include
#include
int main(int argc,char *argv[]){
int fd, IR_fd;
int ret, i;
unsigned short buf[100], TBuf[100];
fd = open("/dev/PGM_IR", O_RDWR);
if (fd < 0){
perror("open PGM_IR fail");
exit(1);
}
ioctl(fd, 3, 0);
if (argc == 2){
IR_fd = open("/tmp/IR_fd0", O_RDONLY);
if (IR_fd < 0){
perror("open IR_fd0");
}
else{
ret = read(IR_fd, TBuf, sizeof(TBuf));
if (ret > 0){
for (i = 0; i < ret/2; i++){
if (!(i%10)) printf("\n");
printf("%04x,", TBuf);
}
}
write(fd, TBuf, ret);
}
}
ret = read(fd, buf, sizeof(buf)) / 2;
if (ret > 0){
for (i = 0; i < ret; i++){
if (!(i%10)){
printf("\n");
}
printf("%04x,", buf);
}
IR_fd = creat("/tmp/IR_fd0", O_RDWR);
if (IR_fd > 0){
printf("creat IR_fd sucess\n");
write(IR_fd, buf, sizeof(short) * ret);
}
else{
perror("IR_fd");
}
}
else{
perror("read PGM_IR");
}
close(fd);
return 0;
}
测试方法:
1> 测试红外学习时直接运行可执行文件即可,然后按住红外遥控,学习完后,将打印出红外学习数据,
然后创建"IR_fd0"文件,将数据保存。
2> 测试红外发射,在执行文件时,需给传送任一参数,如"# ./IR_test 1"。它将 IR_fd0 中的红外
数据发射出去。
4.出现的问题
用
"
request_irq(IRQ_TIMER0,
timer0_Transmit_irq_handle,
SA_INTERRUPT,
DEVICE_NAME"IRQ_TIMER0", Transmit_Buf);"申请中断时,但用"free_irq(IRQ_TIMER0, NULL);"不能
释放中断(可用"#cat /proc/interrupts"命令观察)。
必须使 free_irq 中的 dev_id 参数与 request_irq 中的该参数保持一至,即:free_irq(IRQ_TIMER0,
Transmit_Buf);
5.总结
此驱动与上面几个驱动有几点不同,主要表现在对中断的处理
1> 申请中断和释放中断都直接在 read(红外学习)、write(红外发射)系统函数里完成。原因主要考虑
到红外学习与发射都需要阻塞等待(sleep_on 的系列函数),但在阻塞的过程中又不允许其它进程也对红外
进行操作(因为它们都占用了相同的硬件资源,如定时器 0 等)。所以为了实现红外驱动的互斥,当进行应
用用户调用红外学习或发射时才申请中断,如果中断申请失败,说明已被占用,则出错退出。
2> 同 一 中 断 有 两 个 不 同 的 中 断 处 理 程 序 。 红 外 学 习 时 , 调 用 request_irq(IRQ_TIMER0,
timer0_Study_irq_handle, SA_INTERRUPT, DEVICE_NAME"IRQ_TIMER0_Study", NULL); 红 外 发 射 时 调 用
request_irq(IRQ_TIMER0, timer0_Transmit_irq_handle, SA_INTERRUPT, DEVICE_NAME"IRQ_TIMER0",
Transmit_Buf);这样比共用一个处理程序的可读性和效率更高。
3> 在申请中断时 request_irq 中的 void *dev_id 参数作为私有数据(红外数据指针)传递给中断处
理程序的该参数,参考上面各中断处理程序。
[/code]
展开》
折叠》