注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
mars4zhu的个人空间 http://home.eeworld.com.cn/space-uid-216477.html [收藏] [复制] [分享] [RSS]
日志

【Sipeed 高云GW2A FPGA开发板】——Cortex-M1软核处理器参考设计的例程测试记录

热度 1已有 365 次阅读2022-11-27 19:20 |个人分类:FPGA


5. 高云Cortex-M1软核处理器参考设计的例程测试记录

5.1. led

参见` Keil项目的设置、编译、下载与调试`,注意修改项目:

  1. Keil项目的Options选项设置,

  2. ext_debug.init修改,

  3. SYSTEM_CLOCK值的修改。

5.2. keyscan

设置同前。

代码流程:通过对按键进行扫描,实现流水灯效果:

int main(void)
{
        char i=0;

        SystemInit();//Initializes system
        GPIOInit();//Initializes gpio

  while(1)
  {
                if(Key_Scan(GPIO0,GPIO_Pin_4)==KEY_ON)
                {
                        GPIO_SetBit(GPIO0,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);

                        if(0==i)
                        {
                                GPIO_ResetBit(GPIO0,GPIO_Pin_0);
                                i++;
                        }
                        else if(1==i)
                        {
                                GPIO_ResetBit(GPIO0,GPIO_Pin_1);
                                i++;
                        }
                        else if(2==i)
                        {
                                GPIO_ResetBit(GPIO0,GPIO_Pin_2);
                                i++;
                        }
                        else if(3==i)
                        {
                                GPIO_ResetBit(GPIO0,GPIO_Pin_3);
                                i=0;
                        }
                }

                Delay(166666);
  }
}

运行效果如图:

5.3. gpio_i_int

该例程设置GPIO[7:4]的输入中断,并且在中断响应函数中,点亮对应的LED一段时间,如下:

//GPIO pin 4 interrupt handler
void GPIO0_4_Handler(void)
{
        GPIO_ResetBit(GPIO0, GPIO_Pin_0);
        delay_ms(1000);

        GPIO_SetBit(GPIO0, GPIO_Pin_0);
        delay_ms(1000);

        GPIO_IntClear(GPIO0, GPIO_Pin_0);
}

由于设置了优先级,GPIO0_4对应的优先级最高,因此在main函数或者其他IRQ中断响应函数中,发生了GPIO0_4中断时,会优先执行GPIO0_4的中断响应函数,反之则不然,必须等待高优先级的IRQ中断响应函数完成后,才能执行低优先级的IRQ中断响应函数。

//set interrupt priority
NVIC_SetPriority(GPIO0_7_IRQn, 3);
NVIC_SetPriority(GPIO0_6_IRQn, 2);
NVIC_SetPriority(GPIO0_5_IRQn, 1);
NVIC_SetPriority(GPIO0_4_IRQn, 0);

运行效果如图,仔细观察发现, 按键顺序不同,LED点亮时序也不同,

  1. 按键顺序从 低优先级 到 高优先级(从右到左),对应的中断响应函数是被抢占,

  2. 即低优先级LED还未熄灭,高优先级LED就被点亮。

  3. 按键顺序从 高优先级 到 低优先级(从左到右),对应的中断响应函数是顺序执行,

  4. 即等待高优先级LED熄灭后,再点亮低优先级LED。

体现了 高优先级中断 抢占 低优先级中断 的执行过程。

5.4. nvic

本例程设置了UART0的接收中断,和Timer0的定时中断,且UART0中断的优先级高于Timer0,

NVIC_SetPriority(UART0_IRQn,0);//set uart0 interrupt priority level 0
NVIC_SetPriority(TIMER0_IRQn,1);//set timer0 interrupt priority level 1

nvic测试代码逻辑为:

  1. main函数初始化UART0、Timer0等,然后启动Timer0;

  2. 进入第一次Timer0中断,在中断响应函数TIMER0_Handler中,由于counter_flag标志位未设置,在无限循环中持续往串口打印输出信息。

  3. 当UART0接收到字符,中断TIMER0_Handler的执行,进入UART0中断响应函数UART0_Handler后,设置相应的标志位counter_flag。

  4. 由于UART0中断优先级更高,UART0_Handler继续执行到结束。

  5. 回到TIMER0_Handler继续执行,由于counter_flag已被设置,从而跳出串口的无限循环。

  6. TIMER0_Handler完成剩下的工作,停止Timer0,清除标志位timer_flag和counter_flag。

  7. 当UART0再次接收到数据后,UART0_Handler重新启动Timer0。

//UART0 interrupt handler
void UART0_Handler(void)
{
        if(UART_GetRxIRQStatus(UART0) == SET)
        {
                printf("UART0 Rx interrupt receives a value %c.\r\n",UART_ReceiveChar(UART0));
                printf("UART0 is Rx in interrupt status.\r\n");

                if(timer_flag == true)
                {
                        counter_flag = true;
                }

                UART_ClearRxIRQ(UART0);

                if(timer_flag == false)
                {
                        TIMER_StartTimer(TIMER0);//startup timer0
                        timer_flag = true;
                }
        }
}
void TIMER0_Handler(void)
{
  if(TIMER_GetIRQStatus(TIMER0) != RESET)
  {
    while(1)
    {
                  counter ++;
                  printf("Timer0 interrupt counter is %d.\r\n",counter);

                        if(counter_flag == true)
                        {
                                break;
                        }
    }

                TIMER_ClearIRQ(TIMER0);
                TIMER_StopTimer(TIMER0);
                counter = 0;
                timer_flag = false;

                counter_flag = false;
  }
}

运行效果如图:

5.5. uart

设置同前。

代码分析如下:

  1. 本例程将UART0设置为115200波特率,定时器每0.5s中断一次,

  2. 然后输出字符信息。

  3. 在无限循环中,读取定时器的中断次数计数值count,当计数值count==2时,输出数字。

int main()
{
  int num=0;

        SystemInit();                           //Initializes system
  UartInit();                                   //Initializes uart0
  TimerInit(TIMER0);    //Initializes timer0

  counter=0;
  NVIC_EnableIRQ(TIMER0_IRQn);
  UART_SendString(UART0,"\nNVIC ENABLE IRQ TIMER0\r\n");

  TIMER_EnableIRQ(TIMER0);
  UART_SendString(UART0,"\nTIMER0 IRQ ENABLE\r\n");

  TIMER_StartTimer(TIMER0);
  UART_SendString(UART0,"\nSTART TIMER0\r\n");

  while(1)
  {
    if(counter==2)
    {
      counter=0;

      UART_SendString(UART0,"\r\n");
      UART_SendString(UART0,int2str(num,buff,10));

      num++;
    }

    if(num==60)
          {
      num=0;
          }
  }
}

运行效果:

5.6. retarget

将c标准库中的printf等函数,重定向到UART0,为此需要将C标准库(Keil C Lib)中对底层IO的调用函数,用UART0相关的输入输出函数来实现,如下:

#pragma import (__use_no_semihosting_swi)

struct __FILE
{
    int handle;
};

FILE __stdout;
FILE __stdin;
FILE __stderr;

int fputc(int ch, FILE *f)
{
        UART_SendChar(UART0, (uint8_t) ch);
        while(UART0->STATE & UART_STATE_TXBF);//UART0

        return (ch);
}

int fgetc(FILE *f)
{
        while(!(UART0->STATE & UART_STATE_RXBF));//UART0

        return (int)UART_ReceiveChar(UART0);
}

void _ttywrch(int ch)
{
        UART_SendChar(UART0, (uint8_t) ch);
        while(UART0->STATE & UART_STATE_TXBF);//UART0
}

int _ferror(FILE *f)
{
  /* Your implementation of ferror */
  return EOF;
}

void _sys_exit(int return_code)
{
    //x = x;
label:goto label;
}

main函数中只需要调用C标准库函数printf等,即可实现UART0的打印输出。

int main()
{
  SystemInit(); //init system
  UartInit();           //init uart

        printf("/************GOWINSEMI*****************/\r\n");
        printf("           GOWIN_EMPU_M1                \r\n");

  while(1)
  {
                printf("---------Hello World-----------\r\n");
  }
}

运行效果:

5.7. uart0_irq

设置同前,代码分析如下:

  1. UartInit函数中,使能UART0的接收中断。

  2. 中断响应函数UART0_Handler中,对接收到的数据进行判断,如果是’1’/’2’/…等,则设置对应的LED,否则输出“No operation!”。

void UART0_Handler(void)
{
        char num = '0';

        if(UART_GetRxIRQStatus(UART0) == SET)
        {
                num  = UART_ReceiveChar(UART0);
                printf("UART0 receives a Rx interrupt value %c\r\n",num);

                GPIO0->OUTENSET = 0xffffffff;

                if(num == '1')
                {
                        GPIO0->DATAOUT = 0xe;
                }
                else if(num == '2')
                {
                        GPIO0->DATAOUT = 0xd;
                }
                else if(num == '3')
                {
                        GPIO0->DATAOUT = 0xb;
                }
                else if(num == '4')
                {
                        GPIO0->DATAOUT = 0x7;
                }
                else
                {
                        printf("No operation!\r\n");
                }
        }

        UART_ClearRxIRQ(UART0);
}

运行效果如图:

5.8. systick

通过使用systick中断的精确定时,产生流水灯效果。

//systick initialization
//start systick
void SystickInit(void)
{
        //SystemCoreClock / 1000 : 1ms interrupt
        uint32_t temp = SystemCoreClock / 10000;
        SysTick->LOAD = temp;
        SysTick->VAL = temp;//Reset current counter's value

        //select clock source, enable interrupt, enable counter
        SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
                                                                        SysTick_CTRL_TICKINT_Msk |
                                                                        SysTick_CTRL_ENABLE_Msk;
}

//delay 10us
void delay_us(__IO uint32_t nTime)
{
        TimingDelay = nTime;

        //enable systick
        SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;

        while(TimingDelay != 0);
}

//get systick
//add into SysTick_Handler
void TimingDelay_Decrement(void)
{
        if(TimingDelay != 0x00)
        {
                TimingDelay--;
        }
}

//SysTick interrupt handler --> GOWIN_M1_it.c
void SysTick_Handler(void)
{
        TimingDelay_Decrement();
}

运行效果同led例程。

5.9. timer

代码流程同uart例程,这里主要对timer相关设置进行分析:

  1. 设置Timer0的RELOAD、VALUE和CTRL寄存器,每0.5s中断一次;

  2. 在中断响应函数中,对计数值counter加1;

  3. main函数无限循环根据counter的值,在串口打印对应信息;

void TimerInit(TIMER_TypeDef* TIMERx)
{
  TIMERx->INTCLEAR = 0;
  TIMER0->RELOAD=13500000;//Set the value of the Timmer 0.5s
  TIMER0->VALUE= TIMER0->RELOAD; //Set the init value
  TIMER0->CTRL=0;  // timmer interrupt

  UART_SendString(UART0,"TIMERx->INTCLEAR = 0;\r\n");
  UART_SendString(UART0,"TIMER0->RELOAD=13500000;\r\n");
  UART_SendString(UART0,"TIMERx->VALUE= TIMER0->RELOAD;\r\n");
  UART_SendString(UART0,"TIMERx->CTRL = 0;\r\n");
}

void TIMER0_Handler(void)
{
  if(TIMER_GetIRQStatus(TIMER0) != RESET)
  {
    counter++;
    TIMER_ClearIRQ(TIMER0);
  }
}

运行效果同uart例程。

5.10. dualtimer

将dualtimer这个IP设置为不同的定时间隔,

void DUALTIMER2_Init()
{
        //INIT DUALTIMER 2
        INIT_NUM_load_function(TIMER2_ID,50000000);
        Dtimer_MODE_function(TIMER2_ID,RETIODIC_MODE);
        Dtimer_PRE_function(TIMER2_ID,NON_PRE);
        TIMER_SIZE_function(TIMER2_ID,BIT32);
        ENABLE_interrupt_Dtimer_function(TIMER2_ID,OPEN_INTR);
        ENABLE_Dtimer_function(TIMER2_ID);
}

void DUALTIMER1_Init()
{
        //INIT DUALTIMER 1
        INIT_NUM_load_function(TIMER1_ID,5000000);
        Dtimer_MODE_function(TIMER1_ID,RETIODIC_MODE);
        Dtimer_PRE_function(TIMER1_ID,NON_PRE);
        TIMER_SIZE_function(TIMER1_ID,BIT32);
        ENABLE_interrupt_Dtimer_function(TIMER1_ID,OPEN_INTR);
        ENABLE_Dtimer_function(TIMER1_ID);
}

但是都触发同一个IRQ中断,在中断程序中区分TIMER_ID,来执行不同的操作

void DTimer_Handler(void)
{
        //Clear Handler
        if(Get_DULATIMER_interrupt_num() == TIMER1_ID)
        {
                //dualtimer1 interrupt controls 4-led on and off
                Clear_DULATIMER_interrupt(TIMER1_ID);
                uint32_t data = GPIO_ReadBits(GPIO0);
                GPIO_WriteBits(GPIO0,~data);
        }

        if(Get_DULATIMER_interrupt_num() == TIMER2_ID)
        {
                //dualtimer2 interrupt controls uart0 "Hello World"
                Clear_DULATIMER_interrupt(TIMER2_ID);
                UART_SendString(UART0,"Hi GowinSemiconductor!\r\n");
        }
}

运行效果:

串口打印出“Hi GowinSemiconductor”信息,频次较慢;

而板载LED快速闪烁:

5.11. rtc

rtc例程中:

  1. main函数完成:

    • 对GPIO、UART和RTC的初始化,并使能RTC中断。

    • 无限循环中,每隔550ms获取一次当前的RTC计时器的计时,并打印输出。

    int main()
    {
            uint32_t rtc_current_val = 0;
    
      SystemInit(); //Initializes system
            GPIOInit();             //Initializes GPIO
            UartInit();             //Initializes UART
            RTCInit();              //Initializes RTC
    
      NVIC_EnableIRQ(RTC_IRQn);     //Enable RTC interrupt
    
            //RTC Interrupt Handler.
    
      while(1)
            {
                    rtc_current_val = Get_Current_Value();
                    printf("Current RTC Value : 0x%X\r\n", rtc_current_val);
                    delay_ms(550);
            }
    }
    
  2. 在RTC初始化函数RTCInit中,设置RTC匹配值为10,即每隔10s中断一次、初始值,以及中断屏蔽字,然后启动RTC。

  3. 在RTC中断响应函数RTC_Handler中,设置LED翻转,然后重置RTC计数值为0,以便重复新的中断。

//Initializes RTC
void RTCInit(void)
{
        Set_Match_Value(10);    //Match 10
        Set_Load_Value(0);              //0 Start
        RTC_Inter_Mask_Set();

        Start_RTC();
}

void RTC_Handler(void)
{
        printf("Enter RTC Interrupt.\r\n");

        if(led_flag)
        {
                GPIO_ResetBit(GPIO0, GPIO_Pin_2);
                GPIO_ResetBit(GPIO0, GPIO_Pin_3);
        }
        else
        {
                GPIO_SetBit(GPIO0, GPIO_Pin_2);
                GPIO_SetBit(GPIO0, GPIO_Pin_3);
        }
        led_flag = !led_flag;

        //Clear Inetrrupt
        Clear_RTC_interrupt();

        Set_Load_Value(0);

        printf("Exit RTC Interrupt.\r\n");
}

运行效果如图:

GPIO[3:2]对应的板载的两个LED也每隔10s切换一次亮灭状态。

5.12. watchdog

略作修改,
  1. 注释掉 watchdog_init(86666666,2); 不对watchdog进行重新init,将会重启MCU。

  2. 添加delay_ms,减少main函数中的串口输出,更方便观察。

//delay ms
static void delay_ms(__IO uint32_t delay_ms)
{
  for(delay_ms=delay_ms*2500; delay_ms != 0; delay_ms--);
}

int main ()
{
        SystemInit();//system initialization
        UartInit();//uart initialization
        watchdog_init(86666666,2);//watchdog initialization

        UART_SendString(UART0,"main running!\r\n");

        while(1)
        {
                //add monitor codes
                //feed dogs
                UART_SendString(UART0,"Wdog Feed!\r\n");
                // 注释掉下一行代码,不对watchdog进行重新init,将会重启MCU。
                watchdog_init(86666666,2);
                delay_ms(500);
        }
}
运行效果如图:
  1. 对watchdog进行重新init,会持续执行程序。

  2. 不对watchdog进行重新init,将会重启MCU。

5.13. trng

设置TRNG后,使能中断,使其在完成随机数生成后,进入中断响应函数,将随机数输出到UART0.

void Init_TRNG(void)
{
        Set_Interrupt_Mask(0x0E);
        Set_Config(SHORT_INVERT_LENGTH);
        Set_Random_Source_Enable();
        Set_Sample_Count(1000);
}

void TRNG_Handler(void)
{
        printf("%x ",TRNG->EHR_DATA0);
        printf("%x ",TRNG->EHR_DATA1);
        printf("%x ",TRNG->EHR_DATA2);
        printf("%x ",TRNG->EHR_DATA3);
        printf("%x ",TRNG->EHR_DATA4);
        printf("%x ",TRNG->EHR_DATA5);
        printf("\r\n");

        //Clear Interrupt
        Clear_Int(EHR_VALID);
}

运行效果如图:

5.14. i2c

int main()
{
        unsigned char Temp1[100];
        unsigned char receive1[100];
        uint32_t i;

        SystemInit();                           //system init
        UartInit();                                     //UART0 init
        I2C_Init(I2C,400);      //i2c init

        for(i=0;i<100;i++)
        {
                Temp1 =i;
        }

        printf("Send I2C Slave Data :\r\n");
        for(i=0;i<30;i++)
        {
                printf("%d ", Temp1);
        }
        printf("\r\n\r\n");

        //i2c send bytes
        I2C_SendBytes(I2C ,0x50,0x00,Temp1, 30);

        //i2c receive bytes
        I2C_ReadBytes(I2C,0x50,0x00,receive1,30);

        printf("Receive I2C Slave Data : \r\n");
        for(i=0;i<30;i++)
        {
                printf("%d ",receive1);
        }
        printf("\r\n");

        while(1);
}

将I2C-EEPROM模块接入到I2C对应的引脚,并设置I2C地址为0x50(即I2C-EEPROM的A2/A1/A0均接到GND)

运行效果如图:

当断开I2C-EEPROM模块与FPGA的连接后,软件代码停留在I2C等待ACK回应的阶段:

while(i2c->SR&I2C_SR_RXACK);//wait ack-----Over Here

 

本文来自论坛,点击查看完整帖子内容。

发表评论 评论 (1 个评论)
回复 唐王琦 2022-11-28 09:17
如果开发式的架构,可以快速补充产品的短板。增加产品更广阔的需求点与出货量

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册