热度 1|
参见` Keil项目的设置、编译、下载与调试`,注意修改项目:
Keil项目的Options选项设置,
ext_debug.init修改,
SYSTEM_CLOCK值的修改。
设置同前。
代码流程:通过对按键进行扫描,实现流水灯效果:
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); } }
运行效果如图:
该例程设置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。
体现了 高优先级中断 抢占 低优先级中断 的执行过程。
本例程设置了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; } }
运行效果如图:
设置同前。
代码分析如下:
本例程将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; } } }
运行效果:
将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"); } }
运行效果:
设置同前,代码分析如下:
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); }
运行效果如图:
通过使用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例程。
代码流程同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例程。
将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快速闪烁:
rtc例程中:
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); } }
在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切换一次亮灭状态。
注释掉 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。
设置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); }
运行效果如图:
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