资讯详情

STM32单片机介绍1

内容包括STM32命名规则,启动模式配置说明,总线AMBA、AHB、APB、NVIC、介绍时钟树,TIM输出互补波形程序,中断编程过程,AD用DMA请注意程序初始化顺序和外设地址设置。紫色文字是超链接,点击自动跳转到相关博客。不断更新,原创不容易!
目录:
1)一些应用串口中断时涉及的库文件2)初始化 3)发送数据4)接收数据5)main函数6)在启动文件查找中断函数
1)通用I/O端口连接到16个外部中断/事件线 2)编写外部中断服务函数 3)编写具体程序
1)简述      2)时钟输出的使能控制     3)使用HSE时钟,程序设置时钟参数流程
-----------------------------------------------

或者使用“STM32CubeMX”工具软件,介绍STM32CubeMX之F1xx系列实用报告(图文代码齐全)下载STM32CubeMX软件
-----------------------------------------------
ARM单片机一
ARM单片机二
-------------------------------------------------------------------------------------------------------------------

从命名规则可得STM32F103VET6Flash大小为512K字节。
通过数据手册得到Flash大小,下面以 

STM32F103RBT6为例说明:

Page0 – Page127 = 1KB(0000-03FF) * 128Page = 128KB total,该区域地址范围:0x08000000 – 0x0801FFFF,总大小为128K字节。该区域主要用途:存放STM32的代码段(用户程序)。
-------------------------------------------------------------------------------------------------------------------

所谓启动,一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被存。用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式。

-----------------------

1)Main Flash memory

是STM32内置的Flash,一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。正常工作就在这种模式下,STM32的FLASH可以擦出10万次,所以不用担心芯片哪天会被擦爆!STM32从Flash存储的第一条指令开始执行,即执行STM32的启动代码stm32f10x_vector.s(或stm32F10x_xxx_xxx.s或startup_xxx.s 根据STM32 Firmware library的不同而不同),执行启动代码后会跳到main函数,执行用户程序。

-----------------------

2)System memory

System memory is used to boot the device in System memory boot mode. The area is reserved for use by STMicroelectronics and contains the boot loader which is used to reprogram the Flash memory using the USART1 serial interface. It is programmed by ST when the device is manufactured, and protected against spurious write/erase operations. For further details please refer to AN2606.

从系统存储器启动,这种模式启动的程序功能是由厂家设置的。一般来说,这种启动方式用的比较少。

系统存储器是芯片内部一块特定的区域,STM32在出厂时,由ST在这个区域内部预置了一段BootLoader,也就是我们常说的ISP程序,这是一块ROM,出厂后无法修改。

一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的BootLoader中,提供了串口下载程序的固件(比如mcuisp.exe),可以通过这个BootLoader将程序下载到系统的Flash中。但是这个下载方式需要以下步骤:

将BOOT0设置为1,BOOT1设置为0,然后按下复位键(或重启),这样才能从系统存储器启动BootLoader;

最后在BootLoader的帮助下,通过串口下载程序到Flash中;

程序下载完成后,又需要将BOOT0设置为GND(通过电阻连到地),手动复位(或重启),这样STM32才可以从Flash中启动。

关于DB9的针脚定义见“通信-RS232、RS485、RS422 之3、DB9针脚定义”

-----------------------

3)Embedded SRAM

内置SRAM,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。

(1)故障的局部诊断,写一段小程序加载到SRAM中诊断板上的其他电路,或用此方法读写板上的Flash或EEPROM等。

(2)通过这种方法解除内部Flash的读写保护,当然解除读写保护的同时Flash的内容也被自动清除,以防止恶意的软件拷贝。

(3)只修改了代码中一个小小的地方,然后就需要重新擦除整个Flash,比较的费时,可以考虑从这个模式启动代码(也就是STM32的内存中),用于快速的程序调试,等程序调试完成后,在将程序下载到Flash中。

-----------------------------------------------

先是拿到一块别人的板子和程序来修改,后来做了块板子,烧录程序后发现:烧录后通过烧录工具的“烧录后运行”选项可以正常跑起来,但一旦使程序从Flash开始运行,则跑不了。后发现别人板子的MCU烧录过Boot,而自己做的板子没有烧录Boot,前者程序从0x8003000开始运行,后者程序实际从0x8000000开始运行,把0x8003000处开始运行的程序烧到0x8000000的板子上的结果就是:复位向量地址不正确,导致芯片无法启动,因为0x8003000的程序把中断向量表搬到了0x8003000处。
转载正点原子的分析:

        STM32的内部闪存(Flash)地址起始于0x08000000,一般情况下,程序文件就从此地址开始写入。此外STM32是基于Cortex-M3内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是0x08000004(0x8003000的程序中,中断向量表的地址0x8003000),当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。

         在图53.1.1中,STM32在复位后,先从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号所示;在复位中断服务程序执行完之后,会跳转到我们的main函数,如图标号所示;而我们的main函数一般都是一个死循环,在main函数执行过程中,如果收到中断请求(发生重中断),此时STM32强制将PC指针指回中断向量表处,如图标号所示;然后,根据中断源进入相应的中断服务程序,如图标号所示;在执行完中断服务程序以后,程序再次返回main函数执行,如图标号所示。

-------------------------------------------------------------------------------------------------------------------

AMBA(Advanced Microprocessor Bus Architecture)是ARM公司提出的一种开放性的SoC总线标准,现在已经广泛的应用于RISC的内核上了。  AMBA定义了一种(multilevel busing system),包括系统总线和等级稍低的外设总线。  AMBA支持32位、64位、128位的数据总线,和32位的地址总线,同时支持byte和half-word设计。  它定义了两种总线: 

AHB(Advanced High-performance Bus)先进的高性能总线,也叫做ASB(Advanced System Bus)。APB(Advanced peripheral Bus)先进的外设总线 。
AHB和ASB其实是一个东西,是,主要负责嵌入式处理器、DMA控制器、Memory等等的接口。 
APB是,主要负责外设接口。
AHB和APB之间是通过Bridge(桥接器)链接的。
-----------------------------------------------
总所周知,一个系统中的各个模块之间相互通信是通过总线,总线的作用,就是把数据和地址从设备A搬运到设备B上。如果说设备A和设备B具有一致性(原文是under discussion,这里我不知道怎么翻译比较好,暂且翻译为一致性),那么设备A和设备B可以直接挂在同一个总线上,并直接解读总线上的数据。 但是,如果设备A和设备B不具有一致性,那么设备A和设备B就必须挂在两条不同的总线上,这时候我们就需要一个,把设备A上的总线上的数据和地址,然后放到设备B的总线上,这个“翻译”就是“Bus Bridge”,下面这幅图就形象的说明了Bus Bridge在AHB和APB之间的作用。

AHB链接的设备的数据传输速度是比APB设备传输的速度快很多的,也就是说,这里的这个Bus Bridge所起的作用就是“”。这里可以看到AHB主要是链接在了系统的内核以及存储管理上面的,APB则主要分布给外设。 
下面这张图,更容易看出AHB和APB的作用: 
AHB链接的是系统总线、RAM等;
APB链接的是常用的外设:GPIO、UART等,如下:
APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM
APB2负责AD,I/O,高级TIM,串口1。

-----------------------------------------------
首先看一下F103系列的芯片的总线结构 

#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001) 
#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008) 
#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010) 
#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040) 
#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080) 
#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100) 
#define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200) 
#define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400) 
#define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800) 
#define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000) 
#define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000) 
#define RCC_APB2Periph_USART1 ((uint32_t)0x00004000) 
#define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000) 
#define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000) 
#define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000) 
#define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000) 
#define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000) 
#define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000) 
#define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000) 
#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00)) 
#define RCC_APB1Periph_TIM2 ((uint32_t)0x00000001) 
#define RCC_APB1Periph_TIM3 ((uint32_t)0x00000002) 
#define RCC_APB1Periph_TIM4 ((uint32_t)0x00000004) 
#define RCC_APB1Periph_TIM5 ((uint32_t)0x00000008) 
#define RCC_APB1Periph_TIM6 ((uint32_t)0x00000010) 
#define RCC_APB1Periph_TIM7 ((uint32_t)0x00000020) 
#define RCC_APB1Periph_TIM12 ((uint32_t)0x00000040) 
#define RCC_APB1Periph_TIM13 ((uint32_t)0x00000080) 
#define RCC_APB1Periph_TIM14 ((uint32_t)0x00000100) 
#define RCC_APB1Periph_WWDG ((uint32_t)0x00000800) 
#define RCC_APB1Periph_SPI2 ((uint32_t)0x00004000) 
#define RCC_APB1Periph_SPI3 ((uint32_t)0x00008000) 
#define RCC_APB1Periph_USART2 ((uint32_t)0x00020000) 
#define RCC_APB1Periph_USART3 ((uint32_t)0x00040000) 
#define RCC_APB1Periph_UART4 ((uint32_t)0x00080000) 
#define RCC_APB1Periph_UART5 ((uint32_t)0x00100000) 
#define RCC_APB1Periph_I2C1 ((uint32_t)0x00200000) 
#define RCC_APB1Periph_I2C2 ((uint32_t)0x00400000) 
#define RCC_APB1Periph_USB ((uint32_t)0x00800000) 
#define RCC_APB1Periph_CAN1 ((uint32_t)0x02000000) 
#define RCC_APB1Periph_CAN2 ((uint32_t)0x04000000) 
#define RCC_APB1Periph_BKP ((uint32_t)0x08000000) 
#define RCC_APB1Periph_PWR ((uint32_t)0x10000000) 
#define RCC_APB1Periph_DAC ((uint32_t)0x20000000) 
#define RCC_APB1Periph_CEC ((uint32_t)0x40000000) 
#define IS_RCC_APB1_PERIPH(PERIPH) ((((PERIPH) & 0x81013600) == 0x00) && ((PERIPH) != 0x00))
 

APB1限制在了36MHz,APB2也可以达到全速72MHz  下面是F105和F107的总线构架:

STM32上APB1和APB2的地址映射

-------------------------------------------------------------------------------------------------------------------

   需要在STM32上移植RTOS,那么首先必须深入理解它的中断系统。什么是NVIC?即嵌套向量中断控制器(Nested Vectored Interrupt Controller)。STM32的中有一个强大而方便的NVIC,它是属于Cortex内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,而SYSTICK不是由 NVIC来控制的。

特性:

  60个可屏蔽中断通道(不包含16个Cortex™-M3的中断线);

  16个可编程的优先等级(使用了4位中断优先级);

  低延迟的异常和中断处理;

  电源管理控制;

  系统控制寄存器的实现;

-----------------------------------------------

STM32(Cortex-M3)中有两个优先级的概念--抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。 

  具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套在低抢占式优先级的中断中。

  当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

    Cortex内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(exception)和中断(interrupt),并把它们用一个表管理起来,编号为0~15的称为内核异常,而16以上的则称为外部中断,这个表就称为中断向量表。

    正是因为每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

1)所有8位用于指定响应优先级 

2)最高1位用于指定抢占式优先级,最低7位用于指定响应优先级

3)最高2位用于指定抢占式优先级,最低6位用于指定响应优先级

4)最高3位用于指定抢占式优先级,最低5位用于指定响应优先级

5)最高4位用于指定抢占式优先级,最低4位用于指定响应优先级

6)最高5位用于指定抢占式优先级,最低3位用于指定响应优先级

7)最高6位用于指定抢占式优先级,最低2位用于指定响应优先级

8)最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

以上便是优先级分组的概念,但是Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级。

    而STM32对这个表重新进行了编排,把编号从-3至6的中断向量定义为系统异常,编号为负的内核异常不能被设置优先级,如复位(Reset)、不可屏蔽中断 (NMI)、硬错误(Hardfault)。从编号7开始的为外部中断,这些中断的优先级都是可以用户更改的。详细的 STM32中断向量号可以在startup_stm32f10x_XX.s中查找。

因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级(16种)

第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级(8种)

第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级(4种)

第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级(2种)

第4组:所有4位用于指定抢占式优先级

这里便对于于文章最前提到的固件库里相关的函数了——NVIC_PriorityGroupConfig(u32 NVIC_PriorityGroup),函数的参数共有5种:

这个函数的参数(NVIC_PriorityGroup值)有下列5种:

NVIC_PriorityGroup_0 => 选择第0组

NVIC_PriorityGroup_1 => 选择第1组

NVIC_PriorityGroup_2 => 选择第2组

NVIC_PriorityGroup_3 => 选择第3组

NVIC_PriorityGroup_4 => 选择第4组

     这其实也很好理解,比如选择NVIC_PriorityGroup_1,那么抢占式优先级便占一位,也就是说可以有2^1个级别,可以设置为0和1,而响应优先级则占3位,也就是说可以有2^3个选择,可以设置为0~7;总共来说就可以区别>16种优先级了。

 //NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

 //NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

-----------------------------------------------

      假如现在有4个外部中断,还有一个EXTI9_5中断,那么如果选择优先级分组为第1组,那么抢占式优先级便只有两种,5个中断就至少有3个在抢占式优先级上是相同的优先级上,其他两个在令一优先级别。接着设置响应优先级可以有8种选择;假如现在同时有两个抢占式优先级别相同的中断发生,那么处理的顺序是谁的响应优先级高则谁优先进入中断,另外这点是需要注意的,如果此时进入这个中断之后又来了一个抢占式优先级相同但是响应优先级更高的中断,这时也是不会打断已有的中断的。

void NVIC_Config(void)

{

 NVIC_InitTypeDef NVIC_InitStructure;

 #ifdef  VECT_TAB_RAM

    //Set the Vector Table base location at 0x20000000 

    NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);

 #else

    //Set the Vector Table base location at 0x08000000 

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); 

 #endif

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  //中断优先级组 :1组(整个系统为同一组)

    // 设置先占优先级0~1,响应优先级0~7

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; 

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

    // Enable the TIM3 Interrupt

   NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;       // TIM3 全局中断

   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 先占优先级 1

   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;    // 从优先级 1

   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;       // IRQ通道被使能

   NVIC_Init(&NVIC_InitStructure);

   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0

   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;      //响应优先级0

   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

   NVIC_Init(&NVIC_InitStructure); 

}

-------------------------------------------------------------------------------------------------------------------

使用官方的库函数的DEMO,如下图:

#include "stm32f10x.h"
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t TimerPeriod = 0;
uint16_t Channel1Pulse = 0, Channel2Pulse = 0, Channel3Pulse = 0, Channel4Pulse = 0;
void RCC_Configuration(void);
void GPIO_Configuration(void);
int main(void)
{
  RCC_Configuration();
  GPIO_Configuration();
  TimerPeriod = (SystemCoreClock / 17570 ) - 1;
  Channel1Pulse = (uint16_t) (((uint32_t) 5 * (TimerPeriod - 1)) / 10);
  Channel2Pulse = (uint16_t) (((uint32_t) 375 * (TimerPeriod - 1)) / 1000);
  Channel3Pulse = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 100);
  Channel4Pulse = (uint16_t) (((uint32_t) 125 * (TimerPeriod- 1)) / 1000);
//定时器初始化 函数 见库函数 P246页
  TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIM 时钟频率除数的预分频值
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式
  TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分割
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //设置 周期计数值
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
//TIM1 配置 见 库函数 P294 页
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //脉冲宽度调制模式2
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出比较状态
  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //使能 互补 输出状态
  TIM_OCInitStructure.TIM_Pulse = Channel1Pulse; //脉冲 值
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出比较极性低
  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;//互补输出极性高
  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; //MOE=0 设置 TIM1输出比较空闲状态
  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;//MOE=0 重置 TIM1输出比较空闲状态
  TIM_OC1Init(TIM1, &TIM_OCInitStructure); //设定好的参数初始化TIM
  TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; //脉宽值
  TIM_OC2Init(TIM1, &TIM_OCInitStructure); //设定好的参数初始化TIM
  TIM_OCInitStructure.TIM_Pulse = Channel3Pulse; //脉宽值
  TIM_OC3Init(TIM1, &TIM_OCInitStructure); //设定好的参数初始化TIM
  TIM_OCInitStructure.TIM_Pulse = Channel4Pulse; //脉宽值
  TIM_OC4Init(TIM1, &TIM_OCInitStructure);//设定好的参数初始化TIM
  TIM_Cmd(TIM1, ENABLE); //使能 TIM1
  TIM_CtrlPWMOutputs(TIM1, ENABLE); //使能 TIM1 输出
  while (1)
  {}
}
void RCC_Configuration(void)
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1
  RCC_APB2Periph_GPIOA
  RCC_APB2Periph_GPIOE
  RCC_APB2Periph_GPIOB
  RCC_APB2Periph_AFIO, ENABLE);
}
void GPIO_Configuration(void)

标签: 陶瓷电阻rx

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台