中断技巧增加平行处理的能力
写单晶片程式有意思的地方除了那种“掌控”的感觉外,接着就是有那种“处理复杂事件的能力”!
基本的程式是循序进行,时间就会被耽搁。若是想要把程式并行处理,那么,“中断”插入的方法是个好帮手,这个时候就要能同时处理不同的事件。我常常比喻电脑程式就像厨房;火炉是CPU,餐盘容器是暂存器,食材就是资料;这个做菜的程序容易理解,只是这时间不容易拿捏,所以,单晶片的内部计时器就是一个好帮手。
这个M0 的计时器和中断的写法,比51族的mpu方便多了。
在M0 的C 语言里,使用中断功能要写下几个功能:
- 设定中断来源和 开启中断程式的向量指标
- 编写中断后要执行的服务程式
a.是否接受巢式中断
b.主程式转移进来的资料
c.要转移出去的结果 - 把中断处理的结果(变数)移交回主程式。
- 下一次中断的开启
设定中断来源和 开启中断程式的向量指标
一个用timer 开启的中断
void Config_Timer2(void)
{
RCC->APB1ENR |= 0x1ul;
TIM2->PSC =47;
TIM2->ARR = 1000-1;
TIM2->CNT =0;
TIM2->CR1 |= 0x1ul <<7 | 0x1ul;
TIM2->DIER |= 0x1ul;
NVIC_SetPriority(TIM2_IRQn,3);
NVIC_EnableIRQ(TIM2_IRQn);
}
//---------------------
void TIM2_IRQHandler (void)
{
TIM2_Ticks++;
TIM2->SR &= ~0x1ul;
}
这是一个 UART 用的中断
void Config_uart1(void) // for BlueTooth
{
RCC->APB2ENR |= 0x1ul <<14;
USART1->BRR = 48000000/115200;
USART1->CR3 |= 0x1ul <<12;
USART1->CR1 |= 0x1ul << 5 | 0x1ul<<2 | 0x1ul;
USART1->ICR |= 0x1ul <<6 ;
USART1->CR1 |= 0x1ul << 6 | 0x1ul<< 3 ;
NVIC_SetPriority(USART1_IRQn,3);
NVIC_EnableIRQ(USART1_IRQn);
}
void USART1_IRQHandler(void) //for BlueTooth
{ };
下面的程式片段是设定了一个中断程式的应用内容:
//--------------------------------
void setIRQPin(uint8_t y,uint8_t x) // to set the IRQ_pin of STM32
{
SYSCFG->EXTICR[x/4] &= ~(0xful <<((x%4)*4));
SYSCFG->EXTICR[x/4] |= (y <<((x%4)*4));
} //******这个副程式很有用,很方便
//***** need to set PA.0.1.9.10
// PA0,PA1 -->EXTICR[0] 0.1
// PA9,PA10 -->EXTICR[2] 2.3
void Config_PA01PA910(void)
{
RCC->AHBENR |= 0x1ul << 17;
GPIOA->MODER &=~(0x3ul| (0x3ul << 1*2)|(0x3ul << 9*2)|(0x3ul << 10*2));
RCC->APB2ENR |= 0x1ul;
// SYSCFG->EXTICR[0] &= ~(0xful | 0xful << 1*4);
// SYSCFG->EXTICR[2] &= ~((0xful<< 2*4)| (0xful << 3*4));
setIRQPin(0,0); //PA.0
setIRQPin(0,1); //PA.1
setIRQPin(0,9); //PA.9
setIRQPin(0,10); //PA.10
EXTI->FTSR |= 0x1ul | 0x1ul<< 1;
EXTI->IMR |= 0x1ul | 0x1ul<< 1;
EXTI->PR |= 0x1ul | 0x1ul<< 1;
EXTI->FTSR |= 0x1ul<<9 | 0x1ul<< 10;
EXTI->IMR |= 0x1ul <<9 | 0x1ul<< 10;
EXTI->PR |= 0x1ul<<9 | 0x1ul<< 10;
NVIC_SetPriority(EXTI0_1_IRQn,3);
NVIC_EnableIRQ(EXTI0_1_IRQn);
}
//-----------------------------------------
void EXTI0_1_IRQHandler(void)
{
for (int i =0; i<1000; i++)
__NOP;
printf("IRQ 5 interrup. \n\r");
Delay(50);
NVIC_SetPendingIRQ(EXTI2_3_IRQn);
printf("IRQ 5 end. \n\r");
}
我们翻看 F072 Reference manual : page 217,218,219 的表格,可以看到所有中断的名称,使用对象和向量的地址。如下面的三张图。
最左一行是编号栏,其中有 7 个不给编号,那是系统用的。第四栏位 acronym(缩写)说明了这些中断都被规划给那些功能使用,譬如:WWDG(看门狗),EXTI(擴展中斷和事件控制,就是使用者特殊规划用的),TSC(触摸控制),DMA,Timer,I2C,SPI,UART,…,USB 等。
下图是Reference Manual上的使用范例:
由于这些副程式的英文字数多,Keil 都有参考编辑让你选,打错字的几率比较少。
下面这三种副程式必须要形影不离的一起使用。
NVIC_SetPriority(USART1_IRQn,3);
NVIC_EnableIRQ(USART1_IRQn);
void USART1_IRQHandler(void) { 自定义执行内容 };
为了避免再发生打错字,我们从内部现有的范例 copy 这些名称 ,而这里的这个位置可以找到正确的内部定义,(在startup_stm32f072xb.s(Startup) 这个档案里)
计时器的部分,写在下一篇!(完)