由于设计的需求,原本是想要利用PWM的捕获中断,进行中断的定义,但是研究了一会,发现PWM一直进不了中断状态。
但是在无意中发现,GPIO中也有中断函数。
因此萌发出利用GPIO获取PWM的输入波形,从而进入中断状态。
但是官方wujian100给出的中断vic案例中只是简单利用GPIO中断直接跳出循环,并没有涉及连续边沿中断的应用。
于是我修改了代码,利用PWM输入GPIO口,想产生1s的中断尝试一下,发现中断间隔并不是1s。
因此调试观察GPIO的中断情况,发现当产生中断后,GPIO中断清除寄存器不能将中断清除,导致无限的进入中断。
阅读手册,获得GPIO基础地址为base = 0x60018000。
手册中找到清除中断位原理,为在Clear interrupt寄存器中写入1
在c项目中GPIO口调用的中断位清除函数定义。
static void gpio_irq_clear(gpio_pin_handle_t pin, uint32_t idx)
{
wj_oip_gpio_pin_priv_t *gpio_pin_priv = pin;
/* convert portidx to port handle */
wj_oip_gpio_priv_t *port_handle = &gpio_handle[gpio_pin_priv->portidx];
wj_oip_gpio_control_reg_t *gpio_control_reg = (wj_oip_gpio_control_reg_t *)(port_handle->base + 0x30);
gpio_control_reg->PORTA_EOI = idx; //清除中断
}
观察发现其是在 gpio_control_reg->PORTA_EOI 对应的GPIO口写入1使其执行中断清除(对应手册的清除中断寄存器)。
但是经调试发现写入之后并不能清除中断状态,导致无限进入中断循环状态。
寄存器结构体定义
typedef struct {
__IOM uint32_t INTEN; /* Offset: 0x000 (W/R) Interrupt enable register */
__IOM uint32_t INTMASK; /* Offset: 0x004 (W/R) Interrupt mask register */
__IOM uint32_t INTTYPE_LEVEL; /* Offset: 0x008 (W/R) Interrupt level register */
__IOM uint32_t INT_POLARITY; /* Offset: 0x00c (W/R) Interrupt polarity register */
__IO uint32_t INTSTATUS; /* Offset: 0x010 (R) Interrupt status of Port */
__IM uint32_t RAWINTSTATUS; /* Offset: 0x014 (W/R) Raw interrupt status of Port */
__IOM uint32_t revreg1; /* Offset: 0x018 (W/R) Reserve register */
__OM uint32_t PORTA_EOI; /* Offset: 0x01c (W/R) Port clear interrupt register */
__IM uint32_t EXT_PORTA; /* Offset: 0x020 (W/R) PortA external port register */
__IM uint32_t EXT_PORTB; /* Offset: 0x024 (W/R) PortB external port register */
__IOM uint32_t revreg2[2]; /* Offset: 0x028 (W/R) Reserve register */
__IOM uint32_t LS_SYNC; /* Offset: 0x030 (W/R) Level-sensitive synchronization enable register */
} wj_oip_gpio_control_reg_t;
在一次尝试中,发现在中断函数中进行中断关闭与重开,能够成功进出中断,自定义中断函数如下。
static void gpio_interrupt_handler(int32_t idx)
{
printf("success");
csi_gpio_pin_set_irq(pin, GPIO_IRQ_MODE_FALLING_EDGE, 0);
csi_gpio_pin_set_irq(pin, GPIO_IRQ_MODE_FALLING_EDGE, 1);
}
于是观察csi_gpio_pin_set_irq(),发现其涉及了中断触发模式的设置与对中断的使能控制。
int32_t csi_gpio_pin_set_irq(gpio_pin_handle_t handle, gpio_irq_mode_e mode, bool enable)
{
GPIO_NULL_PARAM_CHK(handle);
uint32_t ret = 0;
if (enable) {
ret = gpio_set_irq_mode(handle, mode);
if (ret) {
return ret;
}
gpio_irq_enable(handle); //开启中断
} else {
gpio_irq_disable(handle); //关闭中断
}
return ret;
}
于是对中断使能函数进行探究,发现其与清除中断的原理相似,只是功能上是对中断的关闭与开启。
gpio_irq_enable(handle); //开启中断
gpio_irq_disable(handle); //关闭中断
于是修改void wj_oip_gpio_irqhandler(int idx)中的内容,使其每次对中断进行重启,实现中断点清除。
wj_oip_gpio_control_reg_t *gpio_control_reg = (wj_oip_gpio_control_reg_t *)(gpio_handle[idx].base + 0x30);
获取寄存器结构的地址 = GPIO_BASE地址 + 偏移量(0x30),可以在手册中查询
修改代码内容如下,部分省略,主要修改了增加变量value_en、注释代码行 gpio_irq_clear(gpio_pin_priv, (1 << i))以及对使能中断寄存器的关闭重启。
void wj_oip_gpio_irqhandler(int idx)
{
......
wj_oip_gpio_control_reg_t *gpio_control_reg = (wj_oip_gpio_control_reg_t *)(gpio_handle[idx].base + 0x30);
uint32_t value = gpio_control_reg->INTSTATUS;
uint32_t value_en = gpio_control_reg->INTEN;
......
// gpio_irq_clear(gpio_pin_priv, (1 << i)); //clear the gpio interrupt
value_en |= (1 << i);
gpio_control_reg->INTEN &= ~value_en;
gpio_control_reg->INTEN |= value_en;
......
}
最后运行原先编写的中断程序,成功实现1s的中断计时。
由于收到板子的时间只有2、3天并且项目期限的原因,没有时间去解决PWM的中断捕获与GPIO中断清除寄存器的内部问题,但是项目完成之后,如果工具允许将再次深入研究。
第一次发表文章,有错误请多多包涵!!!