嵌入式学习
第一章 什么是嵌入式系统
1. 计算机的体系架构
-
冯诺依曼架构
在完整的计算机系统中,包含五个部分,储存器,运算器,控制器输入设备和输出设备。
改进的冯诺依曼架构
改进型架构的各模块的高速数据交换中心利用储存器这个大容量,极大的提高了效率。
-
哈佛架构
哈佛结构数据空间和地址空间是分开的。
-
两者的比较
两者的区别就是程序空间和 数据空间是否一体的。冯诺依曼数据结构和地址分不开,哈佛结构数据空间和地址空间是分开的。
第二章ARM系列内核
2.1 什么是ARM
ARM是一家公司,只做CPU设计,采用出售IP的方式运行,半导体制造商,ARM同时是一种架构,一种结构体系,最新为ARM Cortex架构。
ARM体系结构特点
- 体积小,低功耗、成本低、高性能
- 支持Thumb(16位)与ARM(32位)双指令集,有良好的兼容性。
- 大量使用寄存器,执行速度快,数据操作在寄存器中完成,
- 共有37个寄存器,有7种不同的处理模式
- 寻址方式灵活简单,指令长度固定,执行效率高。
2.2 ARM指令集与Thumb指令集
- ARM指令集特点:
为典型的精简指令集。固定32位指令宽度,指令结构十分规整,便于存储,传输,解析和执行。自ARM7开始加入了Thumb指令集。 - Thumb指令集特点:
Thumb指令集是精简的16位指令集。只能完成32位标准ARM大部分功能,但是它的16位设计可以有效减少二进制代码的大小,降低对存储器容量的要求,而降低成本。但使得整个CPU更加负载,尤其是开发人员必须谨慎处理两类指令集模式的切换。
2.3 Cortex-M3处理器架构
Thumb2指令集特点:
Thumb2在前面两者之间取了一个平衡,兼有二者的优势, 当一个操作可以使用一条 32位指令完成时就使用 32位的指令,加快运行速度,而当一次操作只需要一条16位指令完成时,就使用16位的指令,节约存储空间。
2.4 STM32F103系列微控制器
命名法则:
芯片选型:(够用即可)
STM32性能与结构
第三章 ARM Cortex-M3 体系结构
3.1 Cortex-M3 结构框透视图
3.2 CM3寄存器
3.3 堆栈模式切换
-
通过异常进行堆栈切换
在线程模式主堆栈 产生异常–>进入处理模式主堆栈-异常退出>进入线程模式进行堆栈。 -
通过MSR指令修改CONTROL[1]进行堆栈切换 (一般不用)
CONTROL[1]=1;进程堆栈
=0;//主堆栈
3.4 XPSR寄存器
XPSR为程序状态寄存器组。是32位的寄存器,有三个状态。
为什么需要执行状态寄存器EPSR ?
LDM、 STM和If-then指令, 为多周期指令, 如果在执行以上多周期指令时发生异常, 处
理器会暂时停止以上指令的操作, 进入异常,
这时需要保护现场。
3.5 总线接口
有关DMA说明:借鉴(https://blog.csdn.net/as480133937/article/details/104927922?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165395856816780366585832%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=165395856816780366585832&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-104927922-null-null.142v11pc_search_result_control_group,157v12control&utm_term=DMA&spm=1018.2226.3001.4187)
3.6 存储器格式
Cortex-M3 既可使用大端格式也可使用小端格式访问存储器。
通常是以小端格式访问代码, 小端格式是ARM 处理器的默认存储器格式。
-
小端存储器系统:
在小端格式中, 数据的高字节存放在高地址中。 -
大端存储器系统:
在大端格式中, 数据的高字节存放在低地址中。
3.7位带操作
在Cortex M3 中,为了方便对位进行操作,设有位带区和位带别名区。
映射公式::
bit_word_addr = bit_band_base + (byte_offset x 32) +bit_number × 4
//位带别名区地址=位基地址+偏移地址*32+位数*4
/*例如:SRAM 位段区中地址为 0x20000300 的字节中的位 2 被映射到别名区中的地址为: 0x22006008( =0x22000000 + (0x300*32) + (2*4)) 的字。*/
3.8 异常与中断
在 ARM 编程领域中,凡是打断程序顺序执行的事件,都被称为异常(exception),中断是异常的一种程序代码也可以主动请求进入异常状态的(常用于系统调用)。也就是CPU需要立刻处理的内部/外部事件。
所有的 Cortex-M处理器都会提供一个用于中断处理的嵌套向量中断控制器(NVIC)。除了中断请求,还有其他需要服务的事件,将其称为“异常”。
在中断中,会有堆栈占用。 原因是保存返回地址和寄存器上下文。寄存器入栈又CPU硬件自动完成。
中断和轮询:
- 中断:(用专门的中断服务程序来处理事件)
-适于处理对响应要求非常高的事件
-适于处理持续事件非常短的事件
-适于低功耗的应用
-程序设计较复杂 - 轮询(会消耗大量的CPU处理时间,可能会丢失关键事件)
-适于处理对时间响应要求低的场合
-程序设计简单
中断的允许与禁止
全局中断控制 – enable/disable ALL interrupts
-CPU的CCR寄存器中一个特殊位
-在复位后,全局禁止位是置起的
-中断发生后,全局禁止位也被置起
-通常在复位后所有的中断都被禁止了
中断向量表:
- 中断向量表是一段连续的存储空间
- 在复位后有默认的起始位置
- 每个中断在向量表中都有相应的表项,该表项的值为该中断对应的服务程序的地址(地址指针)
- 由程序代码确定中断向量表的每个表项
- 中断向量表的位置是可以通过改写中断向量基址寄存器重新定位的
中断向量表的作用:
每一种中断发生对应要处理的函数入口地址填在这里。 CPU就知道对应中断发生的时候要做什么操作 。
根据这个表去查程序的入口地址就能知道应该执行什么样的中断子程序。
中断、异常过程:
占先:
在异常处理程序中, 一个新的异常比当前的异常优先级更高, 处理器打断当前的流程, 响应优先级更高的异常, 此时产生中断嵌套。
末尾连锁:
一个中断返回后, 系统立即处理挂起的中断。能够加快中断响应。
迟来:
迟来是处理器用来加速占先的一种机制。
前一个中断尚未进入执行阶段, 迟来中断优先级比前一个
中断高, 则抢占前一个中断得到优先服务
第四章 STM32F103系列
- 最小系统原理图
第五章 KEIL5的使用与STM32cubemx软件
具体内容,SCND搜索即可。
第六章GPIO原理及应用
6.1 寄存器存储映射
存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射。
片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设, APB1挂载低速外设, APB2 和 AHB 挂载高速外设。相应总线的最低地址我们称为该总线的基地 址,总线基地址也是挂载在该总线上的首个外设的地址。其中 APB1 总线的地址最低,片上外设从这里开始,也叫
6.2 外设寄存器
每个外设寄存器为32bit,占四个字节。
具体外设寄存器作用,见《1-STM32F10x-中文参考手册.pdf》第八章GPIO寄存器的描述。
6.3 GPIO的输入与输出:
- 推挽输出与开漏输出:
-
推挽输出
推挽输出模式,是根据这两个 MOS 管的工作方式来命名的。在该结构中输入高电平时,经过反向后,上方的 P-MOS 导通,下方的 NMOS关闭,对外输出高电平;而在该结构中输入低电平时,经过反向后,N-MOS 管导通, P-MOS 关闭,对外输出低电平。 当引脚高低电平切换时,两个管子轮流导通, P 管负责灌电流, N 管负责拉电流,使其负载能力和开关速度都比普通的方式有很大的提高。
使用场合:推挽输出模式一般应用在输出电平为 0 和 3.3 伏而且需要高速切换开关状态的场合。 在STM32的应用中,除了必须用开漏模式的场合,我们都习惯使用推挽输出模式 -
开漏输出
在开漏输出模式时,上方的 P-MOS 管完全不工作。如果我们控制输出为 0,低电平, 则 P-MOS管关闭, N-MOS管导通,使输出接地,若控制输出为 1 (它无法直接输出高电平) 时,则 P-MOS 管和 N-MOS 管都关闭,所以引脚既不输出高电平,也不输出低电平,为高阻态。
特点:正常使用时必须外部接上拉电阻, 开漏输出具有“线与”特性, 也就是说,若有很多个开漏模式引脚连接到一起时,只有当所有引脚都输出高阻态,才由上拉电阻提供高电平,此高电平的电压为外部上拉电阻所接的电源的电压。若其中一个引脚为低电平,那线路就相当于短路接地,使得整条线路都为低电平, 0伏。
- GPIO结构分析
6.4 外设寄存器使用方法:
/********ODR寄存器输出高低电平********/
#define GPIO_ODR *(unsiged int*)(0x40010C0C)//
//GPIOB基地址 &GPIOB=0x40010C00;
//ODR偏移地址 &ODR=0x0C;
GPIO_ODR|= (1<<4);//第四个引脚输出高电平
GPIO_ODR&= ~( 1<<2 );//第二个引脚输出低电平
点亮LED的三个步骤:
- 开启外设时钟
- 配置GPIO模式
- 控制引脚电平
/************寄存器方式******/
// 开启 GPIOC 端口 时钟
*(unsigned int *)0X40021018 |= (1<<4);//RCC_APB2ENR寄存器
#define GPIOC_CRL *(unsiged int*)(0X40011000)//
// 清空控制 PC2 的端口位
2 *(unsigned int *)GPIOC_CRL &= ~( 0x0F<< (4*2));
3 // 配置 PC2 为通用推挽输出,速度为 10M
4 *(unsigned int *)GPIOC_CRL |= ( 1<<(4*2) );
/*******GPIO标准库函数初始化****/
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitTStruct;
/*开启GPIO相关外设时钟*/
RCC_APB2PeriphClockCmd(LED1_GPIO_CLK|LED2_GPIO_CLK, ENABLE);
/*定义GPIO结构体成员*/
GPIO_InitTStruct.GPIO_Pin = LED1_GPIO_PIN;//初始化引脚
GPIO_InitTStruct.GPIO_Mode = GPIO_Mode_Out_PP;//输出模式
/*typedef enum
{ GPIO_Mode_AIN = 0x0,//模拟输入
GPIO_Mode_IN_FLOATING = 0x04,//浮空输入
GPIO_Mode_IPD = 0x28,//带下拉输入
GPIO_Mode_IPU = 0x48, //带上拉输入
GPIO_Mode_Out_OD = 0x14,//开漏输出
GPIO_Mode_Out_PP = 0x10,//推挽输出
GPIO_Mode_AF_OD = 0x1C,//复用开漏输出
GPIO_Mode_AF_PP = 0x18//复用推挽输出
}GPIOMode_TypeDef;*/
GPIO_InitTStruct.GPIO_Speed = GPIO_Speed_10MHz;
/*typedef enum
{
GPIO_Speed_10MHz = 1,//10MHz速度
GPIO_Speed_2MHz,//20MHz速度
GPIO_Speed_50MHz,//50MHz速度
}GPIOSpeed_TypeDef;*/
/*初始化LED1*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitTStruct);//初始化
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);//写入
/***LED2相同****/
}
void delay(uint32_t count)
{
for(; count!=0; count--);
}
//GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);//写入引脚状态
//GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);//清楚状态
第七章RCC时钟配置
7.1时钟特点
- 时钟是指令执行的基本时间间隔,时钟频率高,意味着CPU执行运算的能力强看门]狗/定时器/异步通信等都依赖稳定的时钟
- MCU内部是一个统一的时钟树,外设的时钟是从系统时钟分频得到的。
- 时钟通常由外部晶体或振荡器提供,使用外部的配置引|脚在复位时选择时钟输入源。
- 广泛应用锁相环技术,将外部较低频率的时钟提高成内部较高频率的时钟(better EMC&EMI)、
STM32一共有5个时钟源HSI, HSE, LSI, LSE,PLL。
7.2 时钟配置:
编程要点对应着时钟树图中的序号。
• 1、开启 HSE/HSI
• 2、设置PLLXTPRE(HSE或HSE/2)
• 3、 打开PLL, 设置 PLL的时钟来源,和 PLL的倍频因子
• 4、 选择 PLLCK为系统时钟 SYSCLK
• 5、选择要配置的总线时钟
• 6、设置 AHB、 APB2、 APB1的预分频因子
7.3 时钟初始化
void SystemClock_Config(void)/******时钟初始化 ********/
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
/**Initializes the CPU, AHB and APB busses clocks */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
第八章STM32中断应用
8.1 异常类型
F103 在内核水平上搭载了一个异常响应系统, 支持为数众多的系统异常和外部中断。其中系统异常有 8 个(如果把 Reset 和 HardFault 也算上的话就是 10 个),外部中断有 60个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。
8.2 NVIC介绍
NVIC 是嵌套向量中断控制器, 控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设.
- NVIC特点:
- 68个可屏蔽中断通道(不包含16个Cortex™-M3的中断线);
- 16个可编程的优先等级(使用了4位中断优先级);
- 低延迟的异常和中断处理;
- 电源管理控制;
- 系统控制寄存器的实现;
- 嵌套向量中断控制器(NVIC)和处理器核的接口紧密相连,可以实现低延迟的中断处理和高效地 处理晚到的中断。
-
NVIC寄存器设置
typedef struct { 2 __IO uint32_t ISER[8]; // 中断使能寄存器 3 uint32_t RESERVED0[24]; 4 __IO uint32_t ICER[8]; // 中断清除寄存器 5 uint32_t RSERVED1[24]; 6 __IO uint32_t ISPR[8]; // 中断使能悬起寄存器 7 uint32_t RESERVED2[24]; 8 __IO uint32_t ICPR[8]; // 中断清除悬起寄存器 9 uint32_t RESERVED3[24]; 10 __IO uint32_t IABR[8]; // 中断有效位寄存器 11 uint32_t RESERVED4[56]; 12 __IO uint8_t IP[240]; // 中断优先级寄存器(8Bit wide) 13 uint32_t RESERVED5[644]; 14 __O uint32_t STIR; // 软件触发中断寄存器 15 } NVIC_Type; /* 2 * 配置中断优先级分组:抢占优先级和子优先级 3 * 形参如下: 4 * @arg NVIC_PriorityGroup_0: 0 bit for 抢占优先级 5 * 4 bits for 子优先级 6 * @arg NVIC_PriorityGroup_1: 1 bit for 抢占优先级 7 * 3 bits for 子优先级 8 * @arg NVIC_PriorityGroup_2: 2 bit for 抢占优先级 9 * 2 bits for 子优先级 10 * @arg NVIC_PriorityGroup_3: 3 bit for 抢占优先级 11 * 1 bits for 子优先级 12 * @arg NVIC_PriorityGroup_4: 4 bit for 抢占优先级 13 * 0 bits for 子优先级 14 * @注意 如果优先级分组为 0,则抢占优先级就不存在,优先级就全部由子优先级控制 15 */void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) 17 { 18 // 设置优先级分组 19 SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; 20 }
-
中断编程特点
- 使能外设某个中断,这个具体由每个外设的相关中断使能位控制。
- 设置中断优先级分组
- 始化 NVIC_InitTypeDef 结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求
- 写中断服务函数
-
程序编写
//NVIC 初始化结构体 1 typedef struct { 2 uint8_t NVIC_IRQChannel; // 中断源 3 uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级 4 uint8_t NVIC_IRQChannelSubPriority; // 子优先级 5 FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能 6 } NVIC_InitTypeDef; static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* 配置NVIC优先级组为1 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 配置中断源:按键1 */ NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; /*配置抢占优先级 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 配置子优先级 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 势能中断通道*/ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
第九章EXTI外部中断/事件控制器
9.1EXTI简介
EXTI(External interrupt/eventcontroller) —外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿 检测和下降沿的检测。 EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
9.2 中断事件线
EXTI 有 20 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另外几根用于特定的外设事件 。EXTI(0-15)---->引脚A-I(pin0-pin15)
9.3 EXTI程序实现
- 编程要点
- 初始化用来产生中断的 GPIO;
- 初始化 EXTI;
- 配置 NVIC;
- 编写中断服务函数
-
程序实现
/*EXTI 初始化结构体 1 typedef struct { 2. uint32_t EXTI_Line; // 中断/事件线 3. EXTIMode_TypeDef EXTI_Mode; // EXTI 模式 4. EXTITrigger_TypeDef EXTI_Trigger; // 触发类型 5 FunctionalState EXTI_LineCmd; // EXTI 使能 6 } EXTI_InitTypeDef;*/ /*****采用HAL库编辑**/ void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); /*Configure GPIO pins : PFPin PFPin PFPin PFPin */ GPIO_InitStruct.Pin = LED1_Pin|LED2_Pin|LED3_Pin|LED4_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); /*Configure GPIO pins : PGPin PGPin PGPin PGPin PGPin */ GPIO_InitStruct.Pin = JOY_SEL_Pin|JOY_DOWN_Pin|JOY_RIGHT_Pin|JOY_LEFT_Pin |JOY_UP_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;//上升沿出发// GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0)//中断线 抢占优先级 子优先级 HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);//使能 HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); } /********在stm32f1xx_it.c中***/ void EXTI9_5_IRQHandler(void) { /* USER CODE BEGIN EXTI9_5_IRQn 0 */ /* USER CODE END EXTI9_5_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);//处理引脚7中断线 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8); /* USER CODE BEGIN EXTI9_5_IRQn 1 */ /* USER CODE END EXTI9_5_IRQn 1 */ } void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)/****HAL库自带*/ { /* EXTI line interrupt detected */ if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)//__HAL_GPIO_EXTI_GET_IT判断事是否产生中断标志位,返回SET和RESET,具体见EXTI_PR寄存器 { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); HAL_GPIO_EXTI_Callback(GPIO_Pin);//修改回调函数 } } HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { //中断编写内容 }
第十章 定时器
10.1 SYSTICK—系统定时器
-
定义:SysTick—系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器 是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置 系统时钟 SYSCLK等于 72M。当重装载数值寄存器的值递减到 0的时候,系统定时器就产生一次中断,以此循环往复。因为 SysTick 是属于 CM3 内核的外设,所以所有基于CM3 内核的单片机都具有这个 系统定时器,使得软件 CM3单片机中可以很容易的移植。系统定时器一般用于操作系统, 用于产生时基,维持操作系统的心跳。
-
四个寄存器
-
计算与程序实现
/*******计算公式 T=reload*(1/clk);//clk 为8MHz 或者72MHz //例如reload=72000,为1ms
/*通过查询CTRL的特定位数*/ void SysTick_Delay_ms(uint32_t ms) { uint32_t i; SysTick_Config(72000);//1ms for(i=0; i<ms; i++) { //查询Systick的cTRL寄存器 //查看第16位是否为1,若不是则表面reload没有减到零,若是则跳出while,进入下一个for while( !((SysTick->CTRL) & (1<<16)) ); } /*做足够个循环后失能systick*/ SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; } volatile uint32_t isr_ms; /*通过中断服务函数实现要求*/ void SysTick_Delay_ms_INT(uint32_t ms) { isr_ms = ms; SysTick_Config(72000); while(isr_ms); /* 失能 */ SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; }
10.2 TIM定时器
-
定时器分类
-
基本定时器
-
功能介绍:
1-计数器16bit, 只能向上计数,只有TIM6和TIM7
2-没有外部的GPIO,是内部资源, 只能用来定时
3-时钟来自PCLK1,为72M,可实现1~65536分频 -
基本定时器功能框图
-
定时时间的计算
1、 PSC = 72-1,定时器频率=72M/(PSC+1)=1MHZ
2、 ARR = 1000-1,从0计数到999,则计了1000次
3、中断周期T = 1000 *1/1000000 = 1mS -
程序实现定时
typedef struct { uint16_t TIM_Prescaler;//分频因子 uint16_t TIM_CounterMode;//计数模式,基本定时器只能向上计数 uint32_t TIM_Period;//自动重装载值 uint16_t TIM_ClockDivision;//外部输入时钟分频因子,基本定时器没有 uint8_t TIM_RepetitionCounter;//重复计时器,基本定时器没有,高级定时器专用 }TIM_TimeBaseInitTypeDef;
-
高级定时器
-
高级定时器功能
1-计数器16bit,上/下/两边 计数, TIM1和TIM8,还
有一个重复计数器RCR,独有。
2-有4个GPIO,其中通道1~3还有互补输出GPIO
3-时钟来自PCLK2,为72M,可实现1~65536分频 -
引脚分布:
-
高级定时器功能框图
-
框图讲解:
a. 时钟源:
1-内部时钟源CK_INT
2-外部时钟模式1—外部的GPIO Tix(x=1 2 3 4)
3-外部时钟模式2—外部的GPIO ETR
4-内部触发输入 b. 内部时钟源
1-内部时钟源来自RCC的TIMx_CLK c. 外部时钟1
d. 外部时钟2
-
-
控制器
1-控制器就是用来控制的,发送命令的
2-CR1、 CR2、 SMCR、 CCER,主要学习这几个寄存
器即可。 -
时基单元
- 时基单元组成 1-16位的预分频器 PSC, PSC 2-16位的计数器CNT, CNT 3-8位的重复计数器RCR, RCR(高级定时器独有) 4-16位的自动重装载寄存器ARR, ARR
-
预分频器
- 预分频器PSC
预分频器PSC,有一个输入时钟CK_ PSC和一个输出时钟CK_ CNT。输入时钟
CK_ PSC就是上面时钟源的输出,输出CK_ CNT则用来驱动计数器CNT计数。通过设置
预分频器PSC的值可以得到不同的CK CNT,实际计算为: fok _CN等于
fck psc(PSC[15:0]+1),可以实现1至65536分频。
- 预分频器PSC
-
计数器(上/下/两边)
- (1) 递增计数模式下,计数器从0开始计数,每来一个CK_ CNT脉冲计数器就增加1,直到计数器的值与自动重载寄存器ARR值相等,然后计数器又从0开始计数并生成计数器上溢事件,计数器总是如此循环计数。如果禁用重复计数器,在计数器生成上溢事
件就马上生成更新事件(UEV); - 递减计数模式下, 计数器从自动重载寄存器ARR值开始计数,每来-一个CK CNT脉冲计数器就减1,直到计数器值为0,然后计数器又从自动重载寄存器ARR值开始递减计数并生成计数器下溢事件,计数器总是如此循环计数。如果禁用重复计数器,在计数器生成下溢事件就马上生成更新事件;如果使能重复计数器,每生成- -次下溢事件重复计数器内容就减1,直到重复计数器内容为0时才会生成更新事件.
- 中心对齐模式下,计数器从0开始递增计数,直到计数值等于(ARR-1)值生成计数器上溢事件,然后从ARR值开始递减计数直到1生成计数器下溢事件。然后又从0开始计数,如此循环。每次发生计数器上溢和下溢事件都会生成更新事件。
- (1) 递增计数模式下,计数器从0开始计数,每来一个CK_ CNT脉冲计数器就增加1,直到计数器的值与自动重载寄存器ARR值相等,然后计数器又从0开始计数并生成计数器上溢事件,计数器总是如此循环计数。如果禁用重复计数器,在计数器生成上溢事
-
自动重装载寄存器
自动重载寄存器ARR自动重载寄存器ARR用来存放与计数器CNT比较的值,如果两个值相等就递减重复计数器。可以通过TIMx_ CRI寄存器的ARPE位控制自动重载影子寄存器功能,如果ARPE位置1,自动重载影子寄存器有效,只有在事件更新时才把TIMx_ ARR值赋给影子寄存器。如果ARPE位为0,则修改TIMx ARR值马上有效。
-
输入捕获的作用和原理
输入捕获可以对输入的信号的上升沿,下降沿或者 双边沿进行捕获,常用的有测量输入信号的脉宽和测量PWM输入信号的频率和占空比这两种。输入捕获的大概的原理就是,当捕获到信号的跳变沿的时候,把数器CNT的值锁存到捕获寄存器CCR中,把前后两次捕获到的CCR寄存器中的值相减,就可以算出脉宽或者频率。如果捕获的脉宽的时间长度超你的捕获定时器的周期,就会发生溢出,这个我们需要做额外的处理。- 输入通道
当使用需要被测量的信号从定时器的外部引脚TIMx_CH1/2/3/4 迚入,通常叫 TI1/2/3/4,在后面的捕获讲解中对于要被测量的信号我们都以 TIx 为标准叫法。 - 输入滤波和边沿检测
- 捕获通道
- 预分频器
- 捕获寄存器
- 输入通道
-
输出比较
-
输出比较的作用
输出比较就是通过定时器的外部引脚对外输出控制信号,有冻结、将通道X(x=1,2,3,4)设置为匹配时输出有效电平、将通道X设置为匹配时输出无效电平、翻转、强制变为无效电平、强制变为有效电平、PWM1和PWM2这八种模式,具体使用哪种模式由寄存器CCMRx的位0CxM[2:0]配置。其中PWM模式是输出比较中的特例,使用的也最多。文章来源:https://www.toymoban.com/news/detail-471411.html
-
文章来源地址https://www.toymoban.com/news/detail-471411.html
/*******定义定时器***********/
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 32;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.Pulse = 64;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.Pulse = 0;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sConfigOC.Pulse = 32;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_TIM_MspPostInit(&htim2);
}
/*******基本定时器*************/
static void BASIC_TIM_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//开启定时器时钟,即内部时钟CK_INT=72M
BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
//自动重装载寄存器的值,累计TIM_Peiod+1个频率后产生一个更新或者中
TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;
/时钟分频因子,基本定时器没有,不用管
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);
// 清楚计数器中断标志位
TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);// 开启计数器中断
TIM_Cmd(BASIC_TIM, ENABLE); // 使能计数器
}
到了这里,关于嵌入式学习stm32基础知识(期末复习)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!