MicroBlaze中断处理程序未触发:PS/2鼠标数据包捕获求助
PS/2鼠标中断未触发问题排查
问题背景
在嵌入式系统中需捕获PS/2模式初始化的鼠标移动数据包,但中断处理程序始终未被触发。已配置处理器启用外部中断,仅将一个信号连接至处理器的中断输入端口,未使用中断控制器,采用软件循环缓冲区来捕获输入移动。
相关代码
C++ PS/2核心类实现
#include "xparameters.h" #include "xil_exception.h" #include "ps2_core.h" Ps2Core::Ps2Core(uint32_t core_base_addr) { base_addr = core_base_addr; registerCallback(); } Ps2Core::~Ps2Core() { } void Ps2Core::enqueue(unsigned char value) { if ((tail + 1) % QUEUE_SIZE == head) { // queue is full, do nothing return; } queue[tail] = value; queueCount++; tail = (tail + 1) % QUEUE_SIZE; } unsigned char Ps2Core::dequeue(void) { if (head == tail) { // queue is empty, do nothing return 0; } unsigned char value = queue[head]; queueCount--; head = (head + 1) % QUEUE_SIZE; return value; } void Ps2Core::handleInterrupt(Ps2Core *ps2) { uint8_t byte; byte = ps2->rx_byte(); ps2->enqueue(byte); } void Ps2Core::registerCallback(){ Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)handleInterrupt, this); Xil_ExceptionEnable(); } int Ps2Core::get_mouse_activity(int *lbtn, int *rbtn, int *xmov, int *ymov, int *zmov) { uint8_t b1, b2, b3, b4; uint32_t tmp; /* retrieve bytes only if 4 or a multiple of 4 exist in queue */ if (queueCount >= 4) { b1 = dequeue(); b2 = dequeue(); b3 = dequeue(); b4 = dequeue(); } else return (0); /* extract button info */ *lbtn = (int) (b1 & 0x01); // extract bit 0 *rbtn = (int) (b1 & 0x02) >> 1; // extract bit 1 /* extract x movement; manually convert 9-bit 2's comp to int */ tmp = (uint32_t) b2; if (b1 & 0x10) // check MSB (sign bit) of x movement tmp = tmp | 0xffffff00; // manual sign-extension if negative *xmov = (int) tmp; // data conversion /* extract y movement; manually convert 9-bit 2's comp to int */ tmp = (uint32_t) b3; if (b1 & 0x20) // check MSB (sign bit) of y movement tmp = tmp | 0xffffff00; // manual sign-extension if negative *ymov = (int) tmp; // data conversion tmp = (uint32_t) b4; if (b4 & 0x08) // check MSB (sign bit) of z movement tmp = tmp | 0xfffffff0; // manual sign-extension if negative *zmov = (int) tmp; // data conversion /* success */ return (1); }
Verilog模块实例
//instantiate uBlaze MCS cpu cpu_unit ( .Clk(clk_100M), .Reset(reset), .IO_addr_strobe(io_addr_strobe), .IO_address(io_address), .IO_byte_enable(io_byte_enable), .IO_read_data(io_read_data), .IO_read_strobe(io_read_strobe), .IO_ready(io_ready), .IO_write_data(io_write_data), .IO_write_strobe(io_write_strobe), .INTC_Interrupt(interrupt) ); // instantiated i/o subsystem mmio_sys_sampler_arty_a7 mmio_unit ( .clk(clk_100M), .reset(reset), .mmio_cs(fp_mmio_cs), .mmio_wr(fp_wr), .mmio_rd(fp_rd), .mmio_addr(fp_addr), .mmio_wr_data(fp_wr_data), .mmio_rd_data(fp_rd_data), .rx_done_tick(interrupt), .ps2d_in(ps2d_in), .ps2c_in(ps2c_in), .tri_c(tri_c), .tri_d(tri_d), .ps2c_out(ps2c_out), .ps2d_out(ps2d_out), .rx(rx), .tx(tx) );
关键排查点与修复建议
- 中断触发配置匹配:确认
rx_done_tick的有效电平/边沿与uBlaze外部中断的触发设置一致。比如uBlaze默认外部中断为高电平触发,若rx_done_tick是上升沿脉冲,需修改处理器中断配置为边沿触发。 - 处理器中断使能:除了调用
Xil_ExceptionEnable(),需通过指令手动使能uBlaze的外部中断:// 启用uBlaze外部中断 mtspr(SPR_MSR, mfspr(SPR_MSR) | MSR_EE); - 中断信号同步:在Verilog的
mmio_sys_sampler_arty_a7模块中,对rx_done_tick添加两级同步寄存器,避免亚稳态:reg [1:0] rx_done_sync; always @(posedge clk or posedge reset) begin if(reset) begin rx_done_sync <= 2'b00; end else begin rx_done_sync <= {rx_done_sync[0], rx_done_tick_raw}; end end assign interrupt = rx_done_sync[1]; - 回调函数签名修正:调整中断回调函数的签名以匹配Xilinx异常框架要求,避免参数传递错误:
static void handleInterrupt(void *data) { Ps2Core *ps2 = static_cast<Ps2Core*>(data); uint8_t byte = ps2->rx_byte(); ps2->enqueue(byte); } - 验证PS/2模块信号:使用FPGA内部逻辑分析仪(ILA)观测
rx_done_tick、ps2d_in、ps2c_in信号,确认鼠标移动时rx_done_tick是否产生有效脉冲,排查PS/2模块本身的数据包解析问题。 - 并发访问保护:在中断回调和
get_mouse_activity函数中,对循环缓冲区的操作添加中断禁用/启用保护,避免数据竞争:void Ps2Core::enqueue(unsigned char value) { Xil_ExceptionDisable(); if ((tail + 1) % QUEUE_SIZE != head) { queue[tail] = value; queueCount++; tail = (tail + 1) % QUEUE_SIZE; } Xil_ExceptionEnable(); }
内容的提问来源于stack exchange,提问作者Vahe
相关产品推荐
相关产品推荐

