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: