5. 高云Cortex-M1软核处理器参考设计的例程测试记录
5.1. led
参见` Keil项目的设置、编译、下载与调试`,注意修改项目:
-
Keil项目的Options选项设置,
-
ext_debug.init修改,
-
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点亮时序也不同,
-
按键顺序从 低优先级 到 高优先级(从右到左),对应的中断响应函数是被抢占,
-
即低优先级LED还未熄灭,高优先级LED就被点亮。
-
按键顺序从 高优先级 到 低优先级(从左到右),对应的中断响应函数是顺序执行,
-
即等待高优先级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测试代码逻辑为:
-
main函数初始化UART0、Timer0等,然后启动Timer0;
-
进入第一次Timer0中断,在中断响应函数TIMER0_Handler中,由于counter_flag标志位未设置,在无限循环中持续往串口打印输出信息。
-
当UART0接收到字符,中断TIMER0_Handler的执行,进入UART0中断响应函数UART0_Handler后,设置相应的标志位counter_flag。
-
由于UART0中断优先级更高,UART0_Handler继续执行到结束。
-
回到TIMER0_Handler继续执行,由于counter_flag已被设置,从而跳出串口的无限循环。
-
TIMER0_Handler完成剩下的工作,停止Timer0,清除标志位timer_flag和counter_flag。
-
当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
设置同前。
代码分析如下:
-
本例程将UART0设置为115200波特率,定时器每0.5s中断一次,
-
然后输出字符信息。
-
在无限循环中,读取定时器的中断次数计数值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
设置同前,代码分析如下:
-
UartInit函数中,使能UART0的接收中断。
-
中断响应函数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相关设置进行分析:
-
设置Timer0的RELOAD、VALUE和CTRL寄存器,每0.5s中断一次;
-
在中断响应函数中,对计数值counter加1;
-
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例程中:
-
main函数完成:
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);
}
}
-
在RTC初始化函数RTCInit中,设置RTC匹配值为10,即每隔10s中断一次、初始值,以及中断屏蔽字,然后启动RTC。
-
在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
- 略作修改,
-
-
注释掉 watchdog_init(86666666,2); 不对watchdog进行重新init,将会重启MCU。
-
添加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);
}
}
- 运行效果如图:
-
-
对watchdog进行重新init,会持续执行程序。
-
不对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