STM32F103蓝丸中断从无到有
STM32F103 blue pill interrupts from scratch
如何从头开始为蓝色药丸创建中断?
我不想使用任何类型的特殊库。此外,我使用 Keil IDE,因此,通过“从头开始构建”,我指的是不使用任何额外的库,而不是 assemble 没有 IDE.[= 帮助的项目。 10=]
我试图找到资源,但没有成功。有人可以帮助我并至少为我提供一些 information/bibliography 吗?我将不胜感激。
此外,“奇怪的库”是指 stmf32f1xx.h header 以外的任何其他库。当其中一个引脚的输入值切换时,我想触发一个中断。要做到这一点,在 AVR MCU 上非常简单,只要更改几个寄存器值即可。不幸的是,我不知道 ARM MCU 中的中断如何运行以及我应该在哪些寄存器中写入什么值。
此外,更好地了解 ARM MCU 的中断机制将使我为处理去抖动问题做好更充分的准备。
当你强制要求“没有图书馆”时,我不会完全照字面意思,因为没有人想要完成工作并且知道他们在 Cortex-M 上做什么 - 我会假设 至少 您将使用 CMSIS - 为 所有 ARM Cortex-M 设备提供的通用 API,这使您的代码更加, 不那么便携。
所有 CMSIS 代码均以源代码而非静态库的形式提供,因此没有任何隐藏内容,如果您选择不使用它,您可以查看它的工作原理并根据需要复制该功能(不必要地)。
在 CMSIS 中,默认实现作为“弱链接”提供,用户代码可以简单地通过定义预定义名称的函数来覆盖默认实现。默认实现通常是 无限循环 - 这样未处理的中断就会被“捕获”,这样您就可以干预调试器或等待看门狗重置。
Cortex-M 核心中断处理程序和异常处理程序在所有 Cortex-M 部件中具有通用名称:
Reset_Handler
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
UsageFault_Handler
SVC_Handler
DebugMon_Handler
PendSV_Handler
SysTick_Handler
外设中断处理程序的名称由供应商定义,但命名约定是<interrupt_source>_IRQHandler
。例如,在 STM32F1xx 上,EXTI0_IRQHandler
是 shared 外部中断分配给 GPIO 端口的位零。
要实现 CMSIS 中断处理程序,您需要做的就是:
- 使用CMSIS处理函数名实现中断处理函数
- 在 NVIC(中断控制器)中启用中断。
您还可以做其他一些事情,例如分配中断优先级方案(抢占优先级和子优先级之间的分离),但让我们暂时保持简单。
因为它普遍存在于所有 Cortex-M 部分,并且因为它在几乎所有重要应用程序中都很有用,所以使用 SYSTICK 中断的插图作为起点很有用。
#include "stm32f1xx.h"
volatile uint32_t msTicks = 0 ;
void SysTick_Handler(void)
{
msTicks++ ;
}
int main (void)
{
if( SysTick_Config( SystemCoreClock / 1000 ) != 0 ) // 1ms tick
{
// Error Handling
}
...
}
SysTick_Config()
是另一个 CMSIS 函数。在 core_cm3.h 中它看起来像这样:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
假设您在 GPIOA 引脚 0 的下降沿有一个外部中断源,那么您将使用 STM32 EXTI0 中断。最小处理程序如下所示:
void EXTI0_IRQHandler(void)
{
EXTI->PR |= (1<<0); // clear pending interrupt
// Handle interrupt...
}
设置 EXTI 需要启用 GPIO 和 EXTI 本身以及 NVIC:
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ; // enable clock for GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN ; // enable clock for Alternate Function
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0 ; // set pin to use
EXTI->IMR = EXTI_IMR_MR0 ; // unmask interrupt
EXTI->EMR = EXTI_EMR_MR0 ; // unmask event
EXTI->FTSR = EXTI_FTSR_TR0 ; // set falling edge
NVIC->ISER[0] |= (1 << (EXTI0_IRQChannel & 0x1F)); // enable interrupt EXTI 0
外设寄存器和结构在 stm32f10weakx.h 中定义,要覆盖的“弱”默认外设处理程序在 startup_stm32f10x_cl.s 中用于您的特定部分。您覆盖的任何处理程序都必须与这些符号名称完全匹配。
所有外设中断源以及如何配置它们都在 ST Reference Manual RM0008.
中定义
ARM 在 https://developer.arm.com/ip-products/processors/cortex-m/cortex-m3
提供了所有 Cortex-M 内核特定的东西 - systtick、NVIC、异常处理程序等
CM3 的 CMSIS 记录在 https://developer.arm.com/documentation/dui0552/a/
如何从头开始为蓝色药丸创建中断?
我不想使用任何类型的特殊库。此外,我使用 Keil IDE,因此,通过“从头开始构建”,我指的是不使用任何额外的库,而不是 assemble 没有 IDE.[= 帮助的项目。 10=]
我试图找到资源,但没有成功。有人可以帮助我并至少为我提供一些 information/bibliography 吗?我将不胜感激。
此外,“奇怪的库”是指 stmf32f1xx.h header 以外的任何其他库。当其中一个引脚的输入值切换时,我想触发一个中断。要做到这一点,在 AVR MCU 上非常简单,只要更改几个寄存器值即可。不幸的是,我不知道 ARM MCU 中的中断如何运行以及我应该在哪些寄存器中写入什么值。
此外,更好地了解 ARM MCU 的中断机制将使我为处理去抖动问题做好更充分的准备。
当你强制要求“没有图书馆”时,我不会完全照字面意思,因为没有人想要完成工作并且知道他们在 Cortex-M 上做什么 - 我会假设 至少 您将使用 CMSIS - 为 所有 ARM Cortex-M 设备提供的通用 API,这使您的代码更加, 不那么便携。
所有 CMSIS 代码均以源代码而非静态库的形式提供,因此没有任何隐藏内容,如果您选择不使用它,您可以查看它的工作原理并根据需要复制该功能(不必要地)。
在 CMSIS 中,默认实现作为“弱链接”提供,用户代码可以简单地通过定义预定义名称的函数来覆盖默认实现。默认实现通常是 无限循环 - 这样未处理的中断就会被“捕获”,这样您就可以干预调试器或等待看门狗重置。
Cortex-M 核心中断处理程序和异常处理程序在所有 Cortex-M 部件中具有通用名称:
Reset_Handler
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
UsageFault_Handler
SVC_Handler
DebugMon_Handler
PendSV_Handler
SysTick_Handler
外设中断处理程序的名称由供应商定义,但命名约定是<interrupt_source>_IRQHandler
。例如,在 STM32F1xx 上,EXTI0_IRQHandler
是 shared 外部中断分配给 GPIO 端口的位零。
要实现 CMSIS 中断处理程序,您需要做的就是:
- 使用CMSIS处理函数名实现中断处理函数
- 在 NVIC(中断控制器)中启用中断。
您还可以做其他一些事情,例如分配中断优先级方案(抢占优先级和子优先级之间的分离),但让我们暂时保持简单。
因为它普遍存在于所有 Cortex-M 部分,并且因为它在几乎所有重要应用程序中都很有用,所以使用 SYSTICK 中断的插图作为起点很有用。
#include "stm32f1xx.h"
volatile uint32_t msTicks = 0 ;
void SysTick_Handler(void)
{
msTicks++ ;
}
int main (void)
{
if( SysTick_Config( SystemCoreClock / 1000 ) != 0 ) // 1ms tick
{
// Error Handling
}
...
}
SysTick_Config()
是另一个 CMSIS 函数。在 core_cm3.h 中它看起来像这样:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
假设您在 GPIOA 引脚 0 的下降沿有一个外部中断源,那么您将使用 STM32 EXTI0 中断。最小处理程序如下所示:
void EXTI0_IRQHandler(void)
{
EXTI->PR |= (1<<0); // clear pending interrupt
// Handle interrupt...
}
设置 EXTI 需要启用 GPIO 和 EXTI 本身以及 NVIC:
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ; // enable clock for GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN ; // enable clock for Alternate Function
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0 ; // set pin to use
EXTI->IMR = EXTI_IMR_MR0 ; // unmask interrupt
EXTI->EMR = EXTI_EMR_MR0 ; // unmask event
EXTI->FTSR = EXTI_FTSR_TR0 ; // set falling edge
NVIC->ISER[0] |= (1 << (EXTI0_IRQChannel & 0x1F)); // enable interrupt EXTI 0
外设寄存器和结构在 stm32f10weakx.h 中定义,要覆盖的“弱”默认外设处理程序在 startup_stm32f10x_cl.s 中用于您的特定部分。您覆盖的任何处理程序都必须与这些符号名称完全匹配。
所有外设中断源以及如何配置它们都在 ST Reference Manual RM0008.
中定义ARM 在 https://developer.arm.com/ip-products/processors/cortex-m/cortex-m3
提供了所有 Cortex-M 内核特定的东西 - systtick、NVIC、异常处理程序等CM3 的 CMSIS 记录在 https://developer.arm.com/documentation/dui0552/a/