正点原子的系统文件(转)
Posted nickshen86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正点原子的系统文件(转)相关的知识,希望对你有一定的参考价值。
SYSTEM文件部分
Delay.c(寄存器版本和库函数版本差别不大)
SysTick定时器
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号: 15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。例如,为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。
Cortex‐M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时器,软件在不同 CM3器件间的移植工作得以化简。该定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟(CM3处理器上的STCLK信号)。不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同,你需要检视芯片的器件手册来决定选择什么作为时钟源。
SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3产品间对其处理都是相同的。
延时的 ms 数不能太长。否则超出了 LOAD 的范围,高位会被舍去,导致延时不准。最大延迟 ms 数可以通过公式: nms<=0xffffff*8*1000/SYSCLK 计算。 SYSCLK单位为 Hz, nms 的单位为 ms。如果时钟为 72M,那么 nms 的最大值为 1864ms。超过这个值,建议通过多次调用 delay_ms 实现,否则就会导致延时不准确。
后面delay_ms()和delay_us()两个函数就根据上面的fac_us和fac_ms进行乘法运算
OS自己还没接触过,不过看了原子哥的时钟摘取的方法也是脑洞大开
Sys.c(寄存器版本和库函数版本的差别有点大,库函数只介绍了位带操作,而寄存器版本的教程信息量好大…..)
位带操作
支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在CM3中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。
按上面这幅图的计算公式再来看原子哥的代码就能大概看懂什么意思了,原子哥真牛B,一直不怎么会使用#define,以后也学着这样写试试
<<CM3 权威指南>>第五章(87 页~92 页),对比了使用位带操作和不使用位带操作的区别,包括读、写、以及汇编代码的区别。
*((volatile unsigned long *)(addr)):这个解释下面该论坛的大神已经解释的很清楚了,简单明了
http://www.openedv.com/posts/list/918.htm
“<<和>>实现乘除法的优势”:位操作只需一个指令周期即可完成,而大部分的C 编译器的“%”运算均是调用子程序来完成,代码长、执行速度慢。通常,只要求是求2n 方的余数,均可使用位操作的方法来代替。通常如果需要乘以或除以2n,都可以用移位的方法代替。如果乘以2n,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,提醒自己以后尽量多用移位来代替乘除。
Stm32_Clock_Init(9):库函数版本加了SystemInit,寄存器用的是原子哥写的一个Stm32_Clock_Init(9);函数
寄存器使用的是Stm32_Clock_Init(u8 PLL),库函数使用的SystemInit(),这两个里面的代码几乎都是一样的
1、先是复位配置向量表
2、配置中断向量表基址和偏移量
3、使能外部时钟HSE
4、倍频PLL选择为9倍频,即72Mhz
5、PLL作为系统时钟
Sys.c其中有这个函数还挺好用的,直接调用就能实现软复位
void Sys_Soft_Reset(void)
{
SCB->AIRCR =0X05FA0000|(u32)0x04;
}
低功耗一览表
进入待机模式
库函数:PWR_EnterSTANDBYMode()
寄存器:Sys_Standby() -> 这里面有一个函数是WFI_SET()用于进入模式的操作
__asm void WFI_SET(void)
{
WFI;
}
该函数其实是在 C 语言里面嵌入一条汇编指令,因为CM3 内核的 STM32 支持的 THUMB 指令,并不能内嵌汇编,所以需要通过这个方法来实现汇编代码的嵌入。
__asm关键字:启动内联汇编并且能写在任何c/c++合法语句之处.它不能单独出现.它必须接汇编指令、一组被大括号包含的指令或一对空括号.术语“__asm 块”在这里是任意一个指令或一组指令无论是否在括号内。括号可以清晰的将C或C++代码和汇编代码分开,并且避免了不必要的重复__asm关键字。括号也能避免模糊性。
下面原子哥还有几个类似的函数,记录下方便以后调用
//关闭所有中断
__asm void INTX_DISABLE(void)
{
CPSID I;
}
//开启所有中断
__asm void INTX_ENABLE(void)
{
CPSIE I;
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
JTAG和SWD模式设置
// mode: JTAG,SWD 模式设置;00,全使能;01,使能 SWD;10,全关闭;
寄存器:void JTAG_Set(u8 mode) //用于设置 JTAG 的模式
库函数:GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
中断管理函数
以下是几个概念
可屏蔽中断:
可屏蔽中断由有中断能力的外围设备所产生,包括处在定时模式的定时器溢出。每个可屏蔽中断源可以由中断使能位所单独关闭,或者由寄存器中的通用中断使能位GIE一齐关闭。
非屏蔽中断:
非屏蔽中断是出现在NMI线上的请求 ,不受中断标志位IF的影响,在当前指令执行完以后,CPU就响应。这种中断通常用于电源故障。非屏蔽中断的优先权高于屏蔽中断。
区别:
可屏蔽中断和不可屏蔽中断都属于外部中断,是由外部中断源引起的;但它们也有区别:可屏蔽中断是通过CPU的INTR引脚引入,当中断标志
IF=1时允许中断,当IF=0时禁止中断,不可屏蔽中断是由NMI引脚引入,不受IF标志的影响
过程:
简述:保存断点入栈,响应经中断控制器8259优先级判断后的中断请求,恢复断点。
I/O设备发出的所有中断(IRQ)都可以产生可屏蔽中断,受标志位IF的影响,根据中断循序标志的设置来判断CPU是否响应中断请求。
1.接口发出中断请求
2.当前指令执行完后,CPU响应中断
3.终端类型号N送CPU
4.当前的EFR CS HE EIP 推入栈
5.清除IF(中断允许标志位,IF=0,禁止CPU响应外部中断请求,IF=1,允许响应)和TF(跟踪标志,也称单步执行,CPU按跟踪方式执行)
6.由中断类型号,查中断向量表获得中断服务子程序的入口地址
7.CPU执行中断子程序
8.开中断
9.IRET指令使EIP CS HE EFR弹出栈,返回被中断的源程序
中断挂起:
可以理解为“等待响应”
其中教程中有个ICER寄存器用于中断除能,这里要专门设置一个寄存器 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。具体为什么这么设计,看下面这幅图
下图也是经常用到,放这里,免得总是去翻
原子哥代码中: void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)这个函数用来设置中断分组,
主要用于是对SCB->AIRCR=temp; 这条代码的操作,SCB->AIRCR是应用程序中断及复位控制寄存器,第8到第10位用于分组设置
NVIC_Init()进行了5步操作
1、设置分组
2、设置抢占优先级
3、设置响应优先级
4、设置通道
5、使能中断位
Ex_NVIC_Config()外部中断的配置
1、使能IO口复用时钟
2、设置中断寄存器组(总共4个组,每个组可以对应4个IO,总共可以对应16个IO(0~15)),以及使能的位
3、选择触发方式
4、开启中断
顺便看了下一些寄存器对应地址的方法,主要是分几步#define将各个基地址进行相加,在得到最后的地址后将地址强制转换为结构体指针类型,由于结构体的顺序结构可以将一段地址分配到对应的字节数里面,使用的时候直接用结构体指针指向就行了,非常方便,希望以后能学会使用这种方法写代码。
此论坛大神解释的事件与中断的区别:
事件:是表示检测到某一动作(电平边沿)触发事件发生了。
中断:有某个事件发生并产生中断,并跳转到对应的中断处理程序中。
事件可以触发中断,也可以不触发
中断有可能被更优先的中断屏蔽,事件不会
事件本质上就是一个触发信号,是用来触发特定的外设模块或核心本身(唤醒).
事件只是一个触发信号(脉冲),而中断则是一个固定的电平信号
Usart.c
UART:通用异步收发器;
USART:通用同步异步收发器。一般而言,单片机中,名称为UART的接口一般只能用于异步串行通讯,而名称为USART的接口既可以用于同步串行通讯,也能用于异步串行通讯。
同步通信与异步通信区别:
1.同步通信要求接收端时钟频率和发送端时钟频率一致,发送端发送连续的比特流;异步通信时不要求接收端时钟和发送端时钟同步,发送端发送完一个字节后,可经过任意长的时间间隔再发送下一个字节。
2.同步通信效率高;异步通信效率较低。
3.同步通信较复杂,双方时钟的允许误差较小;异步通信简单,双方时钟可允许一定误差。
4.同步通信可用于点对多点;异步通信只适用于点对点。
串口部分寄存器跟库函数版本还有差别,用库函数完全不用关心,只管直接设波特率就好了,我也不知道还有USART_BRR这个寄存器
以下是计算BRR寄存器值的公式,给了这条公式直接套就可以得出数值了
原子哥教程中也有提醒到在某些波特率和PCLK2频率下,还是会存在误差的,如下图
串口初始化过程:void uart_init(u32 pclk2, u32 bound)
1、先根据公式计算得出USARTDIV的小数和整数部分
2、使能PORTA口时钟
3、使能串口时钟
4、IO口状态设置
5、复位串口1
6、停止复位
7、波特率设置(有上面得出的值直接赋给BRR寄存器)
8、设置为1位停止,无校验位
9、如果使能了接收还需要使能串口中断,设置中断配置
STM32时钟系统
采用一个系统时钟不是很简单吗?为什么 STM32 要有多个时钟源呢? 因为首先
STM32 本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比
如看门狗以及 RTC 只需要几十 k 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干
扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。
顺便记多一个图
————————————————
版权声明:本文为CSDN博主「KeFan2615」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sky_buddha/java/article/details/52845773
以上是关于正点原子的系统文件(转)的主要内容,如果未能解决你的问题,请参考以下文章
正点原子I.MX6U-MINI移植篇Ubuntu-base根文件系统移植构建过程详解
正点原子I.MX6U-MINI移植篇Ubuntu-base根文件系统移植构建过程详解
正点原子I.MX6U-MINI移植篇rootfs移植过程详解
正点原子I.MX6U-MINI移植篇rootfs移植过程详解