#ARM含义
1.是个公司
2.是一类处理器
3.一种技术的名称
ARM公司只设计芯片,不生产芯片
#ARM处理器内核
早期 ARM7 9 11家族 ,之后的命名就都是Cotex系列了
M系列 0 1 3 4 7
面向低成本单片机控制场合
A系列 5 7 8 9 15 53 57
面向操作系统的高性能处理场合
R系列 4 5 7
面向实时性较高的控制场合
#ARM处理器架构
体系结构定义指令集和基于这一体系结构下处理器的编程模型(基本数据类型、工作模式、寄存器组),基于同种体系结构可以有多重处理器,每个处理器的性能不同,面向的领域也不同
V1-V3 基本废弃
V4-V6 经典处理器中运用比较多 V6:ARM1136
V7 Cotex系列处理器主要是这种架构,支持Thumb-2的32位的指令集 A R M系列
V8 兼容ARMv7的特性,并支持64位数据处理
ARMV6-M 在V6的基础上结合了V7的一些特性生产出来的芯片,比如Cotex-M0 M1
#ARM指令集
RICS 精简指令集,格式和长度固定
CISC 复杂指令集,指令长度不固定
#SOC
片上系统,单个芯片上集成一个完整的计算机系统,包括CPU,存储器,外围电路等
CPU核 电源和时钟,片内存储器,存储控制器,定时器,看门狗,UART I2C控制器 USB控制器 中断在控制器 DMA控制器
#M的常见厂商
意法半导体 ST
恩智浦 NXP
德州仪器 TI
新唐科技 nuvoton
#M0概述
#处理器简介
M0 主要用于低功耗和混合信号处理
M3 用来代理AMR7
M7 重点放在高性能领域
#M0结构框图
M0微处理器主要包括
处理器内核
负责处理逻辑运算等相关工作
嵌套向量中断控制器(NVIC)
负责处理外部中断相关工作
唤醒中断控制器(WIC)
当外设没有工作时,整个系统会进入休眠,在休眠中出现了外设的中断,则通知电源管理单元重新唤醒系统重新开始工作
调试子系统
负责调试状态下的运行
内部总线
Cortex-M0 微处理器通过精简的高性能总线(AHB-LITE)与外部进行通信。
内核通过内部的单总线系统的总线单元接口与存储器和外设进行交互,因为是单总线,所以是冯诺依曼架构,M3是哈佛结构
#M0特性
#M0工作模式和工作状态
线程模式:芯片复位后,进入线程模式,执行用户程序
处理模式:发生中断或者异常,进入处理模式处理,处理完返回线程模式
两者的主要区别可能是操作的寄存器存在区别
Thumb状态: 正常运行的状态
调试状态: 调试程序时处理的状态
#M0寄存器组
13个通用寄存器和多个特殊寄存器
R0-R12 为通用寄存器,R0-R7为低端寄存器,可以作为16位或者32位指令操作数,R8-R12为高端寄存器,只能用作32位操作数
R13 堆栈指针SP,有主栈指针MSP和进程栈指针PSP。处理模式下只能使用主栈指针,在线程模式下,可以使用主堆栈也可以使用进程堆栈,这主要是由 CONTROL 寄存器控制完成。系统上电的默认栈指针是MSP
R14 连接寄存器(LR),用于存储子程序或者函数调用的返回地址
R15 程序计数器(PC),存储下一条将要执行的指令的地址。
#寄存器组
xPSR:组合程序状态寄存器,该寄存器由三个32位的程序状态寄存器组成
应用PSR(APSR,28-31位有效,其他位保留):包含前一条指令执行后的条件标志,比如进位什么的
中断PSR(IPSR,0-5位有效,其他位保留):记录着中断号,当发生中断的时候,可以读取这个知道当前发生的是哪个中断,然后去做对应处理
执行PSR(EPSR, 只有24位有效) : 包含Thumb状态位,就是程序运行的ARM状态
PRIMSK:中断屏蔽特殊寄存器。
CONTROL:控制寄存器
控制处理器处于线程模式是,使用哪个堆栈
=0,使用MSP 上电默认使用的是MSP
=1,使用PSP 处理器模式时,固定使用MSP
#异常和中断
最多32个外部中断IRQ和1个不可屏蔽的NMI
一般吧外部中断叫做中断,把内核自己的中断叫做异常
#M0指令集
ARM 处理器支持两种指令集:ARM 和 Thumb。
EPSR 寄存器的 T 标志位负责指令集的切换,Cortex-M0只支持Thumb指令。
ARM指令集
32位精简指令集;
指令长度固定;
降低编码数量产生的耗费,减轻解码和流水线的负担;
Thumb指令集
Thumb指令集是ARM指令集的一个子集;
指令宽度16位;
与32位指令集相比,大大节省了系统的存储空间;
Thumb指令集不完整,所以必须配合ARM指令集一同使用。
注:Thumb 与 ARM 相比,代码体积小了 30%,但性能也低了 20%。2003 年,ARM 公司引入了 Thumb-2 技术,具备了一些 32 位的 Thumb 指令,使得原来很多只有 ARM 指令能够完成的功能,用 Thumb 指令也可以完成了。Cortex-M0 基于的 ARMv6-M 体系结构,该体系结构的处理器只是用了16位Thumb指令和部分32位Thumb指令
#产品介绍
超低功耗 L0 L1 L4
主流型 F0 F1 F3
超高性能 F2 F4 F7
处理器 M0 M3 M4 M7
STM32 F 051 K 8 U 6
STM32 定位 型号 引脚 FLASH大小 封装类型 工作范围
#STM32存储器映射
#GPIO
通用输入输出接口,是最常用最重要的接口,一般将GPIO分为多组,每组最多16个管脚,每个管脚都有一套对应的功能寄存器去设置对应功能,根据功能可以实现LED,按键等设备控制驱动,也可实现串口收发管脚,AD的接口等。从参考手册可以查阅,每个IO口具体支持哪些功能,以stm32f051k8芯片为为例,一个IO口最多支持0-7共八种功能。
#常用寄存器
32位寄存器控制16个引脚,也就是寄存器里每2/1位控制一个引脚
每个IO口都有一组这样的寄存器
GPIOX_OSPEEDR
IO口输出速度寄存器,0-31有效,默认00
IO口波形的最大承载能力设置,设置越高,速度越快,承载能力越大,相对的功耗也越大。
x0 低速 2MHZ
01 中速 10MHZ
11 高速 50MHZ
GPIOX_PUPDR
上下拉控制寄存器,0-31有效,默认00
STM32内部有上下拉的电阻,根据上下拉的设置,默认输出高低电平,如果不设置,默认是浮空状态,此时高低电平默认状态有外部电路决定,例如外接上下拉
00 无上无下 01 上拉 10 下拉 11 保留
GPIOX_ODR
输出数据寄存器,0-15有效, 默认00
IO口设置为输出模式下,该值表示当前引脚希望输出的高低电平状态
2个32位复位功能配置寄存器
GPIOX_AFRH
GPIOX_AFRL
复用功能寄存器,默认0000,共64位,每4位控制一个引脚,每个引脚有八种工作模式,需要设置为不同功能时,请查阅对应手册
#寄存器控制灯
所有的外设都需要时钟的支持,所有的GPIO口都是挂载AHB2这条总线上的。
通过设置RCC_AHBENR寄存器打开对应GPIO的时钟
void led_init(void)
{
RCC->AHBENR |= 1<<18;
GPIOB->MODER |= (0x01 << 0) | (0x01 << 2) | (0x01 << 4); //设置为输出
GPIOB->OTYPER = 0x00; //推挽输出
GPIOB->OSPEEDR = 0x00; //低速模式
GPIOB->PUPDR = 0x00; //无上下拉
}
void led_set(uint8_t t)
{
GPIOB->BSRR = (0x01 << t);
}
void led_reset(uint8_t t)
{
GPIOB->BSRR = (0x01 << (t+16));
}
led_reset(1);
HAL_Delay(500);
led_set(0);
#库函数控制灯
常用的有标准库,HAL库和LL库
具体区别见:https://blog.csdn.net/zcshoucsdn/article/details/54613202
库函数初始化简易说明:
GPIO_InitTypeDef GPIO_InitStruct; //构建一个包含GPIO所有通用寄存器的结构体。
__HAL_RCC_GPIOB_CLK_ENABLE(); //初始化GPIOB的时钟
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET);
//初始化引脚其实是进行了一次高低电平设置
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2; //设置引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; //无上下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //低速输出
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //设置对应引脚
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); //设置GPIO口引脚
HAL_Delay(200); //HAL库毫秒演示函数
#按键
SN74HC32D 四路二输入正或门 输出=输入|输出
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)) //判断按键是否按下
{
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)); //判断按键是否放回
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); //翻转LED
}
#串口
同步通信:收发双方在同一时钟下进行通信
异步通信:通信的主体使用不同的时钟,双方调整到相同的时钟下通信
串行通信:1根信号线的通信,因此同一时刻只能收发一个Bit的位信息
并行通信:多跟信号线的通信,同一时刻可以收发多个Bit的位信息
单工: 任何时候只能收,或者发
半双工: 可以收发,但同一时间只能收或者发
全双工: 同一时刻既能收也能发
USART:通用同步异步收发器(USART)灵活地与外部设备进行全双工数据通信,满足外部设备对工业标准NRZ异步串行数据格式的要求。USART通过小数波特率发生器提供了多种波特率。它支持同步单向通信和半双工单线通信;还支持LIN(局域互联网络),智能卡协议与IrDA(红外数据协会)SIR ENDEC规范,以及调制解调操作(CTS/RTS)。而且,它还支持多处理器通信。USART支持同步模式,因此USART 需要同步始终信号USART_CK(如STM32 单片机),通常情况同步信号很少使用,因此USART和一般单片机UART使用方式是一样的,都使用异步模式
TTL高电平3.65V,低电平0V2.4V
RS-232C标准采用EIA电平,规定:高电平在-3V-15v之间,低电平在+3V+15V之间。
STM32F051有两组串口,最大传输速率可以达到6M,但因为串口的不稳定,所以一般很少使用这么高的频率。
#波特率
在串行通信中,用“波特率”来描述数据的传输速率。所谓波特率,既每秒传送的二进制位数,其单位为bps(bits per second)。它是衡量串行数据速度快慢的重要指标。一般9600个115200使用的最多。
#串口协议
串行接口间按bit进行发送或者接收一段字符流数据,该数据中包含了双方约定好顺序的:起始位、数据位、校验位和停止位,该约定便是串口通信协议。常见的串口协议有RS232,RS422和RS485等。
默认情况下TX/RX为高电平,一般以检测到下降沿或第一个低电平作为起始位,然后依照双方约定的频率进行8-9位的逐位传送,每次传送完之后,根据协议可以选择传送奇偶检验位,当传递完成后,以1-2位的停止位做为结束条件,串口传送时,低位先发送
#USART寄存器 具体查看数据手册
USART_CR1 控制寄存器1
b6 发送完毕中断使能 b5 接收寄存器非空中断使能
b12 决定串口字长
0 1个起始位,8个数据位,n个停止位
1 1个起始位,9个数据位,n个停止位
B9 校验选择 1奇校验 0偶校验
USART_CR2 控制寄存器2
b12-13 这些位用来指定停止位的个数
00 1个停止位 01 保留 10 2个停止位 11 1.5个停止位
USART_SR 状态寄存器
USART_DR 数据寄存器
USART_BRR 波特率寄存器
USART_ISR 中断状态寄存器
描述当前传送状态,b5 接收寄存器非空,b7 发送寄存器空
#串口发送接收过程
串口收发数据一般有两周,第一种使用FIFO将发送数据保存下来再进行发送,第二种直接将一个字节的数据送到TDR寄存器里,控制系统检测到ODR中有数据,就会将数据进行发送,同理通过ROR寄存器进行接收。在这个过程中,使用各种寄存器去标志当前的状态。
#串口的实现
STM32F051K8有两个串口,这里使用的是串口1,也就是PA9和PA10引脚
配置时注意将串口1的配置信息设置为第二个,也就是异步通信
void Uart_Putc(uint8_t c)
{
while(!(USART1->ISR & 0x01<<7)); //当发送寄存区为空
USART1->TDR = c;
}
uint8_t Uart_Getc(void)
{
while(!(USART1->ISR & 0x01<<5)); //当接收寄存器非空
return USART1->RDR;
}
ch = Uart_Getc();
Uart_Putc(ch);
HAL_UART_Receive(&huart1, ch, 31, 100);
HAL_UART_Transmit(&huart1, ch, strlen((const char *)ch), 100);
memset(ch, 0, sizeof(ch));
流程:
重写printf函数,一般printf是打印到屏幕上,这里重写fputc方法,让printf打印到串口
int fputc(int ch, FILE *f)
{
while(!(USART1->ISR & 0x01<<7));
USART1->TDR = ch;
return 0;
}
printf("%s", "hello wordl!");
#中断
#中断的概念
处理器中的中断
在处理器中,中断是一个过程,即CPU在正常执行程序的过程中,遇到外部/内部的紧急事件需要处理,暂时中止当前程序的执行,转而去为处理紧急的事件,待处理完毕后再返回被打断的程序处继续往下执行。中断在计算机多任务处理,尤其是即时系统中尤为重要。比如uCOS,FreeRTOS等。
意义
中断能提高CPU的效率,同时能对突发事件做出实时处理。实现程序的并行化,实现嵌入式系统进程之间的切换
进入中断
处理器自动保存现场到堆栈里
{PC, xPSR, R0-R3, R12, LR}
一旦入栈结束,ISR便可开始执行
晚到的中断会重新取ISR地址,但无需再次保存现场
退出中断
中断前的现场被自动从堆栈中恢复
一旦出栈完成,继续执行被中断打断的指令
出栈的过程也可被打断,使得随时可以响应新的中断,而不再进行
现场保存
#外部中断控制器EXTI
STM32F051x中,共有28中断/事件线,GPIO连接到前16个外部中断/事件线
每个EXTIx都连接着同编号的6个GPIO引脚,也就是说同一个编号的GPIO同时只能有一个可以将中断送入NVIC进行处理
流程步骤:
设置串口为异步通信,设置按键为外部中断,在NVIC里打开引脚使能,从中断向量表里寻找对应的回调函数,然后重写回调函数,因为中断地址有限,多个引脚都会触发同一个中断函数,在中断函数里进行判断是哪个引脚
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_8)
{
HAL_UART_Transmit(&huart1, "interrupt\n", strlen("interrupt\n"), 100);
}
}
流程步骤:
设置串口为异步通信,设置串口波特率及参数,打开串口中断使能,打开NVIC使能,然后从中断向量表里寻找对应回调函数,然后重写回调函数,然后通过指定的串口接收发送函数来触发中断
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
printf("´®¿Ú·¢ËÍÍê³É\n");
}
}
extern uint8_t buf[12];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
printf("%c %c", buf[0], buf[1]);
HAL_UART_Receive_IT(&huart1, buf, 2); //每次想触发都得调用这个函数
}
}
HAL_UART_Transmit_IT(&huart1, "T", 1); //通过这个函数发送,发送完成后会出发中断
HAL_UART_Receive_IT(&huart1, buf, 2); //通过这个函数接收,接收完成后触发中断
#时钟系统
时钟系统是由振荡器、定时唤醒器、分频器等组成的电路。常用的信号源有晶体振荡器和RC振荡器。
振荡器也就是时钟源,外部链接到一个开关和一个唤醒定时器,唤醒定时器可以打开或关闭开关,如果关闭了,系统就可能进入睡眠状态,等有外部时间的时候,又可以唤醒整个系统。CPU内核工作的频率比较高,那么就通过PLL倍频器电路把外部比较低的频率倍频到一个比较高的频率。除了CPU之外又有很多工作较低频率的外设,因此有需要通过VPB分频器电路等将较高频率降低到较低频率
问题:为什么要倍频而不直接使用高频
主频=外频×倍频
想要达到高频是非常难做到的,这是指外频,曾经是没有倍频一说的,当时外频就等于主频(倍频为1),但是后来人们发现,20MHZ-100MHZ好提升,但是120MHZ-150MHZ就非常难了,随着频率的提升,稳定性急剧变差,功耗和发热急剧提升,那么人们想提升性能自然会去走其他的路,后来就有了倍频这么一个东西,具体怎么实现的挺复杂的,你可以百度百科一下。
常用的时钟源有晶体振荡器和RC/LC振荡器。
1.RC由电阻电容构成的震荡电路,能够将直流电转换为具有一定频率交流信号输出的电子电路或装置。因此成本低,但由于电阻电容精度问题导致振荡频率的误差大,同时温度湿度等环境影响导致存在温漂等误差。
2.石英晶体振荡器是高精度和高稳定度的振荡器,被广泛应用于彩电、计算机、遥控器等各类振荡电路中,以及通信系统中用于频率发生器、为数据处理设备产生时钟信号和为特定系统提供基准信号。震荡频率稳定,精度高,但价格贵,使用时还需要接15-33PF的起振电容。
一般SOC外部都使用晶体,但有些内部会集成一些RC,在外部不工作的时候,内部还可以工作。
STM32时钟源
HSI:高速内部时钟,RC振荡器,频率为8MHz;
HSE:高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz
LSI:低速内部时钟,RC振荡器,频率为40KHz。独立看门狗时钟源只能是这个,还可做RTC时钟源
LSE:低速外部时钟,接32.768KHz的石英晶体。这个主要是RTC的时钟源,实时时钟源,比如电脑关机后,重新开机时间一直在变。
时钟树,通过将时钟源经过各种各样的倍频分配后为各种外设进行工作,这个过程中把所有部件组合到一起就构成了我们的时钟树。
SYSCLK 系统时钟,最大72MHz
HCLK :AHB总线时钟,由系统时钟SYSCLK 分频得到,一般不分频,等于系统时钟
经过总线桥AHB--APB,通过设置分频,可由HCLK得到 PCLK1与PCLK2时钟
一般HCLK比PCLK要高,但在STM32F0中,两者的频率可以达到一致
通道选择器 锁相环
STM32里通过RCC控制单元去配置时钟,其本质就是一个寄存器组
void SystemInit(void)
System_stm32f0xx.c中定义,在系统启动之后,程序会先执行 HAL 库定义的 SystemInit 函数,进行系统一些初始化配置,复位 RCC 时钟配置为默认复位值(默认开启 HSI)
void SystemClock_Config(void)
在main.c中定义,实现时钟的具体配置,配置PLL, 配置AHB和HPB的时钟