Introduction:
I bought a Longan Nano evaluation board equipped with a GD32VF103 Risc-V MCU.
I can run the video player demo, and compile and load working code using VS Code, PlatformIO and the DFU tool. Below a video of the board and the demo running.
https://www.youtube.com/watch?v=84_PzcNiJb4
What I want to do
I'm building a robot for a robot competition as an hobby. I used a Microchip 4809 8bit MCU for the motor controller, but I hit the limit of the MCU in running the PID controller at 2KHz and I have yet to add the current loops. I wanted to upgrade the motor controller and I decided to pick up the Longan Nano because of the LCD screen, the vastly superior CPU horsepower and to learn Risc-V.
https://www.youtube.com/watch?v=1dQMktoiuLg
Problem
I can run the pheriperals of the Longan Nano just fine in polling. I tried REALLY hard to make the interrupts work to no avail. I tried to read in polling the interrupt flags and they work that way, so I think its either a linker problem in linking the ISR handler to te Interrupt Vector table in Start.s or a configuration problem with the ECLIC.
This is a polling example that runs correctly. The red led blinks at 2Hz and pressing the boot button will toggle the blue led.
#include <gd32vf103.h>
void init()
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOC);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_1);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ,GPIO_PIN_8);
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_13);
gpio_bit_set(GPIOC,GPIO_PIN_13);
gpio_bit_set(GPIOA,GPIO_PIN_1);
gpio_bit_set(GPIOA,GPIO_PIN_2);
rcu_periph_clock_enable(RCU_AF);
eclic_init(ECLIC_NUM_INTERRUPTS);
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
eclic_irq_enable(EXTI5_9_IRQn, 1, 1);
exti_deinit();
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_8);
exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
//Clear interrupt flags
exti_interrupt_flag_clear(EXTI_8);
eclic_clear_pending(EXTI5_9_IRQn);
eclic_global_interrupt_enable();
return;
}
void EXTI5_9_IRQHandler()
{
}
void delay_us(unsigned int us)
{
uint64_t start_mtime, delta_mtime;
// Don't start measuring until we see an mtime tick
uint64_t tmp = get_timer_value();
do
{
start_mtime = get_timer_value();
}
while (start_mtime == tmp);
do
{
delta_mtime = get_timer_value() - start_mtime;
}
while(delta_mtime <(SystemCoreClock/4000000.0 *us ));
return;
}
int main()
{
init();
while (true)
{
gpio_bit_write(GPIOC, GPIO_PIN_13, (bit_status)(1-gpio_input_bit_get(GPIOC, GPIO_PIN_13)));
delay_us(250000);
//This correctly detects the EXTI8. Only a single acquisition at beginning
if (exti_interrupt_flag_get(EXTI_8) != RESET)
{
gpio_bit_write(GPIOA, GPIO_PIN_1, (bit_status)(1-gpio_input_bit_get(GPIOA, GPIO_PIN_1)));
exti_interrupt_flag_clear(EXTI_8);
eclic_clear_pending(EXTI5_9_IRQn);
}
}
return 0;
}
This is an interrupt example that doesn't work. And I can't figure out why.
#include <gd32vf103.h>
void init()
{
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOC);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_1);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ,GPIO_PIN_8);
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_13);
gpio_bit_set(GPIOC,GPIO_PIN_13);
gpio_bit_set(GPIOA,GPIO_PIN_1);
gpio_bit_set(GPIOA,GPIO_PIN_2);
rcu_periph_clock_enable(RCU_AF);
eclic_init(ECLIC_NUM_INTERRUPTS);
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
eclic_irq_enable(EXTI5_9_IRQn, 1, 1);
exti_deinit();
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_8);
exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
//Clear interrupt flags
exti_interrupt_flag_clear(EXTI_8);
eclic_clear_pending(EXTI5_9_IRQn);
eclic_global_interrupt_enable();
return;
}
void exti_5_9_handler( void )
{
if (exti_interrupt_flag_get(EXTI_8) != RESET)
{
exti_interrupt_flag_clear(EXTI_8);
eclic_clear_pending(EXTI5_9_IRQn);
gpio_bit_write(GPIOA, GPIO_PIN_1, (bit_status)(1-gpio_input_bit_get(GPIOA, GPIO_PIN_1)));
}
else
{
gpio_bit_write(GPIOA, GPIO_PIN_2, (bit_status)(1-gpio_input_bit_get(GPIOA, GPIO_PIN_2)));
}
return;
}
void EXTI5_9_IRQHandler()
{
exti_5_9_handler();
}
void delay_us(unsigned int us)
{
uint64_t start_mtime, delta_mtime;
// Don't start measuring until we see an mtime tick
uint64_t tmp = get_timer_value();
do
{
start_mtime = get_timer_value();
}
while (start_mtime == tmp);
do
{
delta_mtime = get_timer_value() - start_mtime;
}
while(delta_mtime <(SystemCoreClock/4000000.0 *us ));
return;
}
int main()
{
init();
while (true)
{
gpio_bit_write(GPIOC, GPIO_PIN_13, (bit_status)(1-gpio_input_bit_get(GPIOC, GPIO_PIN_13)));
delay_us(250000);
}
return 0;
}
Question
I'd like help in making the interrupts on the Longan Nano GD32VF103 work
Solution
The problem was that the C++ compiler decided it was a good idea to change the name of the interrupt handler, and the platform.io GD32VF103 toolchain relies on an interrupt vector table with .weak symbols in Start.S that only links the interrupt handlers correctly if they are called with a special name.
#include "riscv_encoding.h"
.section .init
.weak eclic_msip_handler
.weak eclic_mtip_handler
~more interrupt vector table enties~
.weak CAN0_EWMC_IRQHandler
.weak EXTI5_9_IRQHandler
The solution is to use the keyword extern "C" before the interrupt service routine so that the C++ compiler does not mess up with the name and the linker can automagically use its address in the interrupt vector table entry
#include <gd32vf103.h>
#define EVER (;;)
void init()
{
//Clock the GPIO banks
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOC);
//Setup the R, G and B LEDs
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_1);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ,GPIO_PIN_8);
//Setup the boot button
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_13);
//Initialize the LEDs to: OFF
gpio_bit_set(GPIOC,GPIO_PIN_13);
gpio_bit_set(GPIOA,GPIO_PIN_1);
gpio_bit_set(GPIOA,GPIO_PIN_2);
//Clock the alternate functions
rcu_periph_clock_enable(RCU_AF);
//Initialize the ECLIC IRQ lines
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
eclic_irq_enable(EXTI5_9_IRQn, 1, 1);
//Initialize the EXTI. IRQ can be generated from GPIO edge detectors
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_8);
exti_init(EXTI_8, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
//Clear interrupt flag. Ensure no spurious execution at start
exti_interrupt_flag_clear(EXTI_8);
//Enable the interrupts. From now on interrupt handlers can be executed
eclic_global_interrupt_enable();
return;
}
extern "C"
void EXTI5_9_IRQHandler()
{
//If: interrupt from PA8 boot button
if (exti_interrupt_flag_get(EXTI_8) != RESET)
{
//Clear the interrupt from PA8 boot button
exti_interrupt_flag_clear(EXTI_8);
//Toggle the blue led
gpio_bit_write(GPIOA, GPIO_PIN_2, (bit_status)(1-gpio_input_bit_get(GPIOA, GPIO_PIN_2)));
}
//Default: interrupt from an unhandled GPIO
else
{
//Do nothing (should clear the interrupt flags)
}
}
void delay_us(unsigned int us)
{
uint64_t start_mtime, delta_mtime;
uint64_t tmp = get_timer_value();
do
{
start_mtime = get_timer_value();
}
while (start_mtime == tmp);
do
{
delta_mtime = get_timer_value() - start_mtime;
}
while(delta_mtime <(SystemCoreClock/4000000.0 *us ));
return;
}
int main()
{
init();
for EVER
{
//Toggle the RED LED
gpio_bit_write(GPIOC, GPIO_PIN_13, (bit_status)(1-gpio_input_bit_get(GPIOC, GPIO_PIN_13)));
//2Hz blink
delay_us(250000);
}
return 0;
}
Video of the example running. Boot button toggles the blue/green LED through an ISR
https://www.youtube.com/watch?v=f135I4lzgCA
Acknowledgement
Thanks for the help! Making the ISR work was driving me mad :)
EXTI5_9_IRQHandler
in start.S, but due to name mangling it may not end up being that name after compilation, resulting in a mismatch. – Billibilliardextern "C"
in front ofvoid EXTI5_9_IRQHandler(void)
and retest. If GCC is mangling your ISR, adding theextern "C"
specifier will prevent any name mangling. – Billibilliard