1.Makefile简介Makefile是和make工具一起配合使用的,用于组织管理项目源代码的编译和链接。make工具用于找出修改过的文件,根据依赖关系,找出受影响的相关文件,最后按照规则单独编译这些文件。Makefile文件则记录依赖关系和编译规则。Makefile的本质:无论多么复杂的语法,都是为了更好地解决项目文件之间的依赖关系。2.Makefile规则介绍Makefile的一个规则由目标、依赖、命令组成,其语法结构如下所示:目标:依赖的文件或者是其他目标<tab>命令1<tab>命令2<tab>...一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[Tab]字符开始,[Tab]字符告诉 make 此行是一个命令行。make 按照命令完成相应的动作。这也是书写 Makefile 中容易产生,而且比较隐蔽的错误。3.Makefile伪目标伪目标不代表一个真正的文件名,在执行 make 时可以指定这个目标来执行其所在规则定义的命令,有时也可以将一个伪目标称为标签。使用伪目标有两点原因:(1).避免在我们的 Makefile 中定义的只执行命令的目标(此目标的目的是为了执行一系列命令,而不需要创建这个目标)和工作目录下的实际文件出现名字冲突。(2).提高执行 make 时的效率,特别是对于一个大型的工程来说,编译的效率也许你同样关心。将一个目标声明为伪目标的方法是将它作为特殊目标".PHONY"的依赖。在书写伪目标规则时,首先需要声明目标是一个伪目标,之后才是伪目标的规则定义。目标"clean"的书写格式应该如下:4.Makefile的变量在 Makefile 中,变量是一个名字(像是 C 语言中的宏),代表一个文本字符串。在 Makefile 的目标、依赖、命令中引用变量的地方,变量会被它的值所取代。变量名是不包括":"、"#"、"="、前置空白和尾空白的任何字符串。变量名最好使用字母、数字和下划线进行定义,对于其他的特殊符号不建议使用到变量名的定义中。变量名是大小写敏感的。变量"foo"、"Foo"和"FOO"指的是三个不同的变量。变量的引用方式是:"$(VARIABLE_NAME)"或者"${VARIABLE_NAME}"来引用一个变量的定义。Makefile的变量包含系统环境变量,自定义变量和自动化变量。4.1.系统环境变量使用系统环境变量需要注意以下几点:(1).在 Makefile 中对一个变量的定义或者以 make 命令行形式对一个变量的定义,都将覆盖同名的系统环境变量(注意:它并不改变系统环境变量定义,被修改的环境变量只在 make 执行过程有效)。而 make 使用"-e"参数时,Makefile 和命令行定义的变量不会覆盖同名的环境变量,make 将使用系统环境变量中这些变量的定义值。(2).make的递归调用中,所有的系统环境变量会被传递给下一级make。默认情况下,只有系统环境变量和通过命令行方式定义的变量才会被传递给子make进程。在Makefile中定义的普通变量需要传递给子make时需要使用"export"指示符来对它声明。4.2.自定义变量在Makefile中自定义变量比较简单,下面简单的区分下各个符号的含义:(1).延迟赋值:"=";使用"="号定义变量时,如果定义的变量存在对其他变量的引用,那么定义的变量并不会立即展开对其他变量的引用,而是在真正使用到该变量时才进行展开。例如下述例子中,只有在echo "$(B)"时才会去展开$(A),虽然在定义变量B之前A的值为123,但是在执行echo "$(B)"时,A的最终值为456,因此会输出456。(2).立即赋值:":=";立即赋值是相对于延迟赋值的,使用":="号定义变量时,如果定义的变量存在对其他变量的引用,那么定义的变量会立即展开对其他变量的引用。可以结合下面的例子进行理解。(3).为空赋值(条件赋值):"?=";使用"?="号定义变量时,只有定义的变量在之前没有赋值的情况下才会对这个变量进行赋值。可以结合下面的例子进行理解。(4).追加赋值:"+=";使用"+="符号,可以对变量的值进行追加。可以结合下面的例子进行理解。例子:4.3.自动化变量Makefile中常用的自动化变量如下描述:$<:第一个依赖文件;$^:全部的依赖文件;$@:目标;5.Makefile条件分支条件分支语法如下:例子:6.Makefile的常用函数Makefile还是提供了比较多的函数供我们使用,这里介绍下常用的几个函数。6.1.patsubst$(patsubst PATTERN,REPLACEMENT,TEXT)函数名称:模式替换函数 - patsubst。函数功能:搜索"TEXT"中以空格分开的单词,将符合模式"PATTERN"替换为"REPLACEMENT"。参数"PATTERN"中可以使用模式通配符"%"来代表一个单词中的若干字符。如果参数"REPLACEMENT"中也包含一个"%",那么"REPLACEMENT"中的"%"将是"PATTERN"中的那个"%"所代表的字符串。在"PATTERN"和"REPLACEMENT"中,只有第一个"%"被作为模式字符来处理,之后出现的不再作模式字符(作为一个字符)。在参数中如果需要将第一个出现的"%"作为字符本身而不作为模式字符时,可使用反斜杠"\"进行转义处理。返回值:替换后的新字符串。 函数说明:参数"TEXT"单词之间的多个空格在处理时被合并为一个空格,并忽略前导和结尾空格。如果感觉上述的文字说明有点不太好理解,那么看看下面的示例吧,之后再结合示例看上面的文字说明应该就比较好理解了,示例如下:上述示例的运行结果:把字串"x.c.c bar.c"中以.c 结尾的单词替换成以.o 结尾的字符。函数的返回结果是"x.c.o bar.o"。6.2.notdir$(notdir NAMES…)函数名称:取文件名函数 - notdir。函数功能:从文件名序列"NAMES…"中取出非目录部分。目录部分是指最后一个斜线("/")(包括斜线)之前的部分。删除所有文件名中的目录部分,只保留非目录部分。返回值:文件名序列"NAMES…"中每一个文件的非目录部分。函数说明:如果"NAMES…"中存在不包含斜线的文件名,则不改变这个文件名。以反斜线结尾的文件名,是用空串代替,因此当"NAMES…"中存在多个这样的文件名时,返回结果中分割各个文件名的空格数目将不确定!这是此函数的一个缺陷。示例:6.3.wildcard$(wildcard PATTERN)函数名称:获取匹配模式文件名函数 - wildcard。函数功能:列出当前目录下所有符合模式"PATTERN"格式的文件名。返回值:空格分割的、存在当前目录下的所有符合模式"PATTERN"的文件名。函数说明:"PATTERN"使用shell可识别的通配符,包括"?"(单字符)、"*"(多字符)等。示例:6.4.foreach$(foreach VAR,LIST,TEXT)函数"foreach"不同于其它函数。它是一个循环函数。类似于 Linux shell 中的for 语句。函数名称:循环函数 - foreach。函数功能:这个函数的工作过程是这样的:如果需要(存在变量或者函数的引用),首先展开变量"VAR"和"LIST"的引用;而表达式"TEXT"中的变量引用不展开。执行时把"LIST"中使用空格分割的单词依次取出赋值给变量"VAR",然后执行"TEXT"表达式。重复直到"LIST"的最后一个单词(为空时结束)。"TEXT"中的变量或者函数引用在执行时才被展开,因此如果在"TEXT"中存在对"VAR"的引用,那么"VAR"的值在每一次展开式将会到的不同的值。返回值:空格分割的多次表达式"TEXT"的计算的结果。备注:函数中参数"VAR"是一个局部的临时变量,它只在"foreach"函数的上下文中有效,它的定义不会影响其它部分定义的同名"VAR"变量的值。示例:示例分析:例子中,"TEXT"的表达式为"$(wildcard $(dir)/*)"。表达式第一次执行时将展开为"$(wildcard a/*)";第二次执行时将展开为"$(wildcard b/*)";第三次展开为"$(wildcard c/*)"; ...;以此类推。7.其他默认规则:.o文件默认使用对应的.c文件来进行编译。
CPU:STM32F429IGT6对于其他的stm32芯片或者其他ARM Cortex-M芯片,其实解决方法都相通。建议先完整阅读了本文之后,再对照着你所遇到问题的现象进行调试。1.基础知识在ARM Cortex-M系列处理器中,有若干个系统异常专用于 fault 处理。 CM3 中的 Faults 可分为以下几类:(1).总线 faults;(2).存储器管理 faults;(3).用法 faults;(4).硬 fault;1.1.总线 faults当 AHB 接口上正在传送数据时,如果回复了一个错误信号(error response),则会产生总线faults,产生的场合可以是:(1).取指,通常被称作“预取流产”(prefetch abort);(2).数据读/写,通常被称作“数据流产”(data abort);在 CM3 中,执行如下动作时,如果地址有误,亦会触发总线异常:(1).中断处理起始阶段的堆栈 PUSH 动作。此时若发生总线 fault,则称为“入栈错误”;(2).中断处理收尾阶段的堆栈 POP 动作。此时若发生总线 fault,则称为“出栈错误”;(3).在处理器启动中断服务序列(sequence)后读取向量时。这是一种极度罕见的特殊情况,被归类为硬 fault。总线 fault 状态寄存器(BFSR),地址:0xE000_ED29,BFSR的各个位的定义如下:1.2.存储器管理 faults存储器管理 faults 多与 MPU 有关,其诱因常常是某次访问触犯了 MPU 设置的保护规范。另外,某些非法访问,例如,在不可执行的存储器区域试图取指,也会触发一个 MemManage fault,而且在这种场合下,即使没有 MPU 也会触发 MemMange fault。MemManage faults 的常见诱因如下所示:(1).访问了所有 MPU regions 覆盖范围之外的地址;(2).访问了没有存储器与之对应的空地址;(3).往只读 region 写数据;(4).用户级下访问了只允许在特权级下访问的地址;存储器管理 fault 状态寄存器(MFSR),地址:0xE000_ED28,MFSR的各个位的定义如下:1.3.用法 faults用法 faults 发生的场合可以是:(1).执行了协处理器指令。 Cortex-M3 本身并不支持协处理器,但是通过 fault 异常机制,可以建立一套“软件模拟”的机制,来执行一段程序模拟协处理器的功能,从而可以方便地在其它 Cortex 处理器间移植。(2).执行了未定义的指令。同上一点的道理,亦可以软件模拟未定义指令的功能。(3).尝试进入 ARM 状态。因为 CM3 不支持 ARM 状态,所以用法 fault 会在切换时产生。软件可以利用此机制来测试某处理器是否支持 ARM 状态。(4).无效的中断返回(LR 中包含了无效/错误的值);(5).使用多重加载/存储指令时,地址没有对齐。另外,如果需要严格要求程序的质量,还可以让 CM3 在遇到除数为零的时候,以及遇到未对齐访问的时候也产生用法 fault。在 NVIC 中有两个控制位分别与它们对应。通过设置这两个控制位,就可以激活它们。用法 fault 状态寄存器(UFSR),地址:0xE000_ED2A,UFSR的各个位的定义如下:1.4.硬 fault硬 fault 是上文讨论的总线 fault、存储器管理 fault 以及用法 fault 上访的结果。如果这些 fault 的服务例程无法执行,它们就会成为“硬伤” ——上访( escalation)成硬 fault。另外,在取向量(异常处理时对异常向量表的读取)时产生的总线 fault 也按硬 fault 处理。在 NVIC中有一个硬 fault 状态寄存器(HFSR),它指出产生硬 fault 的原因。如果不是由于取向量造成的,则硬 fault 服务例程必须检查其它的 fault 状态寄存器,以最终决定是谁上访的。硬 fault 状态寄存器(HFSR),地址:0xE000_ED2C,HFSR的各个位的定义如下:2.UsageFault INVPC置1解决过程最近在使用RTOS增加DMA驱动时,在对内存到设备和设备到内存的DMA传输测试时,出现了UsageFault,并且UFSR中的INVPC置1了。最开始,单独测试DMA发送是没有问题的,但是DMA发送和接收一起测试时,就会出现UsageFault(INVPC置1)。这个异常不太好定位出现问题的具体位置,因此就检查DMA驱动,并且逐步调试吧。最终,DMA驱动检查和修改好了,仍然出现UsageFault,实在没法了,还是从为什么会出现UsageFault(INVPC置1)开始分析吧。2.1.出现UsageFault(INVPC置1)的原因如果LR中的EXC_RETURN不是合法的值(合法值见下图,包括企图返回ARM状态),则引起用法fault。如果用法fault被除能,也上访成硬fault。此时,用法Fault状态寄存器(UFSR,地址: 0xE000_ED2A)中的INVPC位(位偏移: 2),或者是INVSTATE位(位偏移: 1)置位。上面就是出现该异常的文字分析了。2.2.UsageFault(INVPC置1)的解决过程因为该异常是异常响应期间才可能出现的异常(<<Cortex-M3权威指南>> 9.8节介绍了下,在9.8.4节进行文字说明),因此,只要在异常或中断的返回处打断点,执行下一步就有可能进入UsageFault异常。(当然了这个方法,是比较笨的,不过在缩小了异常出现的范围之后,可以在每次异常或中断的返回处打断点,然后执行下一步,就有可能进入UsageFault异常)我这里是每次开始DMA测试之后,就进入UsageFault异常,并且我的系统中目前就打开了SysTick,PendSV,DMA1_STREAM5,DMA1_STREAM6,NMI,HardFault,MemFault,BusFault,UsageFault这些异常和中断。因此我在开始DMA测试的时候打一个断点,在程序运行到DMA测试开始的断点处时,再在DMA1_STREAM5,DMA1_STREAM6,NMI,HardFault,MemFault,BusFault,UsageFault这些函数的入口打断点,在PendSV的返回处打断点,SysTick就暂时先不管。然后全力运行,发现每次都会在PendSV的异常返回断点处停留(因为任务切换嘛)总共在PendSV的断点处停留了大概7到8次,就进入到了UsageFault。有了上面步骤的铺垫,先去除中断和异常中的断点,还是先在DMA测试开始处打断点,等运行到DMA测试开始处,再在上述的中断和异常相关位置打断点。接下来我就慢慢的调试,在开始DMA测试之后,全速运行,在退出PendSV异常时,执行单步运行到下一步,重复7到8次,从PendSV就进入了UsageFault,在这7到8次中,我看在退出PendSV时,LR寄存器中的值都是0xFFFFFFFD,是合法的啊,当时仔细一想,有可能是退出异常时硬件再将堆栈中的PC赋值给PC时出问题,导致进入了UsageFault。果不其然,在PenSV退出之前,我查看每个PSP(0xFFFFFFFD:返回线程模式,并使用线程堆栈)对应内存数值,能够正常退出PendSV的寄存器和PSP堆栈内容如下图所示:进入UsageFault异常之前的寄存器和PSP堆栈内容如下图所示:此处堆栈中的内容,明显的0x20003D94堆栈中的PC值是有问题的,我的程序是烧写到flash中的PC地址应该是08xxxxxx,然而现在堆栈中的PC地址是0x20003DA8,这个地址是SRAM中的地址,SRAM存储的数据而不是代码,出现这个问题的原因,猜想一下,应该就是任务堆栈溢出导致,当我增加任务堆栈的大小之后,哈哈,程序正常运行,世界是如此美好。3.调试小结3.1.解决过程小结其实上面的步骤2,是我自己的一个调试解决问题的过程,这里给大家提供一个比较直接的解决方式。在开始运行程序之前,直接在UsageFault异常入口函数中打一个断点,然后全速运行程序,等程序停止在UsageFault异常函数的断点处时,需要注意以下几点:(1).如果LR是合法值,那么根据LR判断退出异常时使用的堆栈,然后在Memory查看窗口中,查看堆栈中R0,R1,R2,R3,R12,LR,PC,xPSR这些寄存器的值,根据这些寄存器的值,判断是否是堆栈溢出导致该异常发生;如果不是堆栈溢出导致该异常发生,那么就要根据PC值,在汇编窗口中跳转到PC值对应的代码处,分析导致异常发生的原因;(2).如果LR不是合法值,就要分析下你的代码中,有哪些地方修改过LR的值,确保修改的值要是合法的。3.2.关于UsageFault 如何才能让INVPC置1(1).在退出异常或中断时,执行BX LR时,LR的值是非法的,此时就会触发UsageFault异常,并且INVPC置1。(2).在退出异常或中断时,执行BX LR时,LR的值是合法的,但是退出异常之后要使用的堆栈中,堆栈里面的PC值是有问题的,此时就有可能触发UsageFault异常,并且INVPC置1。对于上面的两点的模拟其实也比较好做,在PendSV或者其他异常的退出的地方打一个断点,然后手动修改LR或者堆栈中PC的值,就能触发UsageFault异常,并且INVPC置1。这里注意一下,修改堆栈中PC的值,我这里测试时候,设置PC值为其他值可能引起其他的异常,貌似修改PC的值为RAM中数据区的地址才会出现该异常,不太清楚为什么会这样,可能是数据区是没有执行代码的权限,因此出异常吧,不太确定,有知道的朋友,欢迎留言讲解。当然了,博主的知识水平有限,对于异常的一些说明可能会有误,如果有误,欢迎指正,谢谢!
CPU:STM32F429IGT6对于其他的stm32芯片或者其他ARM芯片,其实解决方法都相通,主要就是先修改启动方式,再去更改flash或者ram中的程序。1.问题出现原因最近在调试一个程序时,始终调试不通,没办法了,就想着参考网上的例程来看看,结果下载的stm32工程可能和板子的硬件不太一样导致stm32 flash读写保护,此后使用JLink再也无法连接上stm32了,Keil中也没办法识别出设备:2.解决方法首先,修改stm32的启动方式,我的板子默认设置的是从内部flash启动,这里要解决stm32的读写保护就不能选择从内部flash启动了。修改启动方式为:从系统存储器启动(也就是BOOT0接VCC,BOOT1接GND),下图是stm32f429对应的启动方式配置表:然后重新给板子上电,打开一个好的工程(能够在板子上正常运行的程序),此时Keil能够识别出设备了,然后重新下载程序到板子就OK了,程序下载完成之后,切换启动方式到原来的启动方式即可。
1.概述目前网络上使用stm32 spi的nss,基本上都使用的是nss的软件管理模式。对于nss的硬件管理模式,程序的配置以及对nss的软件管理模式与硬件管理模式的说明,真的少之又少。这可能跟stm32官方文档对nss的描述不清也有很大关系吧,下面就带大家理解理解nss,当然了这篇文章是笔者阅读了stm32 spi官方文档再结合网友的一些文章,最终以自己个人对nss理解来编写的,如果有错误之处,还请指正。2.nss的管理模式nss的管理模式分为:硬件管理和软件管理。nss的管理模式由SSM决定。nss其实分为外部引脚和内部引脚的。外部nss引脚就是芯片上肉眼可见的与GPIO复用的引脚;内部nss引脚就是stm32芯片里集成的spi模块引脚,肉眼不可见。换句话说,真正与spi通信控制器连接的是内部nss引脚,外部nss引脚不能直接连到芯片内部的spi模块,而是先连接内部nss引脚,通过内部nss引脚作用spi模块。3.nss的管理模式配置SSM在SPI_CR1控制器里,默认为0。SSM可以控制内部nss引脚与SSI(一个寄存器,软件模式)相连,还是与外部nss引脚(真正的STM32引脚,硬件模式)相连。真正起作用的是内部nss引脚(内部nss引脚才真正连接到SPI通信控制器上)。当SSM=0:说明使用硬件管理模式,内部nss引脚与外部nss引脚相连,忽视SSI位,对SPI_CR1的SSI位的写操作无效;当SSM=1:说明使用软件管理模式,内部nss引脚与SSI相连,忽视外部nss引脚,我们可以把外部nss引脚当做普通IO口;4.spi从模式配置(MSTR=0)(1).nss硬件模式(SSM=0)当外部nss引脚为低电平时,内部nss也为低电平,相当于片选该从器件,此时spi可以传输数据。外部nss引脚需要配置为复用功能。(2).nss软件模式(SSM=1)SSM=1并且SSI=0,STM32芯片让内部nss引脚为低电平,相当于片选该从器件,此时spi可以传送数据。外部nss引脚被释放,可做普通IO作为其他用途使用。5.spi主模式配置(MSTR=1)(1).nss硬件模式(SSM=0)在spi主模式下,nss硬件模式又分为输入模式和输出模式,由SSOE位决定。a.输入模式(SSOE=0):在外部nss引脚为高电平,内部nss引脚也为高电平,此时才能进行数据传输。如果要使能从设备,还需要一个GPIO引脚。在此情况下,外部nss要是被接低电平,则会进入主模式故障,MSTR会清零,由主模式进入从模式。外部nss引脚需要配置为复用功能,并且外部nss引脚必须接入一个高电平,它才能维持主模式状态。stm32官方手册spi章节有说明,也就一句话,如果你不注意,可能就无法发现,如下图:b.输出模式(SSOE=1):当使能spi模块时,外部nss引脚会被芯片自动输出低电平,使能从设备,进行数据传输,不需要额外的GPIO引脚就能片选从设备(输出模式时,貌似不需要遵循上图框出来的内容,那段话可能就是针对输入模式写的,输出模式是spi模块控制内部nss引脚,因此也就不必遵循那段话)。外部NSS引脚需要配置为复用功能,再把此引脚连接到从器件的CS引脚,因此外部NSS引脚就相当于片选引脚了。(2).nss软件模式(SSM=1)SSM=1,并且SSI=1,将内部nss引脚设置为高电平,这样随时可以传输数据(这句话遵循上图框出来的内容)。当然多数情况还需要一个GPIO引脚输出低电平,来使能从设备,让从设备可以接收数据。综上所述,nss引脚就是片选CS引脚是很不负责任的说法。
1.SPI总线概述SPI是串行外设接口(Serial Peripheral Interface)的缩写。是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线。主要应用在EEPROM,FLASH,实时时钟,AD转换器,数字信号处理器和数字信号解码器等设备与MCU之间进行通信。SPI具有通信简单,支持全双工通信,数据传输速度快的优点。但是由于SPI没有指定的流控制,没有应答机制确认数据是否发送或接收成功,所以SPI总线跟IIC总线比较的话,SPI总线在数据可靠性上有一定的缺陷。2.SPI信号线SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备。SPI使用3条通讯总线和1条片选线。(1).MOSI主设备数据输出,从设备数据输入。(2).MISO主设备数据输入,从设备数据输出。(3).SCLK(或叫SCK)时钟信号线,用于通讯同步,该信号由主机产生和控制。(4).CS片选线,从设备使能信号,由主设备控制。当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时就需要设置从设备对应的片选引脚,来使能从设备。SPI通讯设备之间的连接方式,如下图所示:3.SPI总线的4种通讯模式SPI通信有4种不同的通讯模式,不同的从设备可能在出厂时就配置为某种模式,这是不能改变的;但我们的通信双方必须是工作在同一模式下,所以我们可以对我们的主设备的SPI模式进行配置,通过CPOL(时钟极性)和CPHA(时钟相位)来控制我们主设备的通信模式。MOSI和MISO线在SCK的每个时钟周期传输一位数据,开发者可以自行设置MSB或LSB先行,不过需要保证两个通讯设备都使用同样的协定。时钟极性CPOL是用来配置SCK空闲状态的电平,时钟相位CPHA是用来配置数据采样是在第几个边沿。CPOL=0,表示当SCK=0时处于空闲态,所以有效状态就是SCK处于高电平时;CPOL=1,表示当SCK=1时处于空闲态,所以有效状态就是SCK处于低电平时;CPHA=0,表示数据采样是在第1个边沿,数据发送在第2个边沿;CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿;CPOL和CPHA进行组合配置之后产生四种时序关系,也就是四种通讯模式。四种通讯模式文字描述如下:CPOL=0,CPHA=0:此时空闲态时,SCK处于低电平,数据采样是在第1个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。CPOL=0,CPHA=1:此时空闲态时,SCK处于低电平,数据发送是在第1个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。CPOL=1,CPHA=0:此时空闲态时,SCK处于高电平,数据采集是在第1个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。CPOL=1,CPHA=1:此时空闲态时,SCK处于高电平,数据发送是在第1个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。下面的表格是对上述描述的SPI四种通讯模式的总结:SPI模式CPOLCPHASCK空闲时电平数据采样数据发送000低电平第1个边沿第2个边沿101低电平第2个边沿第1个边沿210高电平第1个边沿第2个边沿311高电平第2个边沿第1个边沿模式0和模式3的差异主要是,模式0的SCK空闲电平是低电平,模式3的SCK空闲电平是高电平。因为模式0的SCK空闲电平是低电平,数据采样发生在SCK的第1个边沿,也就是SCK的上升沿,数据发送发生在SCK的第2个边沿,也就是SCK的下降沿。同样的因为模式3的SCK空闲电平是高电平,数据采样发生在SCK的第2个边沿,也就是SCK的上升沿,数据发送发生在SCK的第1个边沿,也就是SCK的下降沿。从上面一段文字,可以对模式0和模式3进行一个总结:模式0和模式3的主要差异是SCK空闲电平的状态不一样,模式0的SCK空闲电平是低电平,模式3的SCK空闲电平是高电平;模式0和模式3都在SCK的上升沿进行数据采样,在SCK的下降沿进行数据发送。同样的,可以对模式1和模式2进行一个总结:模式1和模式2的主要差异是SCK空闲电平的状态不一样,模式1的SCK空闲电平是低电平,模式2的SCK空闲电平是高电平;模式1和模式2都在SCK的下降沿进行数据采样,在SCK的上升沿进行数据发送。
1.浮点运算指令浮点运算指令用于FPU单元的单精度浮点运算。浮点运算指令都是用V开头的汇编指令。只有在FPU开启的状态下,才能运行这些指令。如果在FPU没有开启的状态下,执行了浮点运算指令,系统会产生一个硬fault异常。2.为什么FPU需要Lazy Stacking当Cortex-M系列的芯片多了对浮点运算的支持之后,在中断响应和退出时会增加对FPU扩展寄存器的保护。入栈浮点寄存器组所带来的影响有如下几方面:a.增加stack frame所占的存储区域;b.增加中断响应延迟;c.在OS环境下,增加上下文切换时间;为了减少中断响应延迟和OS环境下的上下文切换时间,引入了FPU的Lazy Stacking机制。3.FPU Lazy Stacking简述Lazy Stacking机制在下面的情况下,会跳过对浮点寄存器组的入栈操作(仅预留浮点寄存器组S0~S15和FPSCR的存储空间),以避免中断延迟的增加:a.中断处理函数不使用FPU;b.被中断的程序未曾用到FPU;被中断函数使用了FPU,如果在执行中断处理函数时也用到了FPU,在执行到中断函数的第一条浮点指令时,内核会暂停,然后硬件自动将先前的浮点寄存器内容(S0~S15,FPSCR)压入预留的存储空间中。Lazy Stacking是可以通过软件使能和关闭的,如下述操作:a.使能Lazy Stacking必须同时置位FPCCR寄存器的LSPEN位和ASPEN位;b.清除FPCCR寄存器的LSPEN位可以关闭Lazy Stacking;关于FPCCR寄存器的LSPEN位和ASPEN位的组合情况有如下说明:FPCCR.ASPENFPCCR.LSPEN说明00取消自动状态保存。中断响应时不入栈FPU寄存器。应用场景:1. 应用中没有用到OS或者多任务调度,如果没有任何中断异常用到FPU。2. 在应用程序代码中只有一个中断用到FPU。如果有多个中断用到FPU,那么中断嵌套必须被禁止。可以通过把所有的中断优先级设置位相同优先级实现。10关闭Lazy Stacking,仅打开自动状态保存。如果用到FPU,CONTROL.FPCA位自动置1。中断响应时,硬件自动入栈S0~S15和FPSCR寄存器11打开Lazy Stacking,打开自动状态保存。如果用到FPU,CONTROL.FPCA位自动置1。如果响应中断时,CONTROL.FPCA为1,处理器在堆栈中预留FPU寄存器的空间,同时将FPCCR.LSPACT位置1。但是PFU寄存器并没有马上入栈,直到在中断处理函数中用到FPU时再入栈。01非法配置4.FPU Lazy Stacking用到的几个重要标志浮点寄存器入栈和出栈过程中用到的标志位:标志位说明CONTROL.FPCA0 = 当前上下文中没有用到FPU1 = 当前上下文中用到FPU这里的上下文,即表示普通的任务上下文,也表示中断上下文。FPCCR.LSPACT0 = 退出Lazy状态(实际入栈后被硬件清零或者中断返回时硬件清零)1 = 进入Lazy状态(栈帧中预留了FPU寄存器的空间,但没有实际入栈)EXC_RETURN[4]0 = 栈帧中包括了FPU寄存器空间1 = 栈帧中不包括FPU寄存器空间5.FPU Lazy Stacking使用实例下面的例子都是在使能了FPU的Lazy Stacking机制下进行说明的。(1).被中断的程序和中断程序中都没有使用到FPU,其示意图如下所示:(2).被中断的程序用到了FPU,中断程序中没有使用到FPU,其示意图如下所示:(3).被中断的程序和中断程序中都用到了FPU,其示意图如下所示:6.RTOS使用Lazy Stacking首先肯定是要使能FPU的Lazy Stacking,然后在PendSV切换任务的时候做下面的处理:(1).上文保存阶段:检测 EXC_RETURN 的 bit4(通过LR访问),如果该位为零,就入栈剩下的S16-S31 即可;如果该位为 1,则无需保存 FPU 寄存器,因为该任务未曾使用过 FPU。当然了这里有一个标志数据,表示当前任务是否使用了FPU,便于下次任务恢复时,确定是否需要从堆栈弹出内容到S16-S31。标志数据也压入当前任务的堆栈进行保存。(2).下文恢复阶段:从堆栈中弹出标志数据,判断待恢复的任务是否使用了FPU,如果使用了FPU,那么从堆栈中弹出内容到S16-S31;如果没有使用FPU,则不需要恢复FPU寄存器。接下来需要修改当前LR寄存器的内容,如果使用了FPU,LR(EXC_RETURN)的bit4需要清零,来保证在退出PendSV时将先前压入该任务堆栈的S0-S15和FPSCR寄存器出栈;如果没有使用FPU,LR(EXC_RETURN)的bit4需要置1,来告诉CPU该任务栈帧中不包括FPU寄存器内容,不需要进行出栈操作。对于S0-S15和FPSCR寄存器,如果任务使用了FPU,在进入到PendSV之前,就在任务堆栈中预留了存储空间,当执行上文保存阶段的S16-S31的入栈操作时,会首先将S0-S15和FPSCR寄存器压入先前堆栈预留的存储空间中,接着再将S16-S31压入堆栈。如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^。
Zephyr版本号:2.2.0开发板:STM32F429IGT6(野火STM32F429挑战者 V2开发板)开发环境:Windows 7 64位这里选择的是Zephyr当前最新的Release版本2.2.0进行说明的。当需要熟悉Zephyr的具体运行过程,那么调试就必不可少,例如在调试任务切换时,需要观察CPU状态以及各个寄存器的情况,此时图形界面下的调试更为方便。下面讲讲如何使用Eclipse搭配JLink来调试HelloWold应用程序。1、设置Eclipse环境(1).下载并安装Eclipse从Eclipse网站下载Eclipse IDE for C/C++ Developers,然后进行安装。下载网址:https://www.eclipse.org/downloads/packages/release/oxygen/2/eclipse-ide-cc-developers。(2).安装GNU MCU Eclipse插件打开安装好的Eclipse,在Help(或者Window)菜单下面找到Eclipse Marketplace,然后在Find输入框中输入GNU MCU eclipse,按Enter键进行搜索,找到GNU MCU eclipse插件之后,点击Install按钮,我这里之前已经安装好了,如下图所示:2、生成Eclipse工程确定命令提示符的路径在Zephyr2.2.0源代码目录中,执行下述命令来生成Eclipse的工程文件:west build -p auto -b stm32f429i_disc1 samples/hello_world -- -G"Eclipse CDT4 - Ninja"生成的Eclipse工程文件.project在build目录下。3、将工程导入到Eclipse打开安装好的Eclipse,在File菜单下面点击Import…,在弹出的窗口中选择General -> Existing Projects into Workspace,然后点击Next,然后添加Zephyr的build的路径,点击Refresh,选中添加的工程之后点击Finish:4、创建调试器配置文件工程导入完成之后,接下来配置调试器,打开菜单Run -> Debug Configurations...,在弹出的窗口中双击GDB SEGGER J-Link Debugging生成调试配置文件。Main标签页的配置: Project:hello_world@build C/C++ Application:zephyr/zephyr.elfDebugger标签页的配置: J-Link GDB Server Setup: Start the J-Link GDB server locally:取消勾选 GDB Client Setup: Executable:arm-none-eabi-gdb.exeStartup标签页的配置: Initial Reset and Halt:取消勾选 Enable semihosting:取消勾选 Enable SWO:取消勾选,如下图所示:Set breakpoint at: __startCommon标签页的配置如下图红色方框所示:5、运行调试器在使用Eclipse调试Zephyr之前,先打开JLink GDB Server,然后再点击Eclipse调试按钮下拉菜单的hello_world_build Configuration调试选项:点击调试按钮等一小会儿之后,出现了调试界面,惊喜吧:之后的步骤,就是自己动手调试代码啦。如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^。
Zephyr版本号:2.2.0开发板:STM32F429IGT6(野火STM32F429挑战者 V2开发板)开发环境:Windows 7 64位这里选择的是Zephyr当前最新的Release版本2.2.0进行说明的。1、修改代码前面文章的操作,虽然在Windows下能够成功的编译STM32F429的工程,但是却不能在野火挑战者V2开发板上运行,因为Zephyr代码中的STM32F429硬件和野火的外围硬件不一样(其实CPU也有一些差异)。主要体现在外部晶振,时钟的配置以及MPU上面(野火挑战者V2开发板使用的CPU是STM32F429IGT6,不带MPU功能)。进入到boards/arm/stm32f429i_disc1文件夹中,然后修改文件stm32f429i_disc1_defconfig,需要修改的内容如下:CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=180000000CONFIG_ARM_MPU=nCONFIG_CLOCK_STM32_HSE_CLOCK=25000000CONFIG_CLOCK_STM32_PLL_M_DIVISOR=25CONFIG_CLOCK_STM32_PLL_N_MULTIPLIER=360在stm32f429i_disc1_defconfig文件的末尾,添加如下的内容(该内容的含义是编译出的代码不进行优化,这样便于调试):CONFIG_NO_OPTIMIZATIONS=y修改文件board.cmake,需要修改的内容如下:board_runner_args(jlink "--device=STM32F429IG" "--speed=4000")2、编译HelloWorld应用确定命令提示符的路径在Zephyr2.2.0源代码目录中,执行下述命令:west build -p auto -b stm32f429i_disc1 samples/hello_world编译成功的输出信息如下:3、下载程序到开发板确保Windows中安装了JLink驱动,并且将JLink驱动的路径(JLink.exe所在的路径)添加到系统环境变量的Path中。我这里安装的是Jlink V6.48a,其路径为:C:\Program Files (x86)\SEGGER\JLink_V648a。如果没有将JLink驱动的路径添加到环境变量中,下载程序时会出现类似的错误:FATAL ERROR: required program JLink.exe not found; install it or add its location to PATH。将开发板上电,连接好JLink调试器,连接好串口线并且打开串口调试助手,然后执行下述命令将程序烧写到开发板中:west flash --runner jlink程序下载成功之后,会在串口调试助手中打印出信息,如下图所示:4、使用GDB调试程序程序下载成功之后,执行命令:west debug --runner jlink(该命令会自动打开JLink GDB Server,不需要提前手动打开JLink GDB Server),可以使用GDB调试Zephyr,先前的镜像不能成功打印信息,我也是通过这个命令来调试Zephyr,最终定位时钟参数问题的。接下来的一篇文章,会讲述如何使用Eclipse来调试Zephyr,虽然也是使用的GDB,但是调试更加的方便,能够手动添加断点,添加观察的变量,随意切换文件,总之下面一篇文章很OK,敬请关注。如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^。
- 1
本站信息
目前本站共被浏览 162783 次
目前本站已经运行 3508 天
目前本站共有 165 篇文章
目前本站共有 6 条评论信息
目前本站共有 104 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2023年11月26日
目前本站已经运行 3508 天
目前本站共有 165 篇文章
目前本站共有 6 条评论信息
目前本站共有 104 个标签
目前本站共有 0 条留言信息
网站创建时间: 2015年03月01日
最近更新时间: 2023年11月26日
JLink V9掉固件修复(灯不亮) 3Zephyr笔记2 - 在STM32F429上运行HelloWorld 2计算NandFlash要传入的行地址和列地址 1Linux MMC子系统 - 6.eMMC 5.1工作模式-设备识别模式 0Linux MMC子系统 - 5.eMMC 5.1工作模式-引导模式 0Linux MMC子系统 - 4.eMMC 5.1常用命令说明(2) 0
最新评论
标签云
Linux嵌入式实用技巧ARM内核学习问题集合CC++编程语言阅读笔记汇编Linux内核完全注释Windows驱动开发计算机基础ARM11ARMv7-ASTM32IDESublimeLinux内核学习eMMCMMC子系统Ubuntu操作系统OfficeVMWareAPUEgccRTOS中断漫游世界随笔感悟开发工具软件应用编程VsCodearmccarmclang编译器ZephyrSPIJLink网卡驱动安装各种芯片库函数NFSμCOS内核sambaFlashUnix命令与脚本输入法Linux内核设计与实现gitRIFFWAVJATGFTPar8161安装centos有线上网μCGUI字库工程建立右键菜单网络文件系统Firefox百度NTFS文件系统CodeBlocksCentOS数据结构算法PhotoShop51KeilQTUltraEditscanfglibc宏定义UIDGID优先级娱乐天地SourceInsight磁盘扇区总线I2CPDFBComparePythonI2SFPUMakefileSWDCPUARP软件推荐FileZilla