Menu Close

RISC-V GPIO Interrupt Project Design (2)

2. Engineering code

 

Related reference articles:

RISC-V teaching plan

 

2.3. fii_gpio.c

#include <stdio.h>
#include <stdint.h>
#include "fii_gpio.h"
#include "platform.h"

//Function to implement GPIO configuration

void GPIO_IRQ_register( u32_t gpio_pin, E_GPIO_IRQ_SW gpio_sw,
E_IRQ_TRIGGER gpio_trigger, E_IRQ_TYPE gpio_type)
{
    if(gpio_trigger == IRQ_LEVEL)
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_TRIGGER) &= ~(1UL<<gpio_pin);        // level trigger
    else
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_TRIGGER) |= 1UL<<gpio_pin;           //edge trigger
        
    if(gpio_type == IRQ_NEG_LOW)
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_TYPE) &= ~(1UL<<gpio_pin);           // low level or falling edge
    else
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_TYPE) |= 1UL<<gpio_pin;              //High level or rising edge
        
    if(gpio_sw == GPIO_IRQ_DIS)
    {                   // disable interrupts     
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_ENA) &= ~(1UL<<gpio_pin);
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_MASK) &= ~(1UL<<gpio_pin);
    }
    else
    {              //Enable interrupts   
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_ENA) |= 1UL<<gpio_pin;
        GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_MASK) |= 1UL<<gpio_pin;
    }

return;

}

 

2.4. main.c

In the main function, each button or DIP switch has a corresponding interrupt handler. The following takes button 0 and switch 5 as examples to introduce the corresponding interrupt handler.

void button_0_handler(void) {
    // Disable GPIO interrupt mask, disable mask
    //The register address is the base address 0xf000_0000 + offset
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_MASK) &= ~(1 << BUTTON_0_OFFSET);

    // Clear the GPIO interrupt pending bit (by writing 1)
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_CLR) = 1 << BUTTON_0_OFFSET;

    // Enable GPIO interrupt mask, enable mask
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_MASK) |= 1 << BUTTON_0_OFFSET;

    printf ("FI: button 0 pressed... \r\n"); // print output 

    return;

};

void SW_5_handler(void) {
// Disable GPIO interrupt mask, disable mask
//The register address is the base address 0xf000_0000 + offset
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_MASK) &= ~(1 << SW_5_OFFSET);

// Clear the GPIO interrupt pending bit (by writing 1)
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_CLR) = 1 << SW_5_OFFSET;

// Enable GPIO interrupt mask, enable mask
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_MASK) |= 1 << SW_5_OFFSET;

    printf ("FI: sw 5 pressed... \r\n"); // print output 

    return;

};

 

//interrupt entry function

unsigned int handle_trap(unsigned int mcause, unsigned int epc)
{
    // Judging the type of interrupt, it is completed by comparing with the highest bit of mcause and the remaining 31 bits of mcause.
    if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT))
    {
        // External Machine-Level interrupt from PLIC
        handle_m_ext_interrupt();          //If it is an external interrupt
    }
    else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_TIMER))
    {
        // printf("mip = 0x%08x\n" , read_csr(mip) );
        // Machine-Level interrupt from TIMER
        handle_m_time_interrupt();     //If it is a timer interrupt
    }
    else       //Print out relevant debugging information   
    {
        printf("There is not any handle_trap available \r\n");
        printf("mstatus = 0x%08x\r\n" , read_csr(mstatus) );
        printf("mie = 0x%08x\r\n" , read_csr(mie) );
        printf("mcause = 0x%08x\r\n" , read_csr(mcause) );
        printf("mtvec = 0x%08x\r\n" , read_csr(mtvec));
    }

    return epc;

}

 

 

//PLIC interrupt processing entry

void handle_m_ext_interrupt(){
    volatile plic_source int_num;
    int_num = PLIC_claim_interrupt(&g_plic);           //return address, base address + claimed offset
    curr_IRQ_id = int_num;                             //Interrupt ID
    
    if ((int_num >=1 ) && (int_num < PLIC_NUM_INTERRUPTS)) {
        g_ext_interrupt_handlers[int_num]();               //Jump to the corresponding key/dip switch interrupt handler
    }
    else {                   // print output 
        printf ("FII: external IRQ num = %d is blank... \r\n", int_num);
    }

    PLIC_complete_interrupt(&g_plic, int_num);         // interrupt complete

}

 

//interrupt initialization function

void _init(void)
{
    time_cnt = 0;
    curr_IRQ_id = 0;        //The interrupt ID is initialized to 0

    printf("RiscV Program : Display segment number and invoke timer IRQ \r\n");

    // Set the button as input, GPIO D group
    CPUPIN_DIR(GPIO_D, BUT_D_0, GPIO_IN);
    CPUPIN_DIR(GPIO_D, BUT_D_1, GPIO_IN);
    CPUPIN_DIR(GPIO_D, BUT_D_2, GPIO_IN);

    // Set the DIP switch as input, GPIO A group
    CPUPIN_DIR(GPIO_A, SW_5, GPIO_IN);
    CPUPIN_DIR(GPIO_A, SW_6, GPIO_IN);
    CPUPIN_DIR(GPIO_A, SW_7, GPIO_IN);

    // disable all GPIO interrupts
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_ENA) = 0;
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_MASK) = 0;
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_TRIGGER) = 0;
    GPIO_REG(GPIO_IRQ_GRP + GPIO_IRQ_TYPE) = 0;

    /* Assign the address of the interrupt entry function to the mtvec register 
    (review the timer interrupt learned before) */

    write_csr(mtvec, &trap_entry);

    return;

}

 

 

//Set interrupt related registers

void IRQ_register (){
    // Disable all interrupts until interrupt setup is complete
    clear_csr(mie, MIP_MEIP); // disable external interrupt
    clear_csr(mie, MIP_MTIP); // disable timer interrupt

    //Create an interrupt handler for each external interrupt source
    for (int ii = 0; ii < PLIC_NUM_INTERRUPTS; ii ++){
        g_ext_interrupt_handlers[ii] = no_interrupt_handler;
    }

    //Create an interrupt handler for buttons (0-2), GPIO D
    g_ext_interrupt_handlers[INT_DEVICE_BUTTON_0] = button_0_handler;
    g_ext_interrupt_handlers[INT_DEVICE_BUTTON_1] = button_1_handler;
    g_ext_interrupt_handlers[INT_DEVICE_BUTTON_2] = button_2_handler;

    //Create an interrupt handler for the DIP switch (5-7), GPIO A
    g_ext_interrupt_handlers[INT_DEVICE_SW_5] = SW_5_handler;
    g_ext_interrupt_handlers[INT_DEVICE_SW_6] = SW_6_handler;
    g_ext_interrupt_handlers[INT_DEVICE_SW_7] = SW_7_handler;

    // Enable interrupts at both the PLIC level and the GPIO level, here is the PLIC enable
    enable_plic_int(INT_DEVICE_BUTTON_0);
    enable_plic_int(INT_DEVICE_BUTTON_1);
    enable_plic_int(INT_DEVICE_BUTTON_2);
    enable_plic_int(INT_DEVICE_SW_5);
    enable_plic_int(INT_DEVICE_SW_6);
    enable_plic_int(INT_DEVICE_SW_7);


    // The priority must be set to > 0 to effectively trigger the interrupt
    PLIC_set_priority(&g_plic, INT_DEVICE_BUTTON_0, 2);
    PLIC_set_priority(&g_plic, INT_DEVICE_BUTTON_1, 1);
    PLIC_set_priority(&g_plic, INT_DEVICE_BUTTON_2, 1);
    PLIC_set_priority(&g_plic, INT_DEVICE_SW_5, 4);
    PLIC_set_priority(&g_plic, INT_DEVICE_SW_6, 5);
    PLIC_set_priority(&g_plic, INT_DEVICE_SW_7, 1);

    // GPIO interrupt initialization, GPIO enable interrupt
    //Select interrupt type, edge/level interrupt, trigger condition
    GPIO_IRQ_register( BUTTON_0_OFFSET, GPIO_IRQ_EN, IRQ_EDGE, IRQ_NEG_LOW);
    GPIO_IRQ_register( BUTTON_1_OFFSET, GPIO_IRQ_EN, IRQ_EDGE, IRQ_NEG_LOW);
    GPIO_IRQ_register( BUTTON_2_OFFSET, GPIO_IRQ_EN, IRQ_LEVEL, IRQ_NEG_LOW);
    GPIO_IRQ_register( SW_5_OFFSET , GPIO_IRQ_EN, IRQ_EDGE, IRQ_NEG_LOW);
    GPIO_IRQ_register( SW_6_OFFSET , GPIO_IRQ_EN, IRQ_EDGE, IRQ_POS_HIGH);
    GPIO_IRQ_register( SW_7_OFFSET , GPIO_IRQ_EN, IRQ_LEVEL, IRQ_POS_HIGH);

    // GPIO interrupt module initialization ends
    //set timer interrupt
    set_timer(500);

    // enable mie external interrupt bit
    set_csr(mie, MIP_MEIP);

    // Enable mie timer interrupt bit
    set_csr(mie, MIP_MTIP);

    // Enable global interrupts, all interrupts in machine mode
    set_csr(mstatus, MSTATUS_MIE);

    // enable timer module
    TIME_REG( TM_CTRL_REG ) = 0x80000001;

}

 

// main function

int main(void)
{
    //The digital tube display variable after initialization
    int i = 0;
    unsigned int curr_seat = 0x01;
    unsigned int curr_seg = 0;
    unsigned char out_char = 0;

    //call the initialization function
    _init();

    // print output
    printf("\r\nRun Segment Timer IRQ Program \r\n");

    //Call PLIC_init function, struct pointer
    PLIC_init(  &g_plic,
                PLIC_CTRL_ADDR,
                PLIC_NUM_INTERRUPTS,
                PLIC_NUM_PRIORITIES);

    //Call the IRQ_register function
    IRQ_register();

    while ( 1 )           //The digital tube displays the interrupt ID 
    {
        display_seg(out_char, curr_seat);
        
        for(i = 0; i < NOP_DELAY ; i ++ )
            asm("nop");

        curr_seat = curr_seat << 1;
        
        if(curr_seat == 0x40)
        {
            curr_seg = curr_IRQ_id;
            curr_seat = 0x01;
        }
        else
            curr_seg = curr_seg >> 4;

        out_char = curr_seg & 0xf;
    }

}

 

Engineering code download:

 

Posted in FPGA, RISC-V, RISC-V Textbook, Textbook and Training Project

Related Articles

Leave a Reply

Your email address will not be published.

Leave the field below empty!