####STM32开发基础知识入门
###C语言复习
##位操作
与:&
或:|
异或:^ :是否不同 , 不同则真
取反:~
左移:低位补0
右移:高位如果是正数就补0
#对某几个位进行设值
与零进行与操作就是清零
与一进行或操作就是赋一
赋值先清零然后用与
GPIOA->CRL&=0XFFFFFF0F;
GPIOA->CRL|=0X00000040;
#移位操作提高代码可读性
由于左右移先于|=运算
GPIOA->ODR|=1<<5;
#取反操作
#define TIM_FLAG_Update ((uint16_t)0x0001)
#define TIM_FLAG_CC1 ((uint16_t)0x0002)
##define宏定义
##ifdef else endif 条件编译
##extern变量申明 申明id是外部变量,不申明id的作用域到不了test.c
##typedef类型别名 定义新的类型名
##结构体 : 可以不改变入口参数,改变成员变量
###STM32系统架构 25-28 图于103
四个驱动单元是: 内核 DCode 总线; 系统总线; 通用 DMA1; 通用 DMA2;
四被动单元是: AHB 到 APB 的桥:连接所有的 APB 设备; 内部 FlASH 闪存; 内部 SRAM; FSMC
###STM32时钟系统55-73 图于105 //设置时钟时要看一下图
有五个时钟源,为 HSI、HSE、LSI、LSE、PLL
系统时钟源SYSCLK 可选择为 PLL 输出、HSI 或者 HSE。系统时钟最大频率为 72MHz。
SYSCLK 通过 AHB 分频器分频后送给各模块使用。
总结一下SystemInit()函数中设置的系统时钟大小:
SYSCLK(系统时钟) =72MHz
AHB 总线时钟(使用 SYSCLK) =72MHz
APB1 总线时钟(PCLK1) =36MHz
APB2 总线时钟(PCLK2) =72MHz
PLL 时钟 =72MHz
###端口复用 stm中参 109 /116-121
复用端口初始化
使能端口的时钟
复用外设时钟使能
端口模式配置 可以查看stm中参110
//全双工的串口1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //GPIO端口使能 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //复用外设使能 使能串口1时钟
//USART1_TX PA.9 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX PA.10 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
###端口重映射 stm中参 116
可以把一些复用功能重新映射到其他一 些引脚上。
多的步骤:
使能GPIOB时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //由于使用了PB6和PB7
使能串口1时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 本来就有
使能AFIO时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //功能时钟
开启重映射:GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
部分重映射就是部分管脚和默认的是一样的,而部分管脚是重新映射到其他管脚。而完全重 映射就是所有管脚都重新映射到其他管脚
###STM32 NVIC 中断优先级管理
只有了解下面这些中断寄存器,才能方便 的使用 STM32 的中断。
ISER[8]:终断使能寄存器组,STM32F103 只用了CM3内核中断的前 60 位。ISER[0]的 bit0-bit31 分别对应中断 0-31。
ISER[1]的 bit0-27 对应中断 32-59;这样总共 60 个中断就分别对应上了。你要使能某个中断,必须设置相应的
ISER 位为 1,使该中断被使能,具体每一位对应哪个中断,请 参考 stm32f10x.h 里面的第 140 行处
ICER[8]:中断除能寄存器组,用来清除某个中断的使能的。其对应位的功能,也和 ICER 一样。
这里要专门设置一个 ICER来清除中断位,而不是向 ISER 写 0 来清除,
是因为 NVIC 的这些寄 存器都是写 1 有效的,写 0 是无效的。
ISPR[8]:中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,
可以将正在进行的中断挂起,而执行同级或更高级别的中断。写0是无效的。
ICPR[8]:中断解挂控制寄存器组。,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断接挂。写 0 无效。
IABR[8]:中断激活标志位寄存器组。如果为 1,则表示该位所对应的中断正在被执行。在中断执行完了由硬件自动清零。
IP[240]:中断优先级控制的寄存器组。STM32 的中断分组与这个寄存器组密切相关,其由 240 个 8bit 的寄 存器组成,
每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断,STM32 只用到 了其中的前 60 个。
IP[59]-IP[0]分别对应中断 59-0。而每个可屏蔽中断占用的 8bit 并没有全部 使用,而是 只用了高 4 位。
这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位
又要根据 SCB->AIRCR 中的中断分组设置来决定。
这里需要注意两点:第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看 哪个中断先发生就先执行;第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级 中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。
结合实例说明一下:假定设置中断优先级组为 2,然后设置中断 3(RTC 中断)的抢占优先级 为 2,响应优先级为 1。中断 6(外部中断 0)的抢占优先级为 3,响应优先级为 0。中断 7(外 部中断 1)的抢占优先级为 2,响应优先级为 0。那么这 3 个中断的优先级顺序为:中断 7>中 断 3>中断 6。 上面例子中的中断 3 和中断 7 都可以打断中断 6 的中断。而中断 7 和中断 3 却不可以相互打断! 子优先级数值越低越优先。
使用库函数实现以上中断分组设置以及中断优先级管理
/*
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; 通过设置 SCB->AIRCR 寄存器来设置中断优先级分 组,
}
*/
//eg:设置整个系统的中断优先级分组值
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //就确定了一共为“2 位抢占优先级,2 位响应优先级”。
//设置好了系统中断分组,那么对于每个中断我们又怎么确定他的抢占优先级和响应优先级 呢?使用中断初始化函数NVIC_Init
/*
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
typedef struct { //NVIC_InitTypeDef结构体
uint8_t NVIC_IRQChannel; 定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。例如 USART1_IRQn。
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级别
uint8_t NVIC_IRQChannelSubPriority; //子优先级别
FunctionalState NVIC_IRQChannelCmd; //终端是否使能
} NVIC_InitTypeDef; NVIC
*/
//eg:使能串口 1 的中断,同时设置抢占优先级为 1,子优先级位 2,初始化的方法是:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; // 子优先级位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化 NVIC 寄存器
步骤:
###MDK 中寄存器地址名称映射分析 //《STM32 中文参考手册 V10》中的寄存器地址映射表(P159):
GPIOA 的寄存器的地址=GPIOA 基地址+寄存器相对 GPIOA 基地址的偏移值
GPIOA->BRR=value
###MDK 固件库快速组织代码技巧
一个 GPIO 口的状态是由速度 (Speed)和模式(Mode)来决定的。
eg:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
在stmconf中查找那些外设挂载在哪些总线之下 find : #define RCC_APB2Periph_GPIOA
在使能 GPIO 的时候调用的是 RCC_APB2PeriphResetCmd() 函数使能。
如果要确定详细的信息呢我们就得去查看手册了。对于去查看手册的哪个地方,你可以在函数 GPIO_Init()的函数体中搜索 GPIO_Mode 关键字,然后 查看库函数设置 GPIO_Mode 是设置的哪个寄存器的哪个位,然后去中文参考手册查看该寄存 器相应位的定义以及前后文的描述。
#¥%新概念
内核 DCode 总线; 系统总线; 通用 DMA1; 通用 DMA2;
AHB 到 APB 的桥:连接所有的 APB 设备; 内部 FlASH 闪存; 内部 SRAM; FSMC
抢占优先级和子优先级:子优先级就是响应优先级