I/O操作函数inb, outb, inw, outw

; Copyright (C) 2011 Alen D. Archuleta (zeafoo@gmail.com) ;
设备和芯片的I/O端口操作实现,其实没有复杂的东西在里边 ; 
I/O端口操作主要是看一堆文档,把整个X86架构的PC机所有I/O端口记住, ;
并记住它们每一个数据寄存器、命令寄存器等操作访问标准(也可以称之协议) ; 
记住之后,整个过程中就是按标准使用I/O指令: ;
in, out(只能与DX,AX,AL寄存器结合使用) ;
下面的实现是提供给C使用,因为不太喜欢GNU的inline asm,语法太 ;
晦涩,所以直接使用汇编实现。 ;
inb 从I/O端口读取一个字节(BYTE, HALF-WORD) ;
outb 向I/O端口写入一个字节(BYTE, HALF-WORD) ;
inw 从I/O端口读取一个字(WORD,即两个字节) ;
outw 向I/O端口写入一个字(WORD,即两个字节) ;
byte inb(word port); ;
word inw(word port); ;
void outb(word port, byte value); ;
void outw(word port, word value); ;
编译: ;
nasm -f elf -o io.o io.asm ;
与内核一起链接使用 
global inb, outb, inw, outw 
[section .text] 
inb: 
xor eax, eax ; 在C语言中,都是以EAX寄存器作为返回值 
push dx ; 这个过程要对DX修改,所以先保存一下 
; 返回地址占加个字节,所以偏移4开始读取,第一个参数 
; 对于参数传递过程不作详细的记录,另写日志详细介绍 
mov dx, [esp + 4] 
in al, dx 
pop dx ; 恢复DX寄存器 
ret 
 
outb: 
push dx 
mov dx, [esp + 4] 
mov al, [esp + 6] 
out dx, al 
pop dx 
ret 
inw: 
xor eax, eax 
push dx 
 
mov dx, [esp + 4] 
in ax, dx 
pop dx 
ret 
 
outw: 
push dx 
mov dx, [esp + 4] 
mov ax, [esp + 6] 
out dx, ax 
 
pop dx 
ret 
来源:http://www.live83.cn/?p=103 
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include "reg932.h" #include <stdio.h> #include <stdarg.h> OUTA=0;OUTB=0;jma=1; pro(P0si_1,color_R); pyo_1=1; pyo_2=pyo_0=0;pyoy=0; // keypa=0; luma=2;//yoyma(ma,luma); if(yomp!=luma) {yomp=luma;ma1=1;} } //lu=2 报警标志 if((INA_2==1)&&(INA_1==1)) { pro(P0si_1,color_D); in1=0; dj=0; yomp=0; // 无报警1关灯 if(((INA_2==1)&&(INA_1==1))&&((INB_2==1)&&(INB_1==1))) //if((in1==0)&&(in2==0)) { FA=0;b=0;pyo_0=pyo_1=pyo_2=0;inkp1=0; pyo_0a=pyo_1a=pyo_2a=0;sk0=sk1=sk2=0; if(jmb==0) OUTA=1;OUTB=1; jma=0; } //无动作 S1=S2=S3 keypa=0; clrwdt() ; } /*********************第二路**********************************/ if((INB_2==0)&&(INB_1==1)) //预报警动作&&((INA_2==1)&&(INA_1==1)) { pro(P0si_3,color_G); //绿灯,警声指示 S2=S3=0;S1=1; if(jma==0) OUTA=1;OUTB=0; jmb=0; if(pyoy==1) {{pyoy=0;}} pyo_0=1; pyo_1=pyo_2=0; in2=1; //in1=0; // 进入报警1 if(inkp2==1){inkp2=0;keypa=0;} lumb=5;//yoyma(ma,lumb); if(yompb!=lumb) {yompb=lumb;ma1=0;} } if((INB_1==0)&&(INB_2==0)&&(keypa==1) ) //报警动作解除 { pyoy=1; inkp2=1; if(jma==0) OUTA=1;OUTB=0; jmb=0; pro(P0si_3,color_B); //蓝灯 pyo_2=1; pyo_1=pyo_0=0; lumb=7; //yoyma(ma,lumb); if(yompb!=lumb) {yompb=lumb;ma1=0;} } if((INB_1==0)&&(INB_2==0)&&(keypa==0) ) //报警 { OUTA=0;OUTB=0;jmb=1; pro(P0si_3,color_R); pyo_1=1; pyo_2=pyo_0=0;pyoy=0; // keypa=0; lumb=6;//yoyma(ma,lumb); if(yompb!=lumb) {yompb=lumb;ma1=0;} } if((INB_2==1)&&(INB_1==1)) // 无报警1关灯 { pro(P0si_3,color_D); in2=0; dj2=0; yompb=0; if(((INA_2==1)&&(INA_1==1))&&((INB_2==1)&&(INB_1==1))) //无动作 S1=S2=S3 keypa=0; { FA=0;b=0;pyo_0a=pyo_1a=pyo_2a=0; pyo_0=pyo_1=pyo_2=0;inkp2=0; sk0=sk1=sk2=0; if(jma==0) OUTA=1;OUTB=1; jmb=0; } } if(ma1==1) lu=luma; else {lu=lumb;} dj=2 ;yoy (lu,dj); if(FA==1) { if(++b==zma) {tp=1;b=0;FA=0;dj=0; } else {tp=0;FA=1;} } clrwdt() ; goto moa; } /*************************** * 系统初始化 * ****************************/ void SysInit() { uchar data duanma[4]={0X20,0X40,0X80,0 } ; //20-蓝色 40-绿色 20-红色 uchar dong[5]; uchar sd[1]={0x04}; uchar DpBuf[5]; uchar i,j,dj; P0M1=0xCF; // P0口设置P0.0,0.1,0.2,0.3,为输入; P0.6,P0.7 为输出开漏 11001111, P0M2=0xC0; //11000000 P1M1 = 0x0C; //P1口设置P1.2,P1.3,P1.4,P1.6,P1.7 为输出开漏 11011100 0C P1M2 = 0x0C; //110111000C P2M1 = 0xCF; //P2设置口P2.0,P2.1,P2.2,P2.3为输入; P2.6,P2.7 为输出开漏 11001111 P2M2 = 0xC0; //11000000 sk0=sk1=sk2=0; OUTA= OUTB= OUTC= OUTD=1; keyp9= keypa=keypb=0; pyo_0=pyo_1=pyo_2= pyo_0a=pyo_1a=pyo_2a=tp=0; pyo_0a=pyo_1a=pyo_2a=0; //zn=0; in1=in2=in3=in4=0; zma=b=0; dj=0; ISendStr(ZLG7290, 0x0d, sd, 1); for (i=0;i<4;i++) { DpBuf[i]=0; } ISendStr(ZLG7290, 0x10, DpBuf, 5); DelayNS(10); j=0; for (i=0;i<4;i++) { dong[0]=duanma[j]; //百位显示 dong[1]=duanma[j]; //十位显示 dong[2]=duanma[j]; //个位 dong[3]=duanma[j]; dong[4]=duanma[j]; ISendStr(ZLG7290, 0x10, dong, 5); DelayNS(25); j++; } init_wdt(); } /********************************************************* *名称:DelayNS() *功能:延时一段时间 *入口参数:no *出口参数:无 *********************************************************/ void DelayNS(uchar no) { uchar i,j; for(; no>0; no--) { for(i=0; i<200; i++) // clrwdt() ; for(j=0; j<250; j++); clrwdt() ; } } /******************************************************************* * 报警位数函数 *功能 根据输入参数一决定报警位数,颜色,继电器开关 *入口参数 sla 报警位数 * ys 颜色 jou 继电器开关 * 出口参数 函数返回1表示操作成功 否则操作有误 ********************************************************************/ bit pro(uchar wa, uchar ys) { uchar data duanma[4]={0X20,0X40,0X80,0 } ; uchar dong[1]; //先位,后颜色。 dong[0]=duanma[ys]; ISendStr(ZLG7290, 0x10+wa, dong, 1); return(1); } /******************************************************************* * 报警声音函数 *功能 根据输入参数一决定报警声音,预报警声音,报警声音,解除报警声音.延时计数 三路调用同一函数,根据pyo决定输出级别。每一路只能调用一次。 *入口参数 pyo 报警声 预报警声音,报警声音,解除报警声音. * ysa 延时 标志位 * 出口参数 函数返回1,表示延时计数结束, 否则延时计数未结束 ********************************************************************/ void yoy(uchar lu,uchar py1) { uchar pyo; pyo=lu; if((pyo==3)||((pyo==7))) //解除报警声音 { if (pyo_2a!=1) {pyo_2a=1;FA=1;tp=tp2=0;b=0; pyo_0a=0; } // 第一次进入标志位 if(tp==1) {sk2=0; FA=0;b=0;pyo_0a=0; } else {sk2=1;sk0=sk1=0;FA=1; } } if((pyo==2)||((pyo==6))) //报警声音 { if (pyo_1a!=1) {pyo_1a=1;FA=1;tp=0;b=0;pyo_0a=pyo_2a=0; } // 第一次进入标志位 pyo_0a=pyo_2a=0; if(tp==1) {sk1=0; FA=0;b=0; //pyo_0a=0; } else {sk1=1;sk0=sk2=0;FA=1;} // pyo_0a=0; } if((pyo==1)||((pyo==5))) //预报警 { if (pyo_0a!=1) {pyo_0a=1;FA=1;tp=0;b=0;pyo_1a=pyo_2a=0; } // 第一次进入标志位 pyo_0a=pyo_2a=0; if(tp==1) {sk0=0; FA=0;b=0; //pyo_0a=0; } else {sk0=1;sk1=sk2=0;FA=1;} // } if(((INA_2==1)&&(INA_1==1))&&((INB_2==1)&&(INB_1==1))) //if((pyo==4)||((pyo==8))) { sk0=sk1=sk2=0 ; FA=0;tp=0;b=0;pyo_0a=pyo_1a=pyo_2a=0;lu=0;ma=0; } } /******************************************************************* * 报警声音函数 *功能 根据输入参数一决定报警声音,预报警声音,报警声音,解除报警声音.延时计数 三路调用同一函数,根据pyo决定输出级别。每一路只能调用一次。 *入口参数 pyo 报警声 预报警声音,报警声音,解除报警声音. * ysa 延时 标志位 * 出口参数 函数返回1,表示延时计数结束, 否则延时计数未结束 ********************************************************************/ /* void yoyma(uchar ma,uchar luma) { ; //if(ma1==1)lu==luma; // else {lu==lu;} /* if(ma==0){ ma=1; lu=luma; } else{ if(lu==luma)ma=1; else ma=0; } } */ /******************************************************************* * 申请总线 * 功能:进行I2C总线的初始化--包括时钟选择,I2C使能,发送起始信号等等。 * I2EN为1,设置为主机;CRSEL位为0,使用内部SCL发生器。 *******************************************************************/ void GetBus() { I2SCLH = 15; /* 设置SCL高电平的PCLK周期数 */ I2SCLL = 15; /* 设置SCL低电平的PCLK周期数,6MHz时为100Kbit/S */ /* 申请成为主机,起动总线。使用内部SCL发生器,I2EN和AA置位。 */ I2CON = RELEASE_BUS_STA; while( SI==0 ); /* 等待起始位的发送 */ } /******************************************************************* * 发送数据函数 * 功能:用于向总线发送数据 * 入口参数:ACC 待发送的数据 *******************************************************************/ void SendByte(uchar c) { I2DAT = c; I2CON = RELEASE_BUS_ACK; /* 清除SI位等等 */ while( SI==0 ); /* 等待数据的发送 */ } /******************************************************************* * 向有子地址器件发送多字节数据函数 * 功能:从启动总线到发送地址,子地址,数据,结束总线的全过程。 * 入口参数;sla 从器件地址 * suba 子地址 * s 发送内容的指针 * no 发送字节数 * 出口参数:返回1表示操作成功,否则操作有误。 ********************************************************************/ bit ISendStr(uchar sla, uchar suba, uchar *s, uchar no) { uchar i; GetBus(); /* 启动总线 */ SendByte(sla); /* 发送器件地址 */ if( I2STAT!=0X18 ) { I2CON = GENERATE_STOP; return(0); } SendByte(suba); /* 发送器件子地址 */ if( I2STAT!=0X28 ) { I2CON = GENERATE_STOP; return(0); } for(i=0; i<no; i++) { SendByte(*s); /* 发送数据 */ if( I2STAT!=0X28 ) { I2CON = GENERATE_STOP; return(0); } s++; } I2CON = GENERATE_STOP; /* 结束总线 */ return(1); } /******************************************************************* * 向无子地址器件发送字节数据函数 * 功能 从启动总线到发送地址 数据 结束总线的全过程 * 入口参数 sla 从器件地址 * c 待发送的数据 * 出口参数 返回1表示操作成功 否则操作有误 ********************************************************************/ bit ISendByte(uchar sla, uchar c) { GetBus(); /* 启动总线 */ SendByte(sla); /* 发送器件地址 并接收应答位 */ if( I2STAT!=0X18 ) /* 无从机应答则退出操作 */ { I2CON = GENERATE_STOP; return(0); } SendByte(c); /* 发送数据 */ if( I2STAT!=0X28 ) { I2CON = GENERATE_STOP; return(0); } I2CON = GENERATE_STOP; /* 结束总线 */ return(1); } /******************************************************************* * 向有子地址器件读取多字节数据函数 *功能:从启动总线到发送地址,子地址,读数据,结束总线的全过程。 *入口参数:sla 从器件地址 * suba 子地址 * s 读出的内容存储区的指针 * no 读no个字节。 *出口参数:函数返回1表示操作成功,否则操作有误。 ********************************************************************/ bit IRcvStr(uchar sla,uchar suba,uchar *s,uchar no) { uchar i; GetBus(); /* 启动总线 */ SendByte(sla); /* 发送器件地址 */ if( I2STAT!=0X18) { I2CON = GENERATE_STOP; return(0); } SendByte(suba); /* 发送器件子地址 */ if( I2STAT!=0X28 ) { I2CON = GENERATE_STOP; return(0); } I2CON = RELEASE_BUS_STA; /* 重新启动总线 */ while( SI==0 ); SendByte(sla+1); if( I2STAT!=0X40 ) { I2CON = GENERATE_STOP; return(0); } for(i=0; i<no-1; i++) { I2CON = RELEASE_BUS_ACK; /* 接收一字节数据并发送应答位 */ while( SI==0 ); /* 等待接收数据 */ if( I2STAT!=0X50 ) { I2CON = GENERATE_STOP; return(0); } *s = I2DAT; /* 读取数据 */ s++; } I2CON = RELEASE_BUS_NOACK; /* 接收最后一字节数据并发送非应答位 */ while( SI==0 ); *s = I2DAT; I2CON = GENERATE_STOP; /* 结束总线 */ return(1); } /***************************** ;看门狗初始化子程序 ;注意初始化后启动看门狗 *****************************/ void init_wdt() { ACC = WDCON; //读取 WDT 控制寄存器 ACC = ACC|0x04; //置位 ACC.2 准备启动 WDL = 0x80; //设置 8 位倒计时器初值 WDCON = ACC; //启动 WDT WFEED1 = 0xA5; //清零第一部分 WFEED2 = 0x5A; //清零第二部分 } void clrwdt() { // EA = 0; //关闭中断 WFEED1 = 0xA5; //执行清零第一部分 WFEED2 = 0x5A; //执行清零第二部分 // EA = 1; //开中断 } /* void UART_Init() //6M频率 { P1M1&=0xfc; //把TXD和RXD设置 P1M2&=0xfc; SSTAT=0; //双缓冲禁止,中断禁 SCON=0x50; //串口模式1,接收使 BRGCON=0; BRGR1=0x02; //波特率9600bit/s BRGR0=0x61; BRGCON=3; //启动UART的波特率 } */
以下是一个简单的内核模块,实现了申请 I/O 端口、读写 I/O 端口、释放 I/O 端口并打印输出相关信息: ```c #include <linux/init.h> #include <linux/module.h> #include <linux/ioport.h> #include <asm/io.h> #define IOPORT_BASE 0x3f8 #define IOPORT_SIZE 8 static int __init ioport_init(void) { int ret; struct resource *port; printk(KERN_INFO "ioport: init\n"); /* 申请 I/O 端口 */ port = request_region(IOPORT_BASE, IOPORT_SIZE, "my_ioport"); if (port == NULL) { printk(KERN_ERR "ioport: request_region failed\n"); return -EBUSY; } /* 读写 I/O 端口 */ outb('H', IOPORT_BASE); outb('i', IOPORT_BASE + 1); printk(KERN_INFO "ioport: read from I/O port: %c%c\n", inb(IOPORT_BASE), inb(IOPORT_BASE + 1)); /* 释放 I/O 端口 */ release_region(IOPORT_BASE, IOPORT_SIZE); return 0; } static void __exit ioport_exit(void) { printk(KERN_INFO "ioport: exit\n"); } module_init(ioport_init); module_exit(ioport_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("ioport example module"); ``` 在该模块中,我们首先定义了 I/O 端口的基地址和大小。然后在 `ioport_init` 函数中,调用 `request_region` 函数申请 I/O 端口,如果申请失败则返回错误码。接着我们使用 `outb` 函数向 I/O 端口写入数据,使用 `inb` 函数读取 I/O 端口中的数据,并通过 printk 函数打印输出相关信息。最后,我们在 `ioport_exit` 函数中调用 `release_region` 函数释放 I/O 端口。 需要注意的是,在编写内核模块时需要特别注意安全性和稳定性,避免对系统造成不必要的损害。建议在进行实验前备份好系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值