中断
中断可以看作是一种“硬件轮询”。即本质上,CPU会通过读取外部信号来判断CPU的下一步状态。
所谓的“硬件轮询”可以用单片机理解
效果:当按键按下时,蜂鸣器就会响,不松开就一直响,松开关闭。
(主程序死循环,通过CPU中断可以暂时中断,先执行其他任务,然后再回来继续执行被中断的任务。例子中开启外部中断0,中断方式为下降沿触发,比如控制端口原来是高电平的,按下按钮使控制端口变为低电平,高电平到低电平有个下降过程,下降沿触发中断0,调用中断服务处理函数,蜂鸣器响。中断服务处理函数有个循环检测直到按钮释放才关闭蜂鸣器,退出中断服务处理函数,控制端口恢复为高电平。)
中断处理方式
设备管理中,高速的处理器和低速的输入输出设备相对来说,会降低整体效率,为了减少程序直接控制方式中CPU的等待时间,提高系统的并行工作程度,采用中断处理方式是很有必要的。
在I/O设备中断方式下,CPU与I/O设备之间数据的传输步骤如下:
1.在某个进程需要数据时,发出指令启动输入输出设备,准备要处理的数据;
2.在进程发出指令启动设备之后,该进程放弃处理器,等待相关I/O操作完成。此时,进程调度程序会调度其他就绪进程使用处理器。
3.当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转向预先设计好的中断处理程序,对数据传送工作进行相应的处理。
4.得到了数据的进程,转入就绪状态。在随后的某个时刻,进程调度程序会选中该进程继续工作。
那么发生中断时CPU怎么知道程序的地址?
通过映射的关系找到对应程序的地址。
例如在x86架构中,中断向量表的位置保存在IDTR寄存器里,CPU通过这个寄存器就能找到中断向量表,然后根据中断号就可以找到具体的中断入口了
中断方式的优缺点
优点:
I/O设备中断方式使处理器的利用率显著提高;
支持多道程序和I/O设备的并行操作,提高了效率。
缺点:
各种各样的输入输出设备通过中断处理方式进行并行操作,使中断次数增加,会造成CPU无法响应中断;
如果在缓冲区装满数据之后发生中断。那么在数据传送过程中,发生中断的机会较多,将耗去大量的CPU处理时间。
IIC中断方式驱动PCA9534
其中初始化的两个函数分别是
Config = XIic_LookupConfig(DeviceID);
Config = XIic_CfgInitialize(DeviceID);
具体的配置是
XIic_Config XIic_ConfigTable[XPAR_XIIC_NUM_INSTANCES] =
{
{
XPAR_AXI_IIC_0_DEVICE_ID,
XPAR_AXI_IIC_0_BASEADDR,
XPAR_AXI_IIC_0_TEN_BIT_ADR,
XPAR_AXI_IIC_0_GPO_WIDTH
},
{
XPAR_AXI_IIC_1_DEVICE_ID,
XPAR_AXI_IIC_1_BASEADDR,
XPAR_AXI_IIC_1_TEN_BIT_ADR,
XPAR_AXI_IIC_1_GPO_WIDTH
}
};
/*
* Set default values and configuration data, including setting the
* callback handlers to stubs so the system will not crash should the
* application not assign its own callbacks.
*/
InstancePtr->IsStarted = 0;
InstancePtr->BaseAddress = EffectiveAddr;
InstancePtr->RecvHandler = XIic_StubHandler;
InstancePtr->RecvBufferPtr = NULL;
InstancePtr->SendHandler = XIic_StubHandler;
InstancePtr->SendBufferPtr = NULL;
InstancePtr->StatusHandler = XIic_StubStatusHandler;
InstancePtr->Has10BitAddr = Config->Has10BitAddr;
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
InstancePtr->Options = 0;
InstancePtr->BNBOnly = FALSE;
InstancePtr->GpOutWidth = Config->GpOutWidth;
InstancePtr->IsDynamic = FALSE;
InstancePtr->IsSlaveSetAckOff = FALSE;
/*
* Reset the device.
*/
XIic_Reset(InstancePtr);
XIic_ClearStats(InstancePtr);
其中ConfigTable中的一些及地址是在我们配置完BlockDesign后VIVADO自动生成的,一般不会有什么差错,但是在调试的过程中如果出现调试不通的情况,可以在当作备选项检查一下。CfgInitialize()的核心是将寄存器的值进行复位。
i2c_reg8_write16() 是我们自己封装的一个函数,其主要作用是将要发送的地址和数据放入sent_buf 后,再调用 *XIic_Send(UINTPTR BaseAddress, u8 Address u8 BufferPtr, unsigned ByteCount, u8 Option) 这个函数中。有关基于中断的IIC发送数据的程序有很多,但是核心基本上都是这个函数。
这个函数内部做了许多操作,比如等待BUS free,将地址写入待发送的fifo并表明接下来是写操作,配置中断(清楚中断的锁存状态,向中断寄存器中写入数据),发送数据(send_data()),释放总线等操作。
PCA9534操作简述
PCA9534A 包含一个 8 位配置(输入或输出可选)、输入端口、输出端口和极性反转(高电平有效或低电平有效)寄存器。在加电时,I/O 被配置为输入。但是,系统主控制器可以通过写入 I/O 配置位将 I/O 启用为输入或输出。每个输入或输出的数据均保存在相应的输入或输出寄存器中。输入端口寄存器的极性可借助极性反转寄存器进行转换。所有寄存器都可由系统主控器读取。
简单来说,PCA9534就是一个将IIC串行输入的数据转化为并行输出的数据。
为了将PCA9534的引脚配置为输出,需要向其命令字节写入11111111。这也是我们PCA9354_GPIO_sta_init() 所进行的操作。
轮询
轮询方式主要是每隔一段时间对各种设备进行轮询,查询设备有无处理要求,若有处理要求则进行相应处理。由此可见,若设备无处理要求,则 CPU 仍然会查询设备状态。而轮询的过程将会占据 CPU 的一部分处理时间,因此,程序轮询是一种效率较低的处理方式。
串口的轮询实现就是通过不停的轮询状态寄存器的状态判断是否有数据需要接收或发送。发送时先向数据寄存器或发送FIFO写入数据,然后不断检查状态寄存器,查看发送是否完成。发送完成才能发送下一字节或退出发送状态。对于接收则要先创建一个循环定时器或线程死循环,不断的轮询状态寄存器,检查是否有数据收到,有数据收到则读取数据,然后调用接收回调,将数据传递给上层应用。
轮询模式下,发送CPU耗时略大于数据在串口线上传输的时间,要知道串口的波特率相对于CPU主频来讲要慢百万倍以上。而接收时,轮询频率越高则无用的CPU耗时越多,轮询频率越低则接收响应时间越长,两者不能得兼。
其操作核心函数是