当前位置:   article > 正文

寒假学习笔记_cnt2:标准定时,定时时间为 1s。

cnt2:标准定时,定时时间为 1s。

这个是是B站江科大的笔记

看图,将外设的模块用库函数配置好即可,把信号电路打通

STM32中,所有的GPIO都是挂载在APB2外设总线上的

GPIO外设是按照GPIOA等,每个GPIO外设有16个引脚,0-15

模块内包括寄存器和存储器,寄存器就是一段特殊的存储器,内核通过APB2总线对寄存器进行读写,完成输出电平和读取电平功能,每一位对应一个引脚,输出写1对应引脚输出高电平,输入相同,读取为一,端口为高电平,STM是32位寄存器,所以内部寄存器都是32位的,端口只有16位,低16位有对应的端口(引脚,IO口),高16位没有用到,驱动器用来增加信号驱动能力,寄存器如名,只存储数据,要进行点灯等操作,用驱动器。

1.保护二极管,当电压过大时候保护二极管导通,使得电流不流向输入部分

电压过小也导通,不会从内部汲取电流

  1. 上拉和下拉电阻,上面导通,下断为上拉输入,下拉输入同理,如果都断开,为浮空输入(目的,给输入提供默认电平,对于数字断开,输入不是高就是低,假想输入啥都不接,输入处于浮空状态,输入电平容易受外界干扰而改变,避免不确定,加上电阻(上拉时,引脚悬空,还有上拉电阻保证引脚的高电平(上拉默认高电平的输入方式)))类比弹簧,上拉下拉电阻较大,不影响正常的输入

  1. 施密特触发器,对输入电压进行整形,大于某值,变成高电平,小于某值,变为低电平

(原因,数字信号实际情况下会产生各种失真,没有他,有可能因为干扰导致误判,避免信号抖动现象),通过整形的波形可以直接写入输入数据寄存器了,然后用程序读取输入寄存器对应某一位的数据,知道端口的输入电平

  1. 片上外设,1.模拟输入,连接到ADC上,ADC需要接收模拟量,所以线接在施密特触发器前面,另一个是复用功能输入,连接到其他需要读取端口的外设上的,比如窜口的输入引脚,读取数字量,所以在触发器后面

输出部分(IO口对外输出)

数字部分可以由输出数据寄存器或片上外设控制,通过数据选择器连接到输出控制部分

选择输出数据寄存器控制,就是普通的IO口输出,写寄存器的某位控制对应的端口,

左边有未设置清除寄存器,单独操作某一位,不影响其他位,因为输出数据寄存器同时控制16个端口,并且这个寄存器只能整体读写,想单独控制,用特殊方式。

1读寄存器,用按位与和按位或方式更改,最后改完后写回去2设置位设置寄存器,对某一位置1,寄存器对应位写1即可,不需要操作写0,内部有电路自动更改,帮我们完成了更改的步骤,某一位清0,位清除时候对应位为写1即可 3 位带操作,STM中专门分配有一段地址区域,映射了RAM和外设寄存器所有的位。库函数用的是读写位设置和位清除

输出控制到两个MOS管,电子开关,信号来控制开关的导通和关闭,开关将IO口接到VDD或VSS,可以选择推挽,开漏,关闭三种输出方式

推挽时候,均有效,1时为P,P通N断,输入接到VDD,输出高电平,0时候,P断N通,接到VSS,输出低电平,这个模式下高低电平都有较强的驱动能力,叫做强推输出,推挽输出下,STM对IO口有绝对控制,高低电平都有它说的算

开漏下,P没用,其他相同,1时候下管断开,输出断开,高阻模式,0时相同,只有低电平有驱动能力,作为通信协议的驱动模式,例如I2C引脚,避免个个设备相互干扰,开漏也可以输出五V电平信号,外接5V外设,1时候高阻模式,由外部上拉电阻拉高到5V,兼容电平设备

关闭,引脚配置为输入模式,MOS管无效,输出关闭,电平由外部信号控制

模拟输入,GPIO无效,引脚直接接入片上外设(ADC)

一个断开可以有多个输入,只能有一个输出

位设置,高16位是清除,低16位是进行位设置(1起到影响,0不起影响)

单一设置或清除,可以分开用寄存器

一般使用低电平驱动

去波电容主要是为了保证电路的稳定

上拉下拉工作能力逻辑(电阻看成弹簧,阻值越小,拉力越强,高度相当于电路中的电压)

弱上拉,强上拉指的是电阻阻值大小,也就是弹簧弹力大小

按钮是输入单片机模式,按下按钮,接地,为低电平,按钮没按下的时候,悬空,所以要上拉输入模式

开启ahb时钟,初始化(外设都有)(定义结构体变量,结构体赋值,调用函数,函数自动读取结构体的值,将外设参数配置好,写入gpio配置寄存器),函数控制

按位或本质是二进制

GPIO_WriteBit写入为1

中断系统是管理和执行中断的逻辑结构,外部中断是产生中断的外设之一

中断:在主程序运行过程中,出现了特定的外设的中断触发条件(中断源)(对外部中断而言,引脚发生了电平跳变,对定时器而言,可以说定时的时间到了,对串口通信来说,可以是接受到了数据)发生中断触发条件,情况比较紧急,希望中断条件满足时候,CPU暂停当前正在运行的程序,转而去处理中断时间程序(外部中断来了,想要计次,变量++,串口来了,接收的数据转存起来),处理完成后又返回原来被暂停的位置继续运行(提高程序效率,例如没中断系统,防止外部中断被忽略或者串口数据被覆盖,主程序不断查询,不能干别的事情,没有定时器中断,主程序只能靠DELAY函数,才能实现定时的功能,有了中断后,主程序可以安心执行其他事情,有中断时候再去处理,类比闹钟)

中断优先级(紧急程度,根据需要,自己设置):当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行判断,优先响应更加紧急的中断源(事件比较紧急,优先级设置高一些,不紧急,优先级低一些,更好安排中断事件,防止紧急的事情被别的中断耽误)

中断嵌套(中断再次中断,叫做中断嵌套,照顾非常紧急的中断,根据中断优先级决定):当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前的中断程序,转而去处理新的中断程序,处理完后依次进行返回(执行中断时候,又有更高优先级的外设的中断条件被满足,打断,执行新的中断)

断点(外设的中断条件满足后被暂停的地方)中断程序执行前会对程序现场进行保护,中断程序执行后,会还原现场,保证主程序中断后回来还能继续运行)

中断程序在主函数里面,不需要我们调用,中断来临时候,硬件自动调用

程序中中断函数地址由编译器分配,不固定,但是中断跳转,由于硬件限制,只能跳到固定的地址执行程序,为了让他跳转到不固定的中断函数哩,需要在内存中定义地址列表,列表地址固定,中断发生后,跳到这个固定的地址。在固定位置,由编译器,在加上一条跳转到中断函数的代码,这样中断跳转就可以跳转到任意的位置了,中断地址列表,叫做中断向量表,相当于跳板(延申,编译器做好了)

STM中有丰富的中断资源,包括(EXTI外部中断,TIM定时器,ADC模数转换,USART串口,SPI,I2C,RTC实时时钟)

NVIC(嵌套中断向量控制器(在STM32中统一分配优先级,管理中断的东西)):内核外设,是CPU的小助手,(STM32中中断很多,如果全接到CPU上,要引出很多线,设计很麻烦,

且若有很多中断或者中断产生拥堵,CPU很难处理,毕竟CPU是用来运算的,把分配任务放到NVIC中。NVIC有很多输入口,有多少中断线路,都可以接过来。

然后NVIC只有一个输出口,NVIC根据每个中断优先级分配中断的先后顺序,之后通过一个输出口告诉CPU该处理哪个中断,CPU不在需要知道中断先后顺序的分配任务(类比医生看病人,如果只有医生,医生还有知道先看谁,后看谁,影响效率,故设置叫号系统,来病人统一取号,根据病人等级,分配优先级,叫号系统看排队病人,优先叫紧急的,最后输出给医生一个个排好队的病人)

抢占优先级高的可以进行中断嵌套,响应优先级高的可以进行优先排队,抢占优先级和响应优先级均相同的按中断号排队(解释:同样类比病人看病,对于紧急的病人,有两种形式的优先,一种是上一个在看病,外面虽然排了很多人,但他可以直接插队,等上一个病人看完后就进去看病,叫做响应优先级,若病人更紧急,有一个人正在看病,可以直接进去看病,让上一个病人先等下,等他看好后在到上一个,这种是中断嵌套,决定是不是可以中断嵌套的优先级叫做抢占优先级,高的可以进行中断嵌套。)

每个中断有16个优先级,为了把优先级再区分为抢占和响应,需要对16个优先级分组

值越小,优先级越高,不存在先来后到,任何时候,都是优先级高的先响应

分组方式(0,4)(1,3)(2,2)(3,1)(4,0),分组方式在程序中是自己选择的,选好后主要抢占和响应的取值范围

EXTI:(EXTERN INTERRUPT)外部中断(第一个病人)

EXTI可以检测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序(简化,引脚电平变化,申请中断)

支持的触发方式:上升沿(电平从低到高瞬间触发中断)/下降沿(电平从高到低瞬间触发中断)/双边沿(都可以)/软件触发(引脚啥事没有,程序执行一句代码,就能触发中断)

支持的GPIO口:所有GPIO口,但相同的PIN不能同时触发中断(PA0和PB0不能同时用GPIO——PIN一样的,只能选一个作为中断引脚)

通道数:16个GPIO_PIN(主要),外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒(蹭网)

外部中断能从低功耗的停止模式下唤醒STM32)

触发响应方式:中断响应(申请中断,让CPU执行中断程序)/事件响应(当检测到电平变化,可以触发中断,也可以触发时间,选择事件,外部中断信号不会通向CPU,通向其他外设,触发其他外设的操作。

很明显,外部中断是对于GPIO输入模式下而言

GPIO有16个引脚,进来16条线,AFIO数据选择器,3个GPIO外设的16个引脚选择一个连接到后面的EXTI,所以相同的PIN不能触发中断,PA0PB0PC0经过选择后只能有一个接到EXTI0通道上,经过EXTI电路后,分为两种输出,上面接NVIC,触发中断,下面接其他外设,(在重复引脚上,需要通过标志位来区分哪个是中断进来的)

服用你功能引脚重隐射,将默认复用功能,变为重定义功能

NVIC上路中断,下路触发事件,触发中断首先会置一个挂起寄存器,相当于是中断标志位,读取寄存器判断是哪个通道(线)触发的外设(与门,开关控制),通过总线访问寄存器

使用外部中断特性,对STM32中,想要获取的是外部驱动的很快的突发信号

第一步,配置RCC,把涉及到的外设时钟都打开,不打开时钟,外设无法工作

第二步,配置GPIO,选择端口为输入模式

第三步,配置AFIO,选择使用的一路GPIO,连接到后面的EXTI

第四步,配置EXTI,选择边沿触发方式,选择触发响应方式(外设都有结构体初始化方式)

第五步,配置NVIC,给中断选择一个合适的优先级

通过nvic,外部中断信号就能进入cpu,cpu收到中断信号,跳转到中断函数执行中断程序

EXTI和NVIC时钟默认是打开的,NVIC是内核的外设,内核的外设都不需要开启时钟,RCC管的都是内核外的外设

STM32中中断函数名称固定,一个对应一个中断

中断函数里先进行一个中断标志位的判断,确保是想要的中断源触发的函数

中断程序结束后,调用清除中断标志位的函数,因为只要中断标志物置1

陈序就会跳转到中断函数,不清除,一直申请中断

*****优先记忆定时 比如做时钟,秒表,一些程序算法),也可以看出定时器本质是计数器,当计数器输入为一个准确可靠的基准时钟的时候,对基准时钟计数的过程,实际上就是记时的过程

(例如对72MHZ记72个数,就是1US,72000个,1MS)

16位计数器(用来执行计数定时的一个寄存器,对时钟计数,每来一个时钟,计数器加一)、预分频(对计数器的时钟进行分频,让这个计数更加灵活)、自动重装寄存器(计数的目标值,想要计多少个时钟申请中断)的时基单元,在72M计数时钟下可以实现最大59.65S的定时(计数器和预分频器最大65536,相除取倒数为最大定时59.65S

不仅具备基本的定时器中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能(基本结构通用,所以扩展了很多功能)

根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

对72MHZ计72个数就是1MHZ,也就是1US的时间,计72000个数,那就是1KHZ也就是1MS的时间

59.65S =65536 X 65536X 1/72M/(中断频率倒数),

STM32的定时器支持级联的模式:一个定时器的输出当做另一个定时器的输入最大定时时间就是59.65S X 65536 X 65536

一个芯片有很多定时器

预分频器(PSC):对输入的基准频率提前进行一个分频的操作(0是一分频

实际分频系数 = 预分频器的值 + 1,最大可以写65535即65536分频

计数器(CNT):对预分频后的计数时钟进行计数,计数时钟(本质是波,有频率)**每来一个上升沿,计数器加1,也是16位,值可以从0~65535,当计数器的值自增(自减)到目标值时,产生中断,完成定时

自动重装寄存器(存储目标值的寄存器):也是16位,存储我们写入的计数目标(固定的),当计数值等于自动重装值时,就是计时的时间到了,就会产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时,计数值等于自动重装值产生的中断一般叫做“更新中断”,此更新中断就会通往NVIC,再配置好NVIC的定时器通道,定时器上的更新中断(也是一个病人)就会得到CPU的响应了,产生对应的事件叫做“更新事件”,更新事件不会触发中断,但可以触发内部其他电路的工作

从基准时钟,到预分频器,再到计数器,计数器自增,同时不断地与自动重装寄存器进行比较,当计数器和自动重装寄存器的值相等时,即计时时间到,这时会产生一个更新中断和更新事件,CPU响应更新中断,就完成了定时中断的任务了。

主从触发模式(STM32的特色)

内部硬件在不受程序控制下实现自动运行,可以减去CPU负担

使用DAC,会用DAC输出一段波形,隔一段时间触发DAC,让他输出下一个电压点(正常思路:先设置定时器产生中断,隔一段时间在程序中调用代码手动触发一次DAC转换,然后DAC输出,结果,会导致主程序处于频繁被中断的状态,影响运行和其他中断的响应,所以定时器设置了一个主模式)

使用定时器的主模式,可以把定时器的更新事件映射到触发输出TRGO(TRIGGER OUT)的位置,TRGO直接接到DAC的触发转换引脚上,自动触发DAC,这样定时器的更新就不需要再通过中断来触发DAC转换了(实现硬件自动化,这就是主模式的作用)

通用定时器中还支持向下计数(从重装值开始,向下自减,减到0后,返回重装值并申请中断)和中央对齐模式(0开始到重装值,申请,然后向下自减,减到0,申请中断)

上面部分,内外时钟源选择和主从触发模式结构,通用定时器可以选择外部时钟,第一个是TIMX——ETR引脚,即PA0上接一个外部方波时钟时钟,配置极性选择,边沿检测和预分频器电路,配置输入滤波,对外部时钟进行整形

TRGI,触发输入来使用,触发重模式。(当外部时钟1使用,一种是ETRF,另一种是ITR,主模式输出TRGO可以通向其他定时器,通向其他定时器,就接到了其他定时器的ITR引脚上,ITRO到ITR3分别来自其他四个定时器的TRGO输出,实现定时器级联

选择TL1F——ED,连接输入捕获单元的CH1引脚,从CH1引脚获得时钟,加ED表示边缘)

外部时钟输入引脚可以是ETR引脚,其他定时器,CH1引脚边延,CH1.2引脚,一般用ETR引脚)

定时器主模式输出(电路可以把内部事件映射到TRGO引脚上) 基本定时器中,将更新事件映射到TRGO,用于触发DAC,这也是一样,把其他事件映射到TRGO引脚上,用于触发其他定时器,DAC或者ADC。

右边电路是输出比较,有4个通道对应CH1-4的引脚,用于输出PWM波形,驱动电机

左边输入捕获,有4个通道对应CH1-4的引脚,可以测输入方波频率等,中间是捕获比较寄存器,输入捕获和输出比较共用的,因为它两不能同时使用,所以寄存器是共用的,引脚也是共用的。

右边记时时间到,产生更新中断后的信号去向,中断信号在状态寄存器里置一个中断标志位

,通过中断输出控制(很多地方都要申请中断,需要允许,不需要静止,中断输出控制就是中断输出的允许位),到NVIC申请中断

模块打通,通入信号,定时器就可以工作了

  1. 开启时钟(打开基准时钟和外设时钟)2.选择时基单元的时钟源3.配置时基单元(结构体就能配置好)4.配置,使能更新中断即中断输出控制到nvic 5.配置nvic,打开通道,分配优先级 6.运行控制7。使能计数器

使能后开始计数,更新时候产生更新中断,在写中断函数

更新事件和更新中断(置更新标志位,初始化好了,立刻进入)一起

时基单元

第一行是预分频器的输入时钟,内部时钟一般是72MHZ,时钟不断运行,第二个计数器使能,高电平计数器正常运行,低电平计数器停止,第三个,预分频器时钟输出,计数器时钟输入

开始是计数器未使能,计数器不运行,前半段预分频器为1,相同,后半段,为2,变成一半,第三个计数器寄存器也跟随时钟上升延不断自增,FC后变为0,重装值为FC,当数值相同,下一个来临时候,计数值才清零,同时产生更新事件,一个工作流程

下三行:缓冲寄存器(这个是真正起作用的寄存器,预分配控制寄存器只是供我们读写用的):某个时刻把预分频器由0改成了1,当计数计到一半的时候改变了分频值,这个变化不会立即生效,而是会等到本次计数周期结束时,产生了了更新事件,预分频器的值才会被传递到缓冲寄存器里面去,才会生效。中途改变,计数频率仍然不变

预分频器内部也是靠计数器来分频的,值为0,计数器一直为0,输出原频率,值为1,0101计数,回到0的时候输出一个脉冲,这样就变成了二分频,值和分配系数有1的偏移

举个例子来说,如果我们想改变ARR寄存器中的值,但是当前的定时还没有结束,在这时如果未设置影子寄存器,那么设定的值会立即生效。而如果设置了影子寄存器,那么新的值会在当前计数周期结束之后生效。

第三行,计数器时钟,分频系数为2,频率是上面的一半

计数器在时钟内每个上升延自增,到0036溢出,知道下一个上升延来才置零,计数器溢出,产生更新事件脉冲,置一个更新中断标志位,只要为1,就回去申请中断,中断响应后,需要在中断程序中手动清零。如同预分频器,计数器也有缓存寄存器(带阴影都有影子寄存器这样的缓存机制,影子寄存器才是真正起作用的。引入影子寄存器为了同步,防止发生错误

计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) =*** CK_PSC / (PSC + 1) / (ARR + 1)

算溢出时间取倒数

时钟树(STM32中产生和配置时钟,并把配置好的时钟发送到个个外设的系统)

时钟是外设的基础,所以时钟也是最先需要配置的东西(程序中主函数之前韩慧执行一个SYSTEMLNIT函数用来配置时钟树。)

中间SYSCLK是系统时钟72MHZ,时钟产生电路有4个震荡源,分别是内部8MHZ高速RC震荡器,外部4-16MHZ晶体震荡器,晶振,一般是8MHZ,外部32.768KHZ晶振,给RTC提供时钟

内部40KHZ低速RC振荡器,给看门狗提供时钟。上面高速晶振提供系统时钟(AHB,APB2,APB1),内部外部都有8MHZ晶振,都可以用,外部更稳定,一般用外部。

在函数中,他会选择内部8MHZ为系统时钟,然后启动外部时钟,进入PLL锁相环进行倍频,8倍频9倍,等锁相环稳定后,选择锁相环输出为系统时钟,外部出现问题,可能会导致时钟慢了10倍,CSS时钟安全系统,切换时钟,检测外部时钟运行状态,一旦外部失效,自动切换到内部,保证系统时钟运行,防止系统时钟卡死出事。

时钟分配电路,系统时钟进入AHB总线,AHB有预分配器,分频为1,进入APB1,分频为2

.无论什么定时器,他们内部的基准时钟都是72MHZ。与门进行输出控制,控制位写的是外部时钟使能,这就是RCC_APB2PERIPHCLOCKCMD的作用,打开时钟,就是在这个位置写1,让左边的时钟能够通过与门输出给外设

定时器输出比较部分,产生PWM波形,用于驱动电机

OC(OUTPUT COMPARE)输出比较

输出比较可以通过比较CNT(计数器计数自增)和CCR(输入捕获,输出比较共用的)捕获比较寄存器值的关系,(>=<)来对输出电平进行置1、置0或翻转的操作,用于输出电平不断跳变的具有一定频率和占空比的PWM波形(主要作用)

每个高级定时器和通用定时器都拥有4个输出比较通道(输出比较,CNT与CCR比较,对输出电平进行操作,可以同时输出4路PWM波形,有各自CCR寄存器,但他们共用一个CNT计数器

级定时器的前3个通道额外拥有死去生成和互补输出的功能

PWM(脉冲宽度调制)数字输出信号,由高低电平组成的连续变化电平信号,使用PWM波形是为了等效的实现一个模拟信号的输出(数字输出端口只有0和1,按道理只能有完全亮或灭,怎么控制亮度,让LED不断亮灭,当频率足够大时,LED不会闪烁,而是呈现中等的亮度,调节时间比例,不同的亮度,电机控速也是一样,用一个很大的频率,通电断电,使得电机速度为一个中等的速度)必须要是惯性系统,高低跳变的数字信号,可以等效为中间这个虚线所表示的模拟量)

频率表示完整高低电平变化周期的时间,PWM频率越大,等效模拟信号越平稳,消耗越大。

占空比,高电平占一个周期的时间,决定PWM等效出来的模拟电压大小,等效关系一般是线性的。

分辨率,占空比变化步距(变化的精细程度)

具体分析,1.CNT与CCR比较结果,输出比较电路,通过TIM-CH1输出到GPIO引脚上

当CNT>=CCR时候,给输出模式控制器传一个信号,输出模式控制器改变他输出OC1RDF(REF参考信号)的高低电压,ETRF输入(不需要了解),REF前往主模式控制器(可以把REF映射到主模式的TRGO输出上),主要还是下面这路,极性选择,写0,信号往上走,信号电平不反转,写1往下走,非门取反,输出反转的信号,这就是极性选择,就是选择是不是要把高低电平反转一下,然后输出使能,选择要不要输出,最后到OC1引脚(CH1),

输出模式控制器如何工作,什么时候给REF高电平,什么时候低电平。

输入CNT,CCR大小关系,输出REF的高低电平

切换输出比较后当什么状态时候会发生的事情

冻结模式中,比如正在输出PWM波,突然想暂停输出,就可以设置这个模式,且高低电平也维持为暂停时刻的状态,保持不变

置有效电平就是高电平,无效电平就是低电平

电平反转,方便输出频率可调,占空比为50的PWM波形。

PWM模式1和2,一般只使用PWM1向上计数,PWM2是PWM1的取反,只是改变极性

输出模式可以设置极性,最终输出之前也可以设置极性

REF是频率可调,占空比也可调的PWM波形,最后通过极性选择输出使能,到达IO口,完成PWM波形的输出

等于30瞬间跳变为低电平,所以在0-29是高电平,周期对于计数器的一个溢出更新周期

0-99,CNT记了一百个数,CCR变化范围取决于ARR的值,ARR越大,CCR的范围越大,对应分辨率就越大(分频加1,计数+1)

PWM频率为更新频率

通道初始化好oc1通道上就可以输出pwm波形,但波形最终要借用gpio口才能输出,oc1通道借用pa0gpio口,重定义,改变引脚对应的复用关系,此时用复用开漏,推免输出,用片上外设控制,复用是同名功能不同。

Tim——cmd运行控制,开启时钟

定时器输入捕获功能

IC(INPUT CAPTURE)输入捕获

输入捕获模式下,当通道输入引脚出现指定电平跳变(上升或者下降,可以通过程序配置)瞬间,当前CNT的值将被锁存(读取CNT,并写入CCR中)到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数

这四个是边沿信号输入引脚,一旦有边沿,输入滤波和边沿检测器就会检测到,让输入捕获电路产生动作,作用和外部中断差不多,都是检测电平跳变,然后执行动作(外部中断执行的动作是像CPU申请中断,这里电路执行的动作是控制后续电路,让当前CNT的值,锁存到CCR中,对比输出比较,一个引脚是输出端口,一个引脚是输入端口

每个高级定时器和通用定时器都拥有4个输入捕获通道

可配置PWMI(PWM的INPUT)模式,同时测量频率和占空比

可配合主从触发模式,实现硬件全自动测量

主从和PWMI模式结合,测量频率占空比就是硬件全自动执行,软件不用干预,也不要中断,需要测量时候增加读取CCR即可

频率测量:(STM测频率而言,只能测量数字信号,需要车辆正弦波,要搭建信号处理电路,把正弦波转换成数字信号,处理成高低电平,然后在输入给STM32

测频法:在闸门时间T内,对上升沿计次,得到N,则频率

测频法:自定一个闸门时间T,通常设置为1S,在1S时间内,对信号上升沿计次,从0开始计,每来一个上升沿,计次+1,每来一个上升沿,其实就是来了一个周期的信号,在1S时间内,来个几个周期,频率就是多少HZ,(频率的定义:1S内出现了多少个重复的周期),这是一种直接按频率定义来测量的方法,闸门时间也可以是2S,计次值除2,就是频率

测频法测量的是一个闸门时间的多个周期自带一个均值滤波,如果在闸门时间内波形频率有变化,得到的其实是这一段时间的平均频率,测频法测量时间慢,测量结果是一段时间的平均值,值比较平滑

测周法:两个上升沿内,以标准频率计次,得到N,则频率

测周法:捕获信号的两个上升沿,测量之间持续的时间,使用一个已知的标准频率的计次时钟,来驱动计数器,从一个上升沿开始计,计数器从0开始,一直计到下一个上升沿,停止,计一个数的时间是1/FC,计N个数时间就是N/FC,N/FC就是周期,再取个倒数,就得到频率的公式,FX = FC/N

测周法只测量一个周期,就能出一次结果,出结果的速度取决于待测信号的频率,一般来说测周法结果更新更快,但是由于他只测量一个周期,所以结果值会受噪声的影响,波动比较大。

测频法适合测高频信号,测周法适合测量低频信号

例如:定了1S为闸门周期,结果1S内一个上升沿都没有,但不能认为频率是0,计次N很少时,误差会非常大,所以测频法适合测量高频率的信号,测周法适合低频信号,低频信号,周期比较长,计次就会比较多,有助于减少误差。如果待测频率太高,那么一个周期内只能计一两个数,如果待测信号再高一些,甚至一个数也计不到,不能认为频率无穷大

N越大,误差越无所谓

中界频率:测频法与测周法误差相等时的频率点(测频法和测周法的N相同)

计数次数越多,±1误差对结果的影响越小

待测频率<中界频率,测周法合适

待测频率>中界频率,测频法合适

***P17异或门(接到了通道123):当输入引脚的任何一个引脚有电平翻转时,输出引脚就产生一次电平翻转

输入信号来到输入滤波器(对信号进行滤波,避免高频的毛刺信号误触发)和边沿检测器(可以选择高电平触发,或者低电平触发)(当出现指定电平,,边沿检测会触发后续电路

有两套滤波和边沿检测电路,第一套电路:经过滤波和极性选择得到TI1FP1,输入给通道1的后续电路,第二套电路:经过另一个滤波和极性选择得到TI1FP2,输入给下面通道2的后续电路,同理下面TI2的信号进来,也经过两套滤波和极性选择,得到TI2FP1和TI2FP2,其中TI2FP1输入给上面,TI2FP2输入给下面,两个输入信号进来可以选择各走各的,也可以选择进行交叉,让CH2引脚输入给通道1,或者CH1引脚输入给通道2,这样做的目的可以灵活切换后续捕获电路的输入,通过数据选择器进行灵活选择,可以把一个引脚的输入,同时映射到两个捕获单元,这是PWMI的经典结构,

例如,第一个捕获通道,使用上升沿触发,用来捕获周期,第二个通道,使用下降沿触发,用来捕获占空比,两个通道同时对一个引脚进行捕获,就可以同时测量频率和占空比,这就是PWMI模式。

一个通道灵活切换两个CH引脚,和两个通道同时捕获一个引脚

TRC是为了无刷电机的驱动(也可以作为捕获信号的输入)

输入信号进行滤波和极性选择后,来到预分频器,预分频器,每个通道各有一个,可以选择对前面的信号进行分频,分频之后的触发信号就可以触发捕获电路进行工作了,每来一个触发信号,CNT的值就会向CCR转运一次,转运的同时,会发送一个捕获事件,这个事件会在状态寄存器置标志位,同时也可以产生中断,如果需要再捕获期间处理事情就可以开启这个捕获中断

例如:配置上升沿触发捕获,每来一个上升沿,CNT转运到CCR一次,因为CNT计数器是由内部的标准时钟驱动的,所以CNT的数值,可以用来记录两个上升沿之间的时间间隔,这个时间间隔就是周期,再取个倒数就是测周法测量的频率了,

每次捕获后要把CNT清0,下次再上升沿(上升沿也占一格)再捕获的时候取出的CNT才是两个上升沿的时间间隔,可以用主从触发模式,自动来完成。

引脚进来先经过一个滤波器,输入为TI1,就是CH1的引脚,输出TI1F,是滤波后的信号,FDTS是滤波器的采样时钟来源

数字滤波器:由一个事件计数器组成,记录到N个事件后会产生一个输出的跳变,简单来说滤波器的工作原理就是,以采样频率对输入信号进行采样,当连续N个值都为高电平,输出才为高电平,连续N个值都为低电平输出才为低电平,如果信号出现高频抖动,导致连续采样N个值不全都一样,那输出就不会变化,这样就可以达到滤波的效果,采样频率越低,采样个数N越大,滤波效果就越好。

主从触发模式:(主模式、从模式和触发源选择三个功能的简称)

主模式:将定时器内部的信号映射到TRGO引脚,用于触发别的外设。

从模式:接收其他外设或者自身外设的一些信号,用于控制自身定时器的运行,也就是被别的信号控制。

触发源选择:选择从模式的触发信号源,也可以认为是从模式的一部分,触发源选择,选择一个指定的信号得到TRGI,TRGI去触发从模式,从模式可以在上述列表里,选择一项操作来自动执行。

三个东西对应三个函数,调用函数,给个参数就行了

例如:让TI1FP11信号自动触发CNT清零,触发源选择可以选择TI1FP1,从模式执行的操作,就可以选择执行RESET的操作,这样TI1FP1的信号就可以自动触发从模式,从模式自动清零CNT,实现硬件全自动测量。手册14.4.2

选择定时器级联,就可以选择一个定时器主模式输出更新信号到TRGO,另一个选择上一个定时器触发从模式

只使用了一个通道,目前只能测量频率,配置好时基单元,启动定时器,CNT就会在预分频之后的时钟驱动下,不断自增,这个CNT就是测周法用来计数计时的,经过预分频之后的时钟频率就是,驱动CNT的标准频率FC,(标准频率 = 72M/预分频系数),下面输入捕获通道1的GPIO口,输入一个上面的方波信号,经过滤波器和边沿检测,选择TI1FP1为上升沿触发,之后输入选择直连的通道分频器选择不分频,当TI1FP1出现上升沿之后,CNT的当前计数值转运到CCR1里,同时触发源选择,选择TI1FP1选择为触发信号,选中TI1FP1为触发信号,从模式选择复位操作,TI1FP1的上升沿也同样会通过上面的触发源选择那一路,取触发CNT清零,注意是先转运CNT的值到CCR里去,再出发从模式给CNT清零或者是非阻塞的同时转移,CNT的值转移到CCR,同时0转移到CNT里面去,不能是先清零CNT,再捕获,否则捕获值都是0了。

例如:左上角图,信号产生一个上升沿,CCR1 = CNT,就是把CNT的值转运到CCR1里面去,这是输入捕获自动执行的让CNT = 0,清零计数器(从模式自动执行的),在一个周期之内,CNT在标准时钟的驱动下,不断自增,并且由于之前清零过了,所以CNT就是从上升沿开始,从0开始计数一直++,指导,下一次上升沿来临,然后执行相同的操作,CCR1 = CNT,CNT = 0,第二次捕获时CNT,继续执行操作,这个电路工作时,始终保持为最新一个周期的计数值N,当我们想要读取信号时,只需要读取CCR1等到N,在计算FC/N即可

CNT的值有上线(65535)

如果信号频率太低,CNT的计数值可能会溢出

想使用从模式自动清除CNT,只能用通道1和通道2,对于通道3和通道4,就只能开启捕获中断,在中断里手动清零了。(这样做程序会处于频繁中断的状态,比较消耗软件资源)

PWMI模式,使用了两个通道捕获一个引脚,可以同时测量周期和占空比,TI1FP1配置上升沿触发,触发捕获和清零CNT,TI1FP2,配置为下降沿触发,通过交叉通道,去触发通道2的捕获单元,

例如:左上角图,最开始上升沿,CCR1捕获,同时清零CNT,之后CNT一直++,在下降沿这个时刻,触发CCR2捕获,这时CCR的值就是高电平期间的计数值,CCR2捕获不会触发CNT清零,CNT++,直到下一次上升沿,CCR1捕获周期,CNT清零,这样执行,CCR1就一整个周期的计数值,CCR2就是高电平期间的计数值,用CCR2/CCR1,就是占空比。这是PWMI模式使用两个通道来捕获频率和占空比

看手册P14.3.5

图里每个部分都对应一个结构体参数,按流程来配置

定时器的编码器接口

ENCODER INTERFACE编码器接口

编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度。

一个编码器有两个输出,一个A相,一个B相,接入STM32定时器的编码器接口,编码器接口自动控制时基单元中的CNT计数器,进行自增或者自减,例如CNT初始值为0,编码器右转CNT++,右转产生一个脉冲,CNT就加一次,左转CNT--,

左转产生一个脉冲,CNT就减一次。编码器接口(相当于带有方向控制的外部时钟)同时控制CNT的计数时钟和计数方向,CNT的值就表示了编码器的位置,每隔一段时间取一次CNT的值再把CNT清零,每次取出来的值就带表示了编码器的速度,编码器的测速实际上就是测频法测正交脉冲的频率,CNT计次(计次是针对上升沿而言的,每来一次上升沿,计数器加1),每隔一段时间取一次计次。本质就是测频法,只不过编码器接口计次更高级,能根据旋转方向,不仅能++,还能--计次,是一个带方向的测速

也可以用外部中断来接编码器(用软件资源来弥补硬件资源)

每个高级定时器和通用定时器都拥有1个编码器接口(四个定时器只能接四个编码器,实在不行用外部中断来接编码器,用软件弥补硬件(例如PWM,我可以直接来个定时中断,中断里手动计数,手动反转电平。比如输入捕获,可以来个

外部中断,然后在中断里手动把CNT取出来,放在变量里。编码器接口也可以来个外部中断,然后在中断里,手动++或--)

D两个输入引脚借用了输入捕获的通道CH1和通道CH2

对于需要频繁执行,操作简单的任务,一般会设计一个硬件模块来自动完成

当编码器的旋转轴转起来时,A相和B相就会输出方波信号,转的越快,方波的频率越高,方波的频率就代表了速度,取出任意一相的信号来测量频率,就能知道旋转速度,但是只有一相的信号无确定旋转方向。要方向还要有另一个线的辅助。

正交信号:当正转时,A相超前B相90度,反转时,A相滞后B相90度。(精度更高,可以抗噪声

正转时,第一个时刻,A相上升沿,对应B此时是低电平,第二个时刻,B相上升沿,对应A相高电平,第三个时刻,A相下降沿,对应B相高电平,B相下降沿,对应A相低电平。

反转时,第一个时刻,B相上升沿,对应A相低电平,第二个时刻A相上升沿,对应B相高电平,第三个时刻,B相下降沿,对应A相高电平,第四个时刻,A相下降沿,对应B相低电平。

当A、B相出现这些边沿时,对应另一相的状态,正转和反转正好是相反的

编码器接口的设计逻辑是:首先把A相和B相的所有边沿作为计数器的计数时钟,出现边沿信号时,就计数自增或者自减,是增还是减,由另一项的状态来确定

编码器接口的两个引脚,借用了输入捕获单元的前两个通道,编码器的输入引脚就是定时器的CH1和CH2两个引脚,信号的通路就是,CH1通过这里,通向编码器接口,CH3和CH4和编码器接口无关,其中CH1和CH2的输入捕获滤波器和边沿检测,编码器接口也有使用,但是后面的是否交叉,预分频器和CCR寄存器,与编码器接口无关,这就是编码器接口的输入部分,编码器接口的输出部分,相当于从模式控制器,控制CNT的计数时钟和计数方向,输出过程就是如果产生边沿信号,并且对应另一相的状态为正转,则控制CNT自增,否则控制CNT自减,此时计数时钟和计数方向都处于编码器接口托管的状态(不会使用),计数器的自增和自减,受编码器的控制。

编码器接口的基本结构:

输入捕获的前两个通道,通过GPIO口接入编码器的A、B相然后通过滤波器和边沿检测极性选择,产生TI1TP1和TI2FP2,通向编码器接口,编码器接口通过控制预分频器控制CNT计数器的时钟,同时,编码器接口还根据编码器的旋转方向,控制CNT的计数方向,编码器正转时,CNT自增,编码器反转时,CNT自减,一般设置ARR为65535,最大量程,将无符号数转为有符号数

工作模式:

编码器接口的工作逻辑:TI1FP1和TI2FP2接的就是编码器的A、B相,在A相和B相的上升沿或者下降沿触发计数,向上计数还是向下计数取决于边沿信号发生时,另一相的电平状态(相对信号的电平)

配置流程:

第一步,RCC开启时钟,开启GPIO和定时器的时钟

第二步,配置GPIO,配置为输入模式

第三步,配置时基单元,预分频器选择不分频,自动重装,一般给最大65535

第四步,配置输入捕获单元,只需要配置滤波器和极性两个参数

第五步,配置编码器接口模式,调用一个库函数即可

第六步,调用TIM_CMD启动定时器

如果需要测量编码器的速度:每隔一段固定的闸门时间,取出一次CNT,然后把CNT清零

输入模式如何选择,看外接模块默认输出,外接输出高电平,配置上拉,输出低电平,配置下拉,保持一致,默认高电平是习惯的状态

ADC(ANALOG-DIGITAL CONVERTER)模拟-数字转换器

ADC可以将引脚上连续变换的模拟量转换成内存中储存的数字变量,建立模拟电路到数字电路的桥梁,STM主要是数字电路,只有高低电平,没有几V的概念,想读取电压值,就要通过ADC模数转换器来实现

(DAC数模转换,与PWM相比,PWM无功率损耗,且更加简单常用)

ADC读取引脚上的模拟电压,转换为一个数据,存在寄存器里,再把这个数据读取到变量里来,就可以进行显示、判断、记录等操作

12位(分辨率,位数越高,量化结果就越精细,对应分辨率就越高)

逐次逼近型ADC,1US转换时间(从AD转换开始,到产生结果,需要花1US的时间,对应最大AD转换频率为1MHZ),

输入电压范围:0-3.3V,转换结果范围:0~4095,ADC的输入电压要求在芯片的负极和正极之间变化,最低电压是负极0V,最高电压是正极3.3V,经过ADC转换之后最小值是0,最大值是4095,0V对应0,3.3V对应4095,中间都是一一对应的线性关系。乘除系数即可

18个输入通道,可测量16个外部和2个内部信号源,外部信号源就是16个GPIO口,在引脚上直接模拟信号就行了,不需要任何的额外电路引脚就能直接测电压,2个内部信号源是内部温度传感器和内部参考电压,温度传感器可以测量CPU的温度,用ADC读取,内部参考电压是一个1.2V左右的基准电压,这个基准电压不随外部供电电压变化而变化,如果芯片的供电不是标准的3.3V测量外部引脚的电压就可能不对,这时可以读取基准电压进行校准,这样就可以得到正确的电压值了。

规则组和注入组两个转换单元,这个是STM32 ADC的增强功能,普通AD转换流程是,启动一次转换,读一次值,然后再启动,在读值,这样的流程,但是STM32的ADC可以列一个组,连续转换多个值,一次性启动一个组,连续转换多个值,并且有两个组,一个是用于常规使用的规则组,一个是用于突发事件的注入组。

模拟看门狗自动检测输入电压范围,此ADC一般可以用于测量光线强度、温度,经常会要求光线高于某个阈值、低于某个阈值,或者温度高于某个阈值,低于某个阈值时,执行一些操作,高于某你个阈值,低于某个阈值的判断,就可以用模拟看门狗来自动执行,模拟看门狗可以检测指定的某些通道,当AD值高于它设定的上阈值或者下阈值时,就会申请中断,就可以在中断函数中执行相应的操作,这样就不用手动读值,再用IF判断了

STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道,最多只能测量10个外部引脚的模拟信号

逐次逼近型ADC的内部结构

这个图是ADC0809的内部结构图,它是一个独立的8位逐次 逼近型ADC芯片,

(以前性能不行,需要外挂一个ADC芯片才能进行AD转换),左边IN0~到IN7,是8路输入通道,通过通道选择开关,选中一路,输入到比较器上方进行转换,下面部分是地址锁存和译码,就是想选中哪一路,就把通道号放在这三个引脚上,然后给一个锁存信号,上面对应的通路开关就自动拨好了,相当于可以通过模拟信号的数据选择器,因为ADC转换是一个非常快的过程,给个开始信号,过个几个US就转换完成了,所以想转换多路信号,那不必设计多个AD转换器,只需要一个AD转换器,然后加一个多路选择开关,想转换哪一路,拨一下开关,选中对应通道,然后再开始转换就行了,这就是输入通道选择的部分,这个ADC0809只有8个输入通道,STM32内部的ADC是有18个输入通道,对应的是18路输入的多路开关,(怎么知道电压对应编码数据是多少呢,用逐次逼近方法比较)输入信号选好后,到电压比较器,它可以判断两个输入信号电压的大小关系,输出一个高低电平指示谁大谁小,它的两个输入端,上端是待测的电压,另一个是DAC的电压输出端,DAC是数模转换器,给一个数据,就可以输出数据对应的电压,DAC内部是适应加权电阻网络来实现的转换,将外部输入的未知的电压和一个已知输出的电压,两个同时输入到电压比较器,进行大小判断,如果DAC输出的电压比较大,就调小DAC数据,如果DAC输出的电压比较小,就增大DAC数据,直到DAC输出的电压和外部通道输入的电压近似相等,这样DAC输入的数据就是外部电压的编码数据了,这就是DAC的实现原理,电压调节的过程是逐次逼近SAR来完成的,为了最快找到未知电压的编码,通常会使用二分法进行寻找,(用二进制,二分法对应每一位的位权,从高到低依次判断是1还是0,八位判断八次),AD转换结束后,DAC输入数据就是未知电压的编码,输出到锁存缓存器,EOC(END OF CONVERT)是转换结束信号,START是开始转换,给一个输入脉冲,开始转换,CLOCK是ADC时钟,因为ADC内部是一步一步进行判断的,所以需要时钟来推动这个过程,下面VREF+和VREF-是DAC的参考电压,例如给一个数据255,是对应5V还是3.3V就由参考电压决定,这个DAC的参考电压也决定了,ADC的输入范围,所以他也是ADC参考电压,左边是整个芯片电路的供电,VCC和GND,通常参考电压的VCC是一样的,会接在一起,参考电压的负极和GND也是一样的,也接到一起,一般情况下ADC输入电压的范围就和ADC的供电是一样的。

STM32的ADC:

左边是ADC的输入通道、包括16个GPIO口,IN0~IN15,和两个内部的通道,一个是内部温度传感器,另一个是VREFINT(V REFERENCE INTERNAL),内部参考电压,总共是18个输入通道,然后到达模拟多路开关,可以指定想要的通道,右边是多路开关的输出,进入到模数转换器,进行逐次比较,转化结果会放在数据寄存器中,读取寄存器就能知道ADC转换的结果了,对于普通的ADC,多路开关一般都是只选中一个的,就是选中某个通道、开始转换、等待转换完成、取出结果,这是普通的流程,但是STM32就可以同时选中多个,在转换的时候,还分成了两个组,规则通道组和注入通道组,规则组可以一次最多选中16个通道,注入组最多可以选中4个通道,就像是去餐厅点菜,普通的ADC是,你指定一个菜,老板给你做,然后做好了送给你,而这里是,你指定一个菜单,这个菜单最多可以填16个菜,然后直接递个菜单给老板,老板就按照菜单的顺序依次做好,一次性给你端上来,这样的话就可以大大提高效率,当然菜单也可以只写一个菜,这样这个菜单就简化成普通模式了,对于这个菜单也有两种,一种是规则组菜单,可以同时上16个菜,但是规则组只有一个数据寄存器,就是桌子比较小,最多只能放一个菜,如果上16个菜,前15个菜都会被挤掉,只能的到第16个菜,所以对于规则组转换来说,如果使用这个菜单的话,最好配合DMA来实现,DMA是一个数据转运小帮手,它可以在每上一个菜之后,把这个菜挪到其他地方去,防止被覆盖,规则组虽然可以同时转换16个通道,但是数据寄存器只能存一个结果,如果不想之前的结果被覆盖,那在转换完成之后,就要尽快把结果拿走,注入组,相当于是餐厅的VIP座位,在这个座位上一次最多可以点4个菜,并且数据寄存器有4个可以同时上4个菜,对于注入组而言,就不用担心数据覆盖的问题了,这就是规则组和注入组的介绍,一般情况下,使用规则组就足够了,如果要使用规则组的菜单,那就配合DMA转运数据,这样就不用担心数据覆盖的问题了。

对于规则组,左下角是触发的部分,对于STM32的ADC触发开始转换的信号有两种,一种是软件触发,就是在程序中手动调用一条代码,就可以启动转换了,另一种是硬件触发,就是触发源,触发源主要是来自定时器,有定时器的各个通道,还有TRGO定时器主模式的输出,(定时器可以通向ADC、DAC这些外设,用于触发转换),ADC经常需要过一个固定时间段转换一次,比如每隔1MS转换一次,正常的思路就是,用定时器,每隔1MS申请一次中断,在中断里手动开启一次中断,这样也是可以的,但是频繁进中断对程序是有一定影响的,如果有很多中断都需要频繁进入,那将会影响主程序的执行,并且不同中断之间,由于优先级的不同,也会导致某些中断不能及时的到响应,如果触发ADC的中断不能及时响应,那ADC的转换频率就会产生影响,所以对于需要频繁进中断,并且只在中断里只完成了简单的工作的情况,一般都会有硬件的支持,可以给TIM3定一个1MS的时间,把TIM3的更新事件选择为TRGO输出,然后再ADC这里,选择触发信号TIM3的的TRGO,这样TIM3的更新事件就能通过硬件自动触发ADC转换了,整个过程不需要进中断,节省了中断资源,这就是定时器触发的作用,也可以选择外部中断引脚来触发中断,都可以在程序中配置,左上角是VREF+、VREF-、VDDA和VSSA,VREF+、VREF-这两个是ADC的参考电压,决定了ADC输入电压的范围,VDDA和VSSA是ADC的供电引脚,一般情况下VREF+要接VDDA,VREF-要接VSSA,STM32没有VREF+、VREF-的引脚内内部已经和VDDA和VSSA接在一起了。VDDA和VSSA是内部模拟部分的电源,例如ADC、RC震荡器、锁相环等,在STM32中VDDA接3.3V,VSSA接GND,所以输入电压的范围就是0~3.3V,右边的ADCCLK是ADC的时钟,也就是ADC0809中的CLOCK,是用于驱动内部逐次比较的时钟来自ADC预分频器,ADC预分频器来源于RCC,APB2时钟72MHZ,然后通过ADC进行分频,得到ADCCLK,ADCCLK最大是14MHZ,对于ADC预分频器,只能选择6分频,结果是12MHZ和8分频结果是9MHZ,上面的是DMA请求,用于触发DMA进行数据转运,再上面是两个数据寄存器,用于存放转换结果,在上面是模拟看门狗,它们可以存一个阈值高限和阈值低限,如果启动了模拟开门狗,并且指定了看门的通道,那么看门狗就会关注它看门的通道,一但超过这个阈值范围,就会乱叫,就会在上面申请一个模拟看门狗的中断,最后通向NVIC,对于规则组和注入组,它们转换完成后,也会有一个EOC转换完成的信号,EOC是规则组完成的信号,JEOC是注入组完成的信号,这两个信号会在状态寄存器置一个标志位,读取这个标志位,就能知道是不是转换结束了,同时这两个标志位也可以去到NVIC,申请中断,如果开启了NVIC对应的通道,它们就会触发中断。

ADC基本结构图

左边是输入通道,16个GPIO口,外加两个内部的通道,然后进入AD转换器,AD转换器里有两个组,一个是规则组,一个是注入组,规则组最多可以选择16个通道,注入组最多可以选择4个通道,转换的结果有放在AD数据寄存器中,其中规则组只有1个数据寄存器,注入组有4个数据寄存器,下面是触发控制,提供开始转换的的START信号,触发控制可以选择软件触发和硬件触发,硬件触发主要是来自于定时器,当然也可以选择外部中断的引脚,右边是来自RCC的ADC时钟CLOCK,ADC逐次比较的过程就是由此时钟推动,上面可以布置一个模拟看门狗用于检测转换的结果的范围,如果超出设定的阈值,就通过中断输出控制,向NVIC申请中断,规则组和注入组在转换完成后会有个EOC信号,会置一个标志位,也可以通向NVIC,右下角是开关控制,在库函数中,就是ADC_CMD函数,用于ADC上电的。

双ADC模式:就是ADC1和ADC2一起工作,可以配合组成同步模式,交叉模式等等模式,交叉模式就是ADC1和ADC2交叉的对一个通道进行采样,这样可以提高采样率。

规则组的四种转换模式

单次转换、非扫描模式

表为菜单,有16个空位,分别为序列1到序列16,可以点菜,写入你要转换的通道

在非扫描模式下,这个菜单只有第一个序列1的位置有效,这时菜单同时选择一组的方式就退化成简单的选中一个的方式了,我们可以在序列1的位置指定我们想转换的通道,比如通道2,然后就可以触发转换,ADC就会对这个通道2进行模数转换,过一小段时间后,转换完成,转换结果放在数据寄存器里,同时给EOC标志位置1,整个转换过程就结束了。判断这个标志位,如果转换完了,就可以在数据寄存器中读取结果了。如果想再启动一次转换,那就需要再触发一次。转换结束,置EOC标志位,读结果。如果想换一个通道转换,那在转换之前,把第一个位置通道2改成其他通道,然后再启动转换。

连续转换、非扫描模式

非扫描模式,所以菜单列表就只用第一个,与上次单次转换不同的是,它在一次转换结束后不会停止,而是立刻开始下一轮转换,然后一直持续下去,这样就只需要触发一次,之后就可以一直转换了。这个模式的好处就是,开始转换之后不需要等待一段时间,它一直都在转换,不需要手动开启转换了。也不用判断是否结束,想要读AD值的时候,就直接从数据寄存器取就行。

单次转换、扫描模式

这个模式也是单次转换,所以每触发一次,转换结束后,就会停下来,下次转换就得再触发才能开始,他是扫描模式,这就会用到这个菜单列表了,可以在菜单里点菜,比如第一个菜是通道2,第二个菜是通道5,等等,这里每个位置是通道几可以任意指定,并且也是可以重复的,初始化结构体里还有个参数,就是通道数目,因为这16个位置可以不用完,只用前几个,那就需要再给个通道数目的参数,告诉他,我有几个通道,这里指定通道7,那它就只看前7个位置,然后每次触发之后,它就依次对前7个位置进行AD转换,转换结果都放在数据寄存器中,为了防止数据被覆盖,就需要用DMA及时将数据挪走,7个通道转换完成后,产生EOC信号,转换结束,然后再触发下一次,就又开始新一轮的转换,这就是单次转换,扫描模式的工作流程。

连续转换、扫描模式

在上一次模式的基础上,可以在一次转换完成后,立刻开始下一次的转换。在扫描模式的情况下,还可以右边一种模式,叫间断模式,它的作用是,在扫描的过程中,每隔几个转换,就暂停一次,需要再次触发,才能继续

触发控制:

这个表就是规则组的触发源,在这个表里有来自定时器的信号,还有来自引脚或者定时器的信号,具体是引脚还是定时器,需要AFIO重映射来确定,最后是软件控制位,也就是软件触发,这些触发信号可以配置右边的寄存器来完成,库函数直接给一个参数就行。

数据对齐

这个ADC是12位的,它的转换结果就是一个12位的数据,但是这个数据寄存器是16位的,所以就存在一个数据对齐的问题,分为两种对齐方式,数据右对齐,和数据左对齐,数据右对齐就是12位的数据向右靠,高位多出来的几位就补0,第二种是数据左对齐,是12位的数据向左靠,低位多出来的几位就补0,我们一般使用的都是数据右对齐,这样读取这个16位寄存器,直接就是转换结果,如果选择左对齐,直接读的话,得到的数据会比实际的大,因为数据左对齐实际上就是把数据左移了4次,二进制的数据,数据左移一次,就等效于把这个数据乘2,左移4次,就相等于把结果乘16了,所以直接读会比实际值大16倍,左对齐的作用就是,不需要高分辨率时,就可以选择左对齐再把后面的低4位去掉,这个12位的ADC就退化成8位的ADC了。

转换时间

AD转换的步骤:采样,保持,量化,编码

STM32 ADC的总转换时间为:TCONV = 采样时间(采样保持花费的时间,采样时间越大,越能避免一些毛刺信号的干扰)+12.5个ADC周期(量化编码花费的时间)

例如:当ADCCLK = 14MHZ,采样时间为1.5个ADC周期,TCONV = 1.5 + 12.5 = 14个ADC周期 = 1US

校准

ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正值(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差

建议在每次上电后执行一次校准

启动校准前,ADC必须处于关电状态超过至少两个ADC时钟周期。

校准是固定的只需要在ADC初始化最后加几条代码即可

硬件电路

电位器的两个固定端,一端接3.3V,另一端接GND,这样中间的滑动端就可以输出一个0~3.3V可调的电压输出了,这里可以接ADC的输入通道例如PA0口,当滑动端往上滑时,电压增大,往下滑时,电压减小,电阻的阻值不能给太小,因为它是直接接在电源正负极上的,阻值太小,这个电阻就会很费电,再小可能就发热冒烟了,一般要接K欧级的电阻

中间是传感器输出电压的电路,一般来说,光敏电阻、热敏电阻、红外接收管、麦克风都可以等效为一个可变电阻,电阻阻值没法直接测量,可以通过和一个固定电阻串联分压,来得到一个反应电阻值电压的电路,传感器阻值变小,下拉作用变强,输出端电压就下降,传感器阻值变大时,下拉作用变弱,输出端受上拉电阻的作用,电压就会升高。固定电阻一般选择和传感器电阻相近的电阻,这样可以得到一个位于中间电压区域比较好的输出

右边的电路是一个简单的电压转换电路,如果我想测量一个05V的VIN电压但是ADC只能接收03.3V的电压,那就可以搭建一个简易转换电路,使用电阻进行分压,上面阻值17K,下面组织33K,加一起是50K,中间的电压就是3.3V,就可以进入ADC转换了,这就是简单的电压转换电路

数据手册11

ADC初始化步骤

第一步,开启RCC时钟,包括ADC和GPIO的时钟,ADCCLK的分频器,也需要配置一下

第二步,配置GPIO,把需要用到的GPIO口配置成模拟输入的模式

第三步,配置多路开关,把左边的通道接入到右边的规则组列表中

第四步,配置ADC转换器,在库函数里,用结构体来配置,配置这一大块电路的参数

第五步,调用ADC_CMD开启ADC,也可以进行一下校准,减小误差

想要软件触发转换,会有函数可以触发,如果想读取结果也会有函数可以读取结果

序列中写入通道

DMA(DIRECT MEMORY ACCESS)直接存储器存取(可以直接访问STM内部存储器,包括运行内存SRAM,程序存储FLASH和寄存器等),主要是用来协助CPU,完成数据转运的工作

DMA可以提供外设(外设寄存器,一般是外设的数据寄存器DR,DATA REGISTER,比如ADC的数据寄存器,串口的数据寄存器)和存储器(运行内存(SRAM)和程序存储器(FLASH)是存储变量数组和程序代码的地方)或者存储器与存储器之前的高速数据传输,无须CPU干预,节省了CPU的资源(本质上都是存储器之间的数据转运)(特别指定了可以转运外设的存储器而言)

12个独立可配置的通道(数据转运的路径):DMA1(7个通道),DMA2(5个通道),是数据转运的路径,从一个地方到另一个地方,就需要占用一个通道,如果有多个通道进行转运,可以个转个的,互不干扰

每个通道都支持软件触发和特定的硬件触发,存储器到存储器的数据转运,一般用软件触发(使用软件触发,一股脑的以最快速度,全部转运完成),外设到存储器的转运一般用硬件触发

每个通道都支持软件触发和特定的硬件触发

STM32F103C8T6 DMA资源:DMA1(7个通道)

存储器映像

有哪些存储器,被安排到了哪些地址上

计算机系统的5大组成部分:运算器、控制器、存储器、输入设备和输出设备,运算器和控制器一般合在一起叫做CPU,计算机的核心关键部分就是CPU和存储器,存储器最重要的是存储器的内容和存储器的地址。

存储器分为两大类:ROM和RAM,ROM就是只读存储器,是一种非易失性、掉电不丢失的存储器,RAM就是随机存储器,是一种易失性、掉电丢失的存储器,ROM分为三块,第一块是程序存储器,FLASH,也就是主闪存,用途就是存储C语言编译后的程序代码,也就是下载程序的位置,运行程序一般也是从主闪存中开始运行的,系统存储器和选项字节,这两块存储器也是ROM的一种,掉电不丢失,实际上它们的存储介质也是FLASH,非主闪存FLASH,系统存储器是用来存储BOOTLOADER,BOOTLOADER程序一般是芯片出厂自动写入的,一般不允许修改,选项字节存的主要是FLASH的读保护、写保护,还有看门狗等等的配置,运行内存SRAM存储我们程序中定义变量、数组、结构体的地方,外设寄存器存储的是我们初始化各个外设,最终读写的东西,外设寄存器起始也是SRAM,存储内核外设NVIC和SYSTICK。

RESERVE,保留区,0X0000没有存储器,别名FLASH或者系统存储器 ,取决于BOOT引脚,程序从0开始执行,故把想执行的程序映射在0地址,映射在FLASH,从FLASH执行,映射在系统区从系统存储器映射BOOTLOMER,映射到SRAM,从SRAM启动,剩下0800开始的FLASH区,用于储存程序代码

DMA的框图

左上角是CORTEX-M3内核,里面包含了CPU和内核外设,剩下的所有东西都可以看成是存储器,所以总共就是CPU和存储器两个东西,FLASH是主闪存,SRAM是运行内存,各个外设都可以看成是寄存器,也是一种SRAM存储器,寄存器是一种特殊的存储器,一方面,CPU可以对寄存器进行读写,就像读写运行内存一样,另一方面,寄存器的每一位背后,都连接了一根导线,这些导线可以控制外设电路的状态,比如置引脚的高低电平,导通和断开、切换数据选择器,或者多位结合起来,当做计数器、数据寄存器,寄存器是连接软件和硬件的桥梁,软件读写寄存器,就相当于在控制硬件的执行,

外设就是寄存器,寄存器就是存储器,使用DMA进行数据转运,都归类为一个问题,就相当于从某个地址取内容,再放到另一个地址去。

为了高效有条理地访问存储器,设计了一个总线矩阵,总线矩阵的左端,是主动单元,也就是拥有存储器的访问权,右边这些,就是被动单元,它们的存储器只能被左边的主动单元读写,主动单元内核有DCODE和系统总线,可以访问右边的存储器,其中DCODE总线是专门访问FLASH的,系统总线是访问其他东西的,由于DMA要转运数据,所以DMA也必须要有访问的主动权,主动单元除了内核、CPU剩下的就是DMA总线了,DMA1和DMA2都各自有一条总线,下面以太网外设自己也私有一个DMA总线,DMA1有7个通道,DMA2有5个通道,各个通道可以分别设置它们转运数据的源地址和目的地址,这样他们就可以独立的工作了,下面的仲裁器,虽然多个通道可以独立转运数据,但是DMA只有一条总线,所以所有的通道都只能分时复用这一条MDA总线,若产生冲突,由仲裁器可以根据通道的优先级决定哪个通道谁先用,在总线矩阵里,也有一个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲突,不过总线仲裁器,仍然会保证CPU得到一半的总线带宽,使CPU也能正常工作,下面的AHB从设备,也就是DMA自身的寄存器,DMA作为一个外设,也会有自己相应的配置寄存器,连接上了总线右边的AHB总线上,所以DMA既是总线矩阵的主动单元,可以读写各种存储器,也是AHB总线上的被动单元,CPU通过AHB,就可以对DMA进行配置。DMA请求(就是触发)就是DMA的硬件触发源,比如说ADC转换完成、串口接收到数据需要触发DMA转运数据的时候,就会通过这条线路,向DMA发出硬件触发信号,之后DMA就可以执行数据转运的工作了,这就是DMA请求的作用。

DMA个个部分与作用总结:访问个个存储器的DMA总线,内部多个通道,可以进行独立的数据转运,仲裁器,用于调度个个通道,防止产生冲突,AHB从设备,配置DMA参数,DMA请求,用于硬件触发DMA数据转运。

FLASH是ROM只读存储器的一种,如果通过总线直接访问的话,无论是CPU,还是DMA,都是只读的,只能读取数据,而不能写入,如果DMA的目的地址,填写了FLASH的区域,那转运时就会出错。也可以配置FLASH接口控制器,对FLASH进行写入,先对FLASH进行擦除,再写入数据。

我们主要用的是数据寄存器,可以正常读写

DMA的基本结构图

数据转运两大站点,左边外设寄存器,右边是存储器站点,包括FLASH和SRAM,存储器一般特指FLASH和SRAM,不包含外设寄存器(一般直接称为外设)

DMA的数据转运可以从外设到存储器,也可以是从存储器到外设,也可以从存储器转运到存储器,外设和存储器两个站点,且FLASH是只读的。都有3个参数,第一个是起始地址,有外设端的起始地址,和存储器端的起始地址,这两个参数决定了数据时从哪里来,到哪里去的,第二个参数是数据宽度,这个参数的作用是,指定一次转运要按多大的数据宽度来进行,可以选择字节BYTE、半字节HALFWORD和字WORD,字节就是8位转运一个UINT8_T,半字节是16位UINT16_T,字是32位UINT32_T,例如ADC的数据,ADC的数据是UINT16_T,所以参数就要选择半字节,依次转运一个UINT16_T,第三个参数是地址是否自增,这个参数的作用是,指定一次转运完成后,下一次转运,是不是要把地址移动到下一个位置去,相当于是指针P++,比如ADC扫描模式,用DMA转运数据,外设地址是ADC_DR寄存器,寄存器这边,显然地址是不用自增的,如果自增下一次转运就跑到别的寄存器那里了,存储器这边地址就需要自增,每转运一个数据后,就往后挪个坑,要不然下次再转就把上次的覆盖掉了,这就是地址是否自增的作用,就是指定是否要转运一次就挪个坑。

要进行存到存转运,把其中一个存储器地址放到外设这个站点(起始地址),起始地址写FLASH或SRAM,就会到FLASH或SRAM找数据,虽然站点叫外设寄存器,但它只是个名字而已,并不是地址只能写寄存器地址,写FLASH会到FLASH里面找,没有限制,只是公司给他起的名字,也可以叫站点A,站点B

传输存储器:用来指定,我总共要转运几次,这个传输计数器是个自减计数器,比如写个5,那DMA就只能进行5次数据转运,转运过程中,每转运一次计数器的值就会减1,当传输计数器减到0之后,DMA就不会再进行数据转运了,之前自增的地址,也会恢复到起始地址的位置,以方便之后DMA新一轮的转运。传输计数器的右边的自动重装器的作用就是,传输计数器减到0之后,是否要自动恢复到最初的值。比如传输计数器给5,如果不使用自动红装器,那转运5次后,DMA就结束了,如果使用自动重装器,那转运5次,计数器减到0后,就会立即重装到初始值5,自动重装器决定了转运的模式,如果不重装,就是正常的单次模式,如果重装就是循环模式,如果你想转运一个数组,那一般是单次模式,转运一轮就结束了,如果是ADC扫描模式+连续转换那为了配合ADC,DMA也需要使用循环模式,这个循环模式和ADC的连续模式差不多。

DMA的触发控制,触发就是决定DMA在什么时机进行转运的,触发源,有硬件触发,和软件触发,具体选择由M2M(MEMORY TO MEMORY )(存储器到存储器)这个参数决定,当给M2M位1时,DMA就会选择软件触发,这个软件触发不是调用某个函数一次就触发一次,而是,以最快的速度,连续不断地出发DMA,指一直到传输计数器清0,(与ADC和外部中断软件触发不太一样,认为是连续触发),软件触发和循环模式不能同时用,因为软件触发是想把传输计数器清零,循环模式是清零后自动重装,如果同时用,那DMA就停不下了,软件触发一般适用于存储器到存储器的转运,因为存储器到存储器的转运是软件启动不需要时机,并且想要尽快完成,当M2M位给0,那就是使用硬件触发了,硬件触发源可以选择ADC、串口、定时器等等,使用硬件触发的转运一般是与外设有关的转运,这些转运需要一定的时机,比如ADC转换完成、串口收到数据、定时时间到等等,当硬件达到这些时机时,传一个信号过来,来触发DMA进行转运。

当给DMA使能后,DMA就准备就绪,可以进行转运了。

DMA进行转运的条件:第一,开关控制,DMA_CMD必须使能,第二,传输计数器必须大于0,第三,触发源,必须有触发信号,触发一次,转运一次,传输计数器自减一次,当传输计数器等于0,且没有自动重装时,无论是否触发,DMA都不会再进行转运了,此时需要DMA_CMD,给DISABLE,关闭DMA,再为传输计数器写入一个大于0的数,再DMA_CMD,给ENABLE,开启DMA,DMA才能继续工作,写传输计数器时,必须要先关闭DMA,再进行,不能在DMA开启时,写传输计数器。

DMA请求

此图是DMA1的请求映像,下面是DMA的7个通道,每个通道都有一个数据选择器,可以选择硬件触发或软件触发,EN是开关控制,为1工作,EN不是控制位,而是决定数据选择器要不要工作。左边的硬件触发源,每个通道的硬件触发源都是不同的,如果想选择ADC1来触发必须选择通道1,如果想选择TIM2的更新事件来触发的话,那就必须选择通道2,每个通道的硬件触发源都不同,如果想使用某个硬件触发源的话,就必须使用它所在的通道(特定的意思)。如果使用软件触发那通道就可以任意选择,因为每个软件通道的选择是一样的。如果要使用ADC1,那就有个库函数ADC_DMACMD,必须使用这个库函数开启ADC1的这一路输出,它才有效,如果想要选择定时器2的通道3那也会有个TIM_DMACMD函数,用来进行DMA输出控制,触发源具体选择哪个,取决于你把哪个外设的DMA输出开启了,如果都开启了,那是一个或门,理论上三个硬件都可以触发,一般情况下,都是开启其中一个,这7个触发源,进入到仲裁器,进行优先级判断,最终产生内部的DMA1请求,(类似中断的优先级)默认优先级是通道号越小,优先级越高,也可以在程序中配置优先级

数据宽度与对齐

数据宽度不一样如何处理

第一列是源端宽度,第二列是目标宽度,第三列是传输数目,当源端宽度和目标宽度都是8位时,转运第一步在源端的0位置,读数据B0,在目标的0位置,写数据B0,之后就是把B1,从左边挪到右边,接着B2、B3,这是源端和目标都是8位的情况,操作也很正常,继续就是源端是8位,目标是16位,它的操作就是,在源端读B0,在目标写00B0,之后读B1写00B1,等等,意思就是如果目标宽度,比源端的数据宽度大那就在目标数据前面多出来的空位补0,之后8位转运到32位,也是一样的处理,前面空出来的都补0,当目标数据宽度,比源端数据宽度小时,比如由16位转到8位现象就是,读B1B0,只写入B0,读B3B2,只写入B2,把多出来的高位舍弃掉,意思就是如果你把小的数据转到大的里面,高位就会补0,如果把大的数据转到小的里面去,高位就会舍弃掉,如果数据宽度一样,那就没事。

数据转运+DMA

将SRAM中的数组DATAA,转运到另一个数组DATAB中,参数配置:外设地址是DATAA数组的首地址,存储器地址,给DATAB数组的首地址,数据宽度,两个数组的类型都是UINT8_T,所以数据宽度都是按8位的字节传输,两个站点的地址都自增,转运完成后DATAB数组的所有数据。就会等于DATAA数组。如果左边不自增,右边自增,,转运完成后,DATAB的所有数据都会等于DATAA[0],如果左边自增,右边不自增,DATAB[0]等于DATAA的最后一个数,DATAB其他的数不变,如果左右都不自增,那就是DATAA[0]转到DATAB[0],其他的数据不变。方向参数,是外设站点转运到存储器站点。传输计数器给7,不需要自动重装,触发选择部分选择软件触发(存到存,不需要等待时机,尽快完成即可),最后调用DMA_CMD,给DMA使能,转运7次后,传输计数器自减到0,DMA停止,转运完成,这里的数据转运是一种复制转运,转运完成后的DATAA的数据并不会消失。

ADC扫描模式+DMA

左边是ADC扫描模式的转运流程,触发一次,7个通道依次进行AD转换,然后把转换结果都放在ADC_DR寄存器里面,在每个单独的通道转换完成后,进行一次DMA数据转运,并且目的地址进行自增,防止数据被覆盖,DMA的配置,外设地址,写入ADC_DR这个寄存器的地址,存储器的地址,可以在SRAM中定义一个数组ADVALUE然后把ADVALUE的地址当做存储器的地址,之后数据宽度,因为ADC_DR和SRAM数组需要UINT16_T的数据,所以数据宽度都是16位的半字传输,外设地址不自增,存储器地址自增,传输方向,是外设站点到存储器站点,传输计数器和通道数一样,通道有7个,所以计数7次,计数器是否重装,看ADC的配置,ADC如果是单次扫描,那DMA的传输计数器可以不自动重装,转换一轮就停止,如果ADC是连续扫描,那DMA就可以选择使用自动重装,在ADC启动下一轮的转换的时候,DMA也启动下一轮的转运,ADC和DMA同步工作,触发选择

ADC-DR的值只有在单个通道转换完成后才会有效,所以DMA转运时机,需要和ADC单个通道转换完成同步,所以DMA触发要选择ADC的硬件触发,ADC扫描模式在单个通道完成转换后,不会置任何标志位,也不会产生中断(不太好判断完成时机是什么),但是会产生DMA请求,去触发DMA转运。一般来说DMA最常用的用途就是配合ADC的扫描模式,来解决ADC固有的缺陷(数据覆盖的问题),使得它两成为最常见的伙伴。

数据手册第10章,第二章

位段,映射了外设寄存器和SRAM全部的位,位短区相当于位寻址,所有位都分配了地址,操作地址,就相当于操作其中一个位。因为空间很大,所以存在位段,单独操作寄存器或SRAM的某一位,位段区是另找了一个地方,开辟了一段地址区域,

初始化DMA步骤:

第一步,RCC开启DMA的时钟,AHB总线的设别

第二步,直接调用DMA_INIT,初始化配置的参数,包括外设和存储器站点的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否需要自动重装、选择触发源、通道优先级

第三步,DMA_CMD给指定通道使能,如果使用的是硬件触发,要在对应外设调用XXX_DMACMD,开启一下触发信号的输出,需要DMA的中断,就调用DMA_ITCONFIG,开启中断输出,再在NVIC中配置相应的中断通道,然后写中断函数就行了,如果传输计数器清0,再想给传输计数器赋值,就DMA失能、写传输计数器、DMA使能,就可以了

外设地址是固定的

USART串口

通信接口

通信的目的:将一个设备的数据传送到另一个设备,单片机有了通信的功能,就能与众多别的模块互联,极大扩展硬件系统(例如STM32中有他没有的功能

例如蓝牙无线遥控,MPU测量加速度等等,需要外挂芯片完成,外挂芯片数据都在STM32外面,如何才能获取数据呢,需要在两个设备中,连接上一根或多个通信线,通过通信线路发送或者接收数据,完成数据交换,从而实现控制外挂模块和读取外挂模块数据的目的)

通信协议:指定通信的规则,通信双方按照协议规则进行数据收发(例如考试传答案,与对方约定通信协议,咳嗽一声表示开始,1-A,2-B,挥手表示通信结束)

通信目的是进行信息传递,双方约定的规则就是通信协议。

TX(TRANSMIT EXCHANGE)数据发送脚,RX数据接收脚

SCL时钟,SDA数据

MOSI是主机输入数据脚 MISO是主机输出数据脚 CS片选,指定通信对象

CAN通信,CAN-H PR-L 差分数据脚,两个引脚表示一个差分数据

USB,引脚是DP和DM,也是差分数据脚(差分线组合成为一个数据线

数据按照协议规定在这些引脚上进行输入和输出,从而实现通信

全双工:通信双方能够同时进行双向通信,全双工,一般有两根通信线,发射接收互不影响。

半双共

单工:数据只能从一个设备到另一个设备(而不能反着来)

时钟特性(发送波形,高电平然后低电平,接收方如何知道你是10还是1100呢,这就需要一个时钟信号来告诉接收方,什么时候需要采集数据)时钟特性分为同步和异步(I2C和SPI有单独时钟线,所以他们是同步的,接收方可以在时钟信号的指引下进行采样,其他没有时钟线,所以需要双方约定一个采样频率(当成时钟),这就是异步通信,并且还要加一些帧头帧尾,进行采样位置的对齐。

电平特性,单端信号即引脚的高低电平都是对GND的电压差,单端信号通信双方必须要共地,就是把GND接在一起。这里通信的引脚,前三个还需要加一个GND引脚,不接没法通信,之后CAN和USB是差分信号,靠差分引脚电压差来传输信号的,差分信号可以不用gnd,但是usb还是需要共地的,使用差分信号能极大的提高抗干扰能力,所以传播速度和距离会非常高,性能不错

设备,串口和usb属于点对点的通信(你跑到办公室和老师对话),中间三个可以在总线上挂载多个设备(老师在教室面对所有同学谈话),需要有寻址过程,以确定通信的对象

串口通信较简单,且单片机内会有串口口的硬件外设,使用方便,串口是点对点通信,两个设备之间。

Usb转串口模块,上面有芯片,信号是ch340,可以把串口协议变成usb协议。蓝牙串口也是一样,下面4哥是串口通信的引脚,上面芯片可以和手机互联,实现手机遥控单片机的功能

TX和RX是通信引脚,单端信号,它们的高低信号都是相对于GND的,严格上来说GND也算是通信线,串口通信的TX,RX,GND是必须要接的,vcc如果一个硬件没有供电(例如蓝牙串口,stm32有供电,蓝牙串口没有供电,需要把他们接在一起,stm通过线,向右边的子模块供电,注意供电电压,按照子模块要求

简单双向串口通信有两根通信线(发送端TX和接收端RX)

复杂一点的串口通信还有时钟引脚、硬件流控制的引脚

TX和RX要交叉连接

当只需单向的数据传输时,可以只接一根通信线(设备1到设备二,就接tx到rx的线),另一根不接

直接从单片机哩出来的一般是ttl电平,相同电平才能互相通信,当电平标准不一致时,需要加电平转换芯片

差分信号抗干扰能力强,使用他,距离可达到上千米

硬件规定,一个硬件用tx发送高低电平,另一个用rx接收高低电平

串口参数及时序

如何用0和1构成我们想要发送的一个字节数据

串口数据帧的整体结构:串口中,每一个字节都装载在一个数据帧里面,每个数据帧都由起始位、数据位和停止位组成,数据位有8个,代表一个字节的8位,右边中还可以在数据位的最后加一个奇偶校验位,这样数据位总共就是9位,其中有效载荷时前8位,代表1个字节,校验位跟在有效载荷后面,占1位

波特率:规定串口通信的速率(串口一般使用异步通信,需要双方约定一个通信速率),例如每隔1S发送一位,接收方也要每隔1S接收一位,接收快了,就会重复接收某些位,如果接收慢了,就会漏掉某些位,发送和接收必须约定好速率,速率参数就是波特率,波特率本义是每秒传输码元的个数,单位是码元/S,或者直接叫波特(BAUD),比特率是每秒传输的比特数,单位是BIT/S,或者叫BPS,在二进制调制的情况下,一个码元就是一个BIT,此时波特率就等于比特率,单片机的串口通信,基本都是二进制调制,也就是高电平表示1,低电平表示0,一位就是1BIT,(二进制数值相同)规定波特率为1000BPS,表示1S要发1000位,每一位的时间就是1mS,发送方每隔1mS发送一位,接收方每隔1mS接收一位,

高低电平是函数,瞬间发送,接受的是上升或下降沿

起始位:标志一个数据帧的开始,固定为低电平(串口的空闲状态是高电平,没有数据传输的时候引脚必须置高电平,作为空闲状态)需要传输的时候先发送一个起始位,起始位必须是低电平,来打破空闲状态的高电平,产生一个下降沿(告诉接受设备,这一帧数据要开始了),如果没有起始位,当发送8个1的时候,数据线一直都是高电平,没有任何波动,这样接收方就不知道我是否发送数据,所以必须要有一个固定为低电平的起始位,产生下降沿,来告诉接受设备,为要发送数据了-----起始位固定为0,产生下降沿,表示传输开始

停止位;同理在一个字节数据发送完成后,必须要有一个停止位,这个停止位的作用是,用于数据帧间隔,固定为高电平,同时这个停止位也是为下一个起始位做准备的,如果没有停止位,那当为数据最后一位是0的时候,下次再发送新的一帧,就没法产生下降沿了-----停止位固定为1,把引脚恢复成高电平,方便下一次的下降沿,如果没有数据了,引脚也为高电平,代表空闲状态

数据位:表示数据帧的有效载荷,1为高电平,0位低电平,低位先行(比如发送0x0f,先变成二进制,然后从最低位开始发送,即11110000,依次放在发射引脚,如果要发射0x0f,按照波特率要求,

定时翻转,产生对应波形。

校验位:用于数据验证,是根据数据位计算得来的,串口使用奇偶校验位方法,奇偶校验可以用来判断数据传输是不是出错了,如果数据出错了可以选择丢弃或者要求重传,校验可以选择三种方式,无校验、奇校验和偶校验,无校验就是不需要校验位,波形就是上图左边的,起始位、数据位,停止位一共3个部分,奇校验和偶校验的波形就是上图右边的,起始位、数据位、校验位、停止位,总共4个部分,如果使用了奇校验,那么包括校验位在内的9位数据会出现奇数个1,如果传输 0000 1111,目前总共4个1,是偶数个,那么校验位就需要再补一个1,连同校验位就是0000 1111 1,总共5个1,保证1的个数为奇数,如果数据是0000 1110,此时3个1,是奇数个,那么校验位就补1个0,连同校验位就是0000 1110 0,总共还是3个1,1的个数为奇数,发送方,在发送数据后,会补一个校验位,保证1的个数为奇数,接收方在接收数据后,会验证数据位和校验位,如果1的个数还是奇数,就认为数据没有出错,如果在传输中,因为干扰,有一位由1变成0,或者由0变成1了,那么整个数据的奇偶特性就会变化,接收方一验证,发现1的个数不是奇数,那就认为传输出错,就可以选择丢弃,或者要求重传,这就是奇校验的差错控制方法。如果选择双方约定偶校验,那就是保证1的个数是偶数,校验方法也是同理,但是奇偶校验的检出率不是很高,例如,如果有两位数据同时出错,就特性不变,那就校验不出来了,就能校验只能保证一定程度上的数据校验,如果想要更高的检出率可以选择CRC校验,STM32内部也有CRC外设。

数据位:有两种表示方法,一种是把校验位作为数据位的一部分,另一种就是把校验位和数据位独立开,数据位就是有效载荷,校验位就是独立的1位,在串口助手里就是选择的把数据位和校验位分开描述的方法,总之无论是合在一起,还是分开描述,描述的都是同一个东西

第一个波形:这个波形是发送一个数据0X55时,在TX引脚输出的波形,波特率是9600,每一位的时间就是1/9600,大概是104US,没发送数据的时候是空闲状态高电平,数据帧开始,先发送起始位,产生下降沿,代表数据帧开始,数据0X55转为2进制,低位先行,就是依次发送1010 1010,然后参数是,1位停止,无校验,所以数据帧之后就是停止位,把引脚置回高电平,在STM32中,这个根据字节数据翻转高低电平,是由USART外设自动完成的,不用我们管,也可以软件模拟产生这样的波形,定时器定一个104US的时间,时间到之后,按照数据帧要求,调用GPIO_WRITEBIT置高低电平,产生一个一模一样的波形,也可以完成串口通信,在TX引脚发送就是置高低电平,在RX引脚接收就是读取高低电平,这也可以由USART外设完成,如果想软件模拟的话那就是定时调用GPIO_READINPUTDATABIT来读取每一位,接收的时候也需要一个外部中断,在起始位的下降沿触发,进入接收状态,并且对其采样时钟,然后依次采样8次,这就是接受的逻辑

总结,tx引脚输出定时翻转的高低电平,rx引脚接收

USART :同步收发器(多了时钟输出模式,不支持时钟输入,为了兼容别的协议或者特殊用途设计的,不支持两个usatr直接进行同步通信,串口主要还是异步通信

)UART:异步收发器

USART (UNIVERSAL SYNCHRONOUS/ASYNCHRONOUS RECEIVER/TRANSMITTER)

通用同步,异步收发器

USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可以自动接收RX引脚的数据帧时序,拼接成一个字节数据,(串口通信的硬件支持电路,分为发送和接收,发送将字节数据转换为协议规定的波形,从tx引脚发送,接收部分自动接收rx引脚的波形,按照协议规定,解码为一个字节数据,存放在数据寄存器里)

配置好电路,直接读写数据寄存器,就能自动发送和接收数据了

自带波特率发生器,最高达4.5MBITS/S,配置波特率,就是就是一个分频器,比如APB2总线给个72MHZ的频率然后波特率发生器进行一个分频,得到我们想要的波特率时钟,在这个时钟下,进行收发,就是我们指定的通信波特率,

支持配置的参数:可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)

可选校验位(常用无校验/奇校验/偶校验),可以通过配置寄存器完成,使用函数直接给结构体赋值也行。

支持同步模式(多了个clk的输出)、硬件流控制、DMA、智能卡、IRDA、LIN,硬件流控制:A设备有个TX向B设备的RX发送数据,A设备一直在发,发的太快了,B处理不过来,如果没有硬件流控制,那B就只能抛弃新数据或者覆盖原数据了,如果有硬件流控制,在硬件电路上,就会多出一根线,如果B没准备好接受,就置高电平,如果准备好了,就置低电平,A接收到了B反馈的准备信号,就只会在B准备好的时候,才发数据,如果B没准备好,那数据就不会发送出去,硬件流控制可以防止处理慢而导致数据丢失的问题,硬件流控制STM32也是有的,但是一般不用,串口也支持DMA数据转运,如果有大量的数据进行收发,可以使用DMA转运数据,减轻CPU的负担

STM32F103C8T6 USART资源:USART1、USART2、USART3,USART1是APB2总线的设备,USART2,3是APB1总线的设备,可挂

载很多设备

SW_RX、IRDA_OUT/IN这些是智能卡和IRDA通信的引脚(不用管)

发送和接收的字节数据存在串口的数据寄存器,数据寄存器分为发送数据寄存器TDR(TRANSMIT),另一个是接收数据寄存器RDR(RECEIVE DR),两个寄存器占用同一个地址,在程序上只表现为一个寄存器,就是数据寄存器DR(DATA REGISTER),但实际硬件中,分成了两个寄存器,一个用于发送的TDR,一个用于接收的RDR,TDR是只写的,RDR是只读的,当进行写操作时,数据就写入到TDR,当进行读操作的时候,数据就是从RDR中读出来的,下面是两个移位寄存器,一个用于发送,一个用于接受,发送移位寄存器的作用就是,把一个字节的数据一位一位地移出去,正好对应串口协议的波形数据位,例如为在某时刻给TDR写入了0X55这个数据,在寄存器就是二进制存储,0101 0101,此时硬件检测到我写入数据了,就会检查移位寄存器是否有数据正在移位,如果没有,这个0101 0101就会立刻全部移动到发送移位寄存器,准备发送,当数据从TDR移动到移位寄存器的时候会置一个标志位,叫TXE(TX EMPTY),发送寄存器空,检查这个标志位,如果置1了,就可以在TDR继续写入下一个数据了,当TXE标志位置1时,数据其实还没有发送出去,只要数据从TDR转移到发送移位寄存器了TXE就会置1,我们就可以写入新的数据了,然后发送移位寄存器就会在发生器控制的驱动下,向右移位,然后一位一位地,把数据输出到TX引脚,向右移位,正好与串口协议规定的低位先行是一致的,当数据移位完成后,新的数据就会在此自动地从TDR转移到发送移位寄存器里来,如果当前移位寄存器的移位还没有完成,TDR的数据进行等待,一但移位完成,就会立刻转移过来,TDR和移位寄存器的双重缓存,可以保证连续发送数据的时候,数据帧之间不会有空闲,提高了工作效率,简单来说就是数据一但从TDR转移到发送移位寄存器了,就立刻把下一个数据放在TDR等着,一但转移完毕,新的数据就会立刻跟上,这样做效率比较高。

接收端也是类似的,数据从RX引脚通向接收移位寄存器,在接收器控制器的驱动下,一位一位的读取RX电平,先放在最高位,然后向右移,移位8次后,就能接收板一个字节了,因为串口协议规定是低位先行,所以接受移位寄存器是从个高位往低位方向移动的,之后,当一个字节移位完成之后,这一个字节的数据就会整体地一下子转移到接收数据寄存器RDR里来,在转移的过程中也会置一个标志位,叫RXNE(RX NOT EMPTY),接收数据寄存器非空,当检测到RXNE置1之后,就可以把数据读走了,同样也是两个寄存器进行缓存,当数据从移位寄存器转移到RDR时,就可以直接接受下一帧数据了,这就是USART外设整个的工作流程。发送需要加上帧头帧尾,接收需要剔除帧头帧尾,这些操作内部电路会自动执行。(大体就是数据寄存器和移位寄存器)

发送器控制:用来控制发送移位寄存器的工作的

接收器控制:用来控制接受移位寄存器的工作

硬件数据流控制(如果发送太快,接收来不及处理,会导致失去和覆盖数据的现象):有两个引脚,一个是NRTS,一个是NCTS,NRTS(REQUEST TO SEND)是请求发送,是输出脚,也就是告诉别人,我当前能不能接受,NCTS(CLEAR TO SEND)是清除发送,是输入脚也就是接受别人的NRTS的信号的,前面的N是低电平有效,使用步骤:找另一个支持流控的串口它的TX接到我的RX,然后我的RTS输出一个能不能接受的反馈信号,接到对方CTS,当我能接收的是吧,RTS就置低电平,请求对方发送,对方的CTS收到后们就可以一直发,当我处理不过来时,比如接收数据寄存器一直没有读,又有新的数据过来了,代表我没有及时处理,那RTS就置高电平,对方CTS接收到之后,就会暂停发送,直到接受数据寄存器被读走,RTS置低电平,新的数据才会继续发送,反过来,TX给对方发送数据时我的CTS就接到对方的RTS,用于判断对方,能不能接收,TX和CTS是一对的,RX和RTS是一对的,CTS和RTS也要交叉连接,这就是流控的工作模式(一般不使用流控)

Sclk控制用于产生同步的时钟信号,配合发送移位寄存器输出,发送移位寄存器每移位一次,同步时钟就跳变一个周期,时钟告诉对方,我移出去一位数据了,看是否需要时钟信号来指导接受一下,这个时钟只支持输出不支持输入,两个USART之间,不能实现同步的串口通信,这个时钟信号的用途,第一个就是兼容别的协议,比如串口加上时钟之后,和SPI协议特别像,所以有了时钟输出的串口,就可以兼容SPI,这个时钟也可以做自适应波特率,比如接受设备不确定发送设备给的什么波特率,可以测量一下这个时钟的周期,然后计算得到波特率(需要另外写程序来实现这个功能)

唤醒单元:实现串口挂在多设备,串口一般是点对点的通信,点对点,只支持两个设备互相通信,想发数据直接发就行,而多设备,在一条总线上,可以接多个从设备,每个设备分配一个地址,先跟某个设备通信,就先进行寻址,确定通信对象,在定义数据收发,这个唤醒单元就可以用来实现多设备的功能,可以给串口分配一个地址,当我发送指定地址时,此设备唤醒开始工作,当我发送别的设备地址时,别的设备就唤醒工作,这个设备没收到地址,就会保持沉默,这样实现多设备的串口通信了。(不用)

中断输出控制:中断申请位就是状态寄存器的各种标志位,状态寄存器这里有两个标志位比较重要,一个是TXE发送寄存器空,另一个是RXNE接收寄存器非空,这两个是判断发送状态和接收状态的必要标志位,中断输出控制就是配置中断是不是能够通向NVIC

波特率发生器:其实就是分频器,APB时钟进行分频,得到发送和接收移位的时钟,时钟输入是发PCLKX(X=1或2),因为USART1挂载在APB2,所以就是PCLK2的时钟,一般是72M,其他的USART都挂载在APB1,所以是PCLK1的时钟,一般是36M,之后时钟在进行一个分频,除以一个USARTDIV的分频系数,USARTDIV是一个数值,分为整数部分和小数部分,因为有些波特率,用72M除于一个整数的话,可能除不尽,会有误差,所以这里的分频系数是支持小数点后4位的,分频就更加精准,之后分频完还要再除个16,得到发送时钟和接收器时钟,通向控制部分,然后右边,如果TE(TX ENABLE)为1,就是发送器使能,发送部分的波特率就有效,如果RE(RX ENABLE)为1,就是接收器使能了,接收部分的波特率就有效。

复用,相同名字有不同功能

USART的基本结构

最左边的是波特率发生器,用于产生约定的通信速率,时钟来源是PCLK2或1,经过波特率发生器分频后,产生的时钟通向发送控制器和接收控制器,发送控制器和接收控制器用来控制发送移位和接收移位,之后由发送数据寄存器和发送移位寄存器这两个寄存器的配合,将数据一位一位的移出去,通过GPIO口的复用输出,输出到TX引脚,产生串口协议规定的波形,这个移位寄存器是向右移的,是低位先行,当数据由数据寄存器转移到移位寄存器时,会置一个TXE的标志位,通过判断这个标志位,就可以知道是不是可以写入下一个数据了,接收部分也是类似的,RX引脚的波形,通过GPIO输入,在接收控制器的控制下,一位一位地移入接收移位寄存器,移完一帧数据后,数据就会统一转运到接收数据寄存器,在转移的同时,置一个RXNE标志位,检查这个标志位,就可以知道是不是收到数据了,同时这个标志位也可以去申请中断,这样就可以在收到数据时,直接进入中断函数,快速的读取和保存数据,虽然有四个寄存器,但是在软件层面上,只有一个DR寄存器可以供我们读写,写入DR时,数据走上面这条路,进行发送,读取DR时,数据走下面这条路,进行接收,这就是USART进行串口数据收发的过程,右下角是个开关控制(配置完成后,用cmd开启外设)。

四种选择:9位字长(数据位长度),有校验或无校验,8位字长有校验或者无校验,最好选择9位字长,有校验或者8位字长无校验,这样每一帧的有效载荷都是1字节,

STM32的串口可以配置停止位为0.5、1、1.5、2,这四种参数的区别,就是停止位的时长不一样,1位停止位,这时停止位的时长就和数据位的一位,时长一样,1.5停止位就是数据位一位,时长的1.5倍,2个停止位,那停止位时长就是2倍,0.5个停止位,时长就是0.5倍,一般选择1位停止位。

串口的输出TX比输入RX简单很多,输出就定时翻转TX引脚高低电平就可以,输入不仅要保证采样频率和波特率一致,还要保证每次输入采样的位置,要正好处于每一位的正中间,只有在每一位的正中间采样,这样高低电平读进来,才是最可靠的,如果采样点过于靠前或者靠后,那有可能高低电平正在翻转,电平还不稳定,或者稍有误差,数据就采样错了,输入最好还要对噪声有一定的判断能力,如果是噪声,最好能置个标志位提醒一下,STM32设计的输入电路,上图展示的是USART的起始位侦测,当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率,连续采样一帧数据,同时,从起始位开始,采样位置就要对齐到位的正中间,只要第一位对齐了,后面都是对齐的,为了实现这些功能对输入的电路对采样时钟进行了细分,它会以波特率的16倍频率进行采样,也就是在一位地时间里,可以进行16次采样,它的策略是最开始,空闲状态高电平,那采样就一直是1,在某个位置突然采集到一个0,那么就说明在这两次采样之间,出现了下降沿,如果没有任何噪声,那之后就应该是起始位了,在起始位,会进行连续16次采样,没有噪声的话,这16次采样,肯定都是0,实际电路有噪声,即使出现下降沿了,后序也要再采样几次,以防万一,这个接收电路还会再下降沿之后的第3次、5次、7次,进行一批采样,在第8次、9次、10次,再进行一批采样,且这两批采样,都要要求每3位里面至少有2个0,没有噪声就全是0,满足情况,如果有轻微的噪声,导致3位里面,只有两个0,另一个是1,也算是检测到了起始位,但是在状态寄存器里会置一个NE(NOISE ERROR),噪声标志位,提醒一下,数据收到了,但是有噪声,如果3位里面只有一个0,就不算检测到了起始位,这时电路就忽略前面的数据,重新开始捕捉下降沿,这就是STM32的串口,在接收过程中,对噪声的处理,如果通过了这个起始位侦测,那接收状态就由空闲,变为接收起始位,同时,第8、9、10次采样的位置,就正好是起始位的正中间,之后接收数据位时,就都在第8、9、10次,进行采样,这样就能保证采样位置在位的正中间了,这就是起始位侦测和采样位置对齐的策略

内部是16位波特率的采样时钟

用库函数方便,系统帮我们选

串口初始化:

第一步,开启时钟,把需要用的USART和GPIO的时钟打开

第二步,GPIO初始化,把TX配置成复用输出,RX配置成输入

第三步,配置USART,直接用一个结构体,就可以配置好所有参数

第四步,如果只需要发送的功能就直接开启USART,如果需要接收的功能,还需要再配置中断,在开启USART之前,加上ITCONFIG和NVIC的代码就可以了

数据包的作用是:把一个个单独的数据给打包起来,方便进行多字节的数据通信,例如,陀螺仪传感器,需要用串口发送数据到STM32,陀螺仪的数据,X轴为一个字节、Y轴为一个字节、Z轴一个字节,一共3个数据需要连续不断的发送,当我像这样XYZXYZXYZ连续发送的时候,接受方不知道这个数据哪个对应Y,哪个对应X,哪个对应Z,因为接收方可能从任意的位置接收,所以可能出现数据错位的现象,我们需要一种方式把数据进行分割(最高位当标志位),把XYZ这一批数据分割开来,分割成一批批数据包,这样在接收的时候,就知道了,数据包第一个数据就是X,数据包第二个数据就是Y,数据包第三个数据就是Z,这就是数据包的任务,就是把同一批的数据进行打包和分割。

串口数据包,通常使用的是额外添加包头包尾的这种方式,一批数据规定4个字节,前面加包头(例0xff),后面加包位(0xfe),接收到包头,知道数据包来了接下来的四个数据,就当成数据包的第1.2.3。4个数据,存在一个数组,接到0xfe,置一个标志位,告诉程序接收到了一个数据包,新数据过来,再重复

防止数据包包头包尾和数据重复的方法,第一种,限制载荷数据的范围,在发送的时候对数据进行限幅,第二种,尽量使用固定长度的数据包,第三种,增加包头包尾的数量,并且让它尽量呈现出载荷数据出现不了的状态。

文本数据包字节经过编码和译码(不用担心重复问题)

串口收发Hex数据包传输直接,解析数据非常简单,比较适合模块发送原始数据

串口收发文本数据包数据直观,容易理解,非常灵活,适合输入指令进行人机交互的场合,例如蓝牙at。cnc和3d打印机最常用的g代码,解析效率低

数据包的收发流程

数据包的发送(定义数组和字符串,调用send函数)

数据包的接收

其他可以套用

过程,收到字节,程序进行中断,拿到之后,就得退出中断,拿一个数据都是独立的过程,数据包具有前后关联性,包头后是数据,数据后是包尾,要有不同的处理逻辑。需要设计一种能够记住不同状态的机制,在不同状态执行不同的操作,同时还要进行状态的合理转移,这种程序设计思维叫做“状态机”。

第一个状态是等待包头,第二个状态是接收数据,第三个状态是等待包尾,每个状态需要一个变量来标志一下,类似于置置标志位,标志位只有0和1,状态机是多标志位的一种方式

执行流程是最开始S = 0,收到一个数据,进中断,根据S = 0,进入第一个状态的程序,判断数据是不是包头FF,如果是FF,则代表收到包头,之后置S = 1,退出中断,结束,这样下次再进中断,根据S = 1,就可以进行接收数据的程序了,在第一个状态,如果收到的不是FF就说明数据包没有对齐,应该等待数据包包头的出现,这时状态仍然是0,下次进中断,就还是判断包头的逻辑,直到出现FF,才能转到下一个状态,之后出现了FF,就可以转移到接收数据的状态了,这时再收到数据,就可以直接把它存在数组中,另外再用一个变量,记录收了多少个数据,如果没收够4个,就一直是接收状态,如果收够了,就置S = 2,下次进中断时,就可以进入下一个状态了,最后一个状态就是等待包尾,判断数据是不是FE,这样就可以置S = 0,回到最初的状态,开始下一个轮回。不是fe,进入重复等待包尾状态,知道真正接收到包尾,防止因数据和包头重复造成的错误

(编程思路)状态机使用的基本步骤:先根据项目要求,画几个圈,考虑好各个状态在什么情况下会进行转移,如何转移,画好线和转移条件,最后根据图来进行编程,例如,做个菜单,按什么键,切换什么菜单,执行什么程序。

输入模式不分普通或者复用,一个线可以有一个输出,但可以有多个输入,输入外设和gpio都可以用CRTL+ALT+空格,联想参数

TX引脚产生数据对应引脚,rx引脚接受并根据通行协议转换数据

Hex原始数据显示,选择文本模式显示字符串,即编码后的形式

传递数组用指针

数据包作用,把一个个单独数据给打包起来,方便我们进行多字节的数据通信

,实际应用中需要把多个字节打包为一个整体进行发送

例如mpu6050发送xyz,接收方不知道数据哪个对应x,y,z,从任意位置接受

,出现数据错位的现象,研究方式,把数据进行切割,分成数据包,再接收的时候,就知道xyz

数据包任务就是把同一批数据进行切割和打包,使用固定包长,包头包尾固定,就可以知道哪个数据是载荷数据

接受数据包,每收到一个字节,程序都会进行一遍中断,中断函数里,我们可以拿到这个字节,拿到之后退出中断,每拿到一个数据都是一个独立的过程,数据包有前后关联性,包头之后是数据,数据之后是包尾

包头数据和包围状态都需要有不同的处理逻辑,设计一个能记住不同状态的机制,不同状态不同操作,同时还要进行状态的合理转移,状态机,使用状态机来接受一个数据包

三个状态,等待包头,接受数据,等待包尾,每个状态用变量标记一下,类似于置标志位,进中断推进数据进行

I2C总线(Inter IC Bus)(对比串口,串口是从tx引脚到rx引脚发送数据流,以字节为单位,组合多个字节,变成数据包)(读写寄存器控制电路,至少要定义两个字节数据,一个是指定寄存器地址,一个是这个地址下存储器存的内容,写入内容就是控制电路,读取内容就是获取电路状态,问题,单片机读写自己的寄存器,可以直接通过内部数据总线来实现,直接用指针操作,不用我们担心)但是模块寄存器在单片机外面,不可能把单片机内部数据总线拉出来控制,解决办法,设计通信协议,在单片机和外部模块连接少量的几根线,实现单片机读写外部模块寄存器的功能,利用数据包,是基于对话形式完成的通信,整个过程不需要同时发送或者接收,导致始终存在一条数据线处于空闲状态,浪费资源,i2c,删掉一根线,把全双工变成半双工,增加应答机制,每发送一个字节,对方都要给我应答,同时一根线上能接多个模块,单片机可以指定和任意一个模块通信,同时在通信时候,其他模块不能对正常的通信进行干扰,把协议改成同步,加一条时钟线指导对方读写,存在时钟线,对时间要求不像异步通信那么高,单片机也可以随时暂停传输,去处理其他事情(暂停同时,时钟线也暂停了,双方能定格在暂停的时刻,过一段时间继续,不会对传输造成影响,降低单片机对硬件电路的依赖)就是一种设计方案)

在单片机和外部模块连接少量的几根线,实现单片机读写外部模块寄存器的功能,实现指定位置读写,实现了读写,就实现了对外挂模块的完全控制

两根通信线:SCL(Serial Clock)串行时钟线、SDA(Serial Data)串行通信线(一根线用来发送和接收)

同步,半双工

带数据应答

支持总线挂在多设备(一主多从(单片机作为主机,主导i2c总线的运行,所有外部模块都是从机,只有被主机点名后才控制i2c总线,未经允许,会发生冲突(类比老师上课))、多主多从(总线上任何一个模块都可以主动跳出来当主机,)

硬件规定,电路然后连接,端口输入输出模式是什么样子,软件规定,时序如何定义,字节如何传输,高位先行还是低位先行,时序由哪些部分构成。

主机权力很大,任何时候都是主机完全掌控scl线,空闲状态下,主机可以主动发起对sda的控制

只有在从机发送和接收数据时候,主机才会转交sda控制器给从机

从机可以是姿态传感器,oled,存储器,时钟模块等,从机权力小,对于scl线任何时候只能被动的读取,从机不允许控制scl线,对于sda,不允许主动发起对sda的控制,只有主机读取从机或者从机应答的时候,从机才能短暂的获取sda的控制权。

从机scl和sda连在主机拉出的线

I2c时序基本单元

空闲状态时候,scl和sda都处于高电平,无设备,由外挂上拉电阻拉高到高电平

开始,打破总线状态

起始条件:SCL高电平期间,SDA从高电平切换到低电平,从机捕获到scl高电平,sda下降沿信号时候,进行复位,等待主机使用,sda下降沿完成后,主机scl拽下来,拽下来,一方面是占用总线,另一方面方便基本单元的拼接,保证除了开始和结束,每个时序单元scl都是低电平开始,低电平结束

终止条件:SCL高电平期间,SDA从低电平切换到高电平,即scl放手到高电平,sda再放手,会到高电平,产生上升沿,触发终止条件,之后scl和sda都是高电平,回归初始状态

从机不允许产生起始和终止,所以在总线空闲状态,从机必须双手放开,不允许碰总线

Sda线,由于是半双工,主机发送时候输出,接收时候输入,从机sda也相同,如果总线时序每协调好,可能会导致电源短路,所以采用开漏输出,禁止所以设备强上拉输出,导致所以设备只能输出低电平而不能输出高电平,避免引脚浮空,在总线外面添加上拉电阻

开漏加弱上拉,避免短路和引脚的频繁切换,使得引脚同时具有输入和输出功能(类比拉杠子scl和sda是杠子,为了防止有人向上推杠子,有人向下拉杠子,规定所有人不能推杠子,只能往下拉或者放手,然后外加弹簧向上拉,低电平向下拉,弹簧打不过,输出高电平,放手,弱上拉的高电平,但是不影响数据传输造成冲突。想要输出,拉杆子或放手,控制杠子变化,像输入,直接放手,观察杠子高低,开漏模式下,输出高电平相当于断开引脚,输入之前可以直接输出高电平。)

线与,只要有一个或多个设备输出低电平就为低电平,所有为高,才是高电平,利用模式,执行多主机下的时钟同步和总线仲裁

发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次即可发生一个字节,主机拉低SCL,把数据放在SDA上,主机松开SCL,从机读取SDA的数据(高位先行)在SCL低电平期间,主机如果想要发送0,就拉低SDA到低电平,如果想要发送1,就放手,SDA回弹到高电平,在SCL低电平期间允许改变SDA的电平,当这一位放好后,主机就松手时钟线,SCL回弹到高电平,在高电平期间是从机读取SDA(主机发送)的时候,SCL高电平期间,SDA不允许变化,SDA处于高电平时从机需要尽快读取SDA,一般是在上升沿的时刻,从机已经读取完成了,主机在放手SCL一段时间后,就可以继续拉低SCL传输下一位,主机需要在SCL下降沿之后尽快把数据放在SDA上,主机有时钟的主导权,不需要着急,只需要在低电平的任意时刻把数据放在SDA上就行了,数据放完之后,主机再松手SCL,SCL高电平从机读取这一位,在SCL的同步下,依次进行主机发送和从机接收,循环8次就发送了8位数据,也就是一个字节。

拉低scl,放数据在sda,松开scl,从机读取sda

时钟线进行同步,主机字节发送一半,突然进入中断,不操作scl和sda,时序在中断位置不断拉长

Scl和sda电平暂停变化,传输暂停,中断结束后,主机回来继续操作,传输不会出问题,同步时序的好处,发送单元中,scl和sda都有主机掌控,从机只能被动读取

接收一个字节:SCL低电平期间,从机写入数据到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机再接收之前,需要释放SDA,释放SDA就相当于切换为输入模式或可以这样理解:所以设备都始终处于输入模式,发送时候主动拉低,被动接收时候,先释放sda,不去董,以免影响别人发送(原因,总线是线与,任何设备拉低,总线就是低电平,接收的时候还拽着sda不放手,别人无论发什么数据,总线都是低电平,无法发送,所以要释放sda)。

接收发送非常相似,区别发送是scl低电平期间主机给sda写数据,scl高电平中间时候从机读sda数据,

接收是scl低电平从机给sda写,scl高电平主机读sda(主机在接收之前,需要释放SDA)释放完后,从机取得sda控制权,从机要发送1,放手,sda回到高电平

可以当成发送一位和接收一位,这一位作为应答,发送接收都是在主机视角上考虑的

发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

理解:主机发送一个字节,然后问有没有人收到,主机要把sda放手,有人收到,把sda拽下来,主机高电平读取数据,发现有人给他拽下来,说明有人收到,如果主机发现松手后,sda回弹,没人回应

接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA,把权力给从机)应答后从机放开sda

从机发送数据,得到主机应答,从机接着发送,如果从机没得到主机应答,从机认为主机不想要,从机释放sda,交出sda控制权,防止干扰后续操作

主机在起始条件之后,要先发送一个字节叫一下从机名字,所有从机都会收到第一个字节,与自己的名字(地址)比较,如果一样,相对应的从机就会响应主机的读写操作,在同一条I2C总线里,挂在的每个设别地址必须不一样,防止主机叫一个地址有多个设备都响应。

从机设备地址,在I2C协议标准里分为7位地址和10位地址,相当于每个设备的名字

每个I2C设厂时,厂商都会为它分配一个7位的地址、芯片手册

MPU6050的地址是:1101 000

一般不同型号的设备地址都是不同的,相同型号的设备地址都是相同的

如果相同型号的设备挂在在同一条总线上,可以利用设备的地址的可变部分,一般器件地址的最后几位是可以在电路中改变的,例如MPU6050地址的最后一位,由板子上的AD0引脚确定,AD0引脚接低电平,那它的地址就是1101 000,AD0引脚接高电平那它的地址就是1101 001,AT24C02地址的最后三位都可以分别由这个板子上的A0、A1、A2引脚确定

指定地址写,都是对从机的操作,主机输出字节

对于指定设备(Slave Address从机地址),在指定地址(Reg Address寄存器地址)下写入数据(Data)

空闲状态下两个总线都是高电平,主机需要给从机写入数据的时候,在SCL高电平期间,拉低SDA,产生起始条件,在起始条件之后紧跟的时序,必须是发送一个字节的时序,字节的内容必须是从机地址+读写位,(从机地址是7位,读写位是1位加起来就是1个字节8位)(发送从机地址:确定通信的对象),(发送读写位:确认接下来是要写入还是读出,0:写入,1:读出),紧跟着的单元是接收从机的应答位(Receive Ack,RA),这个时刻主机需要释放SDA,如果从机应答,从机会立即拉低SDA,应答位产生后,从机释放SDA,从机交出SDA的控制权,同样的时序再来一遍,第二个字节数据就会送入指定数据的内部,一般第二个字节是寄存器地址或者是指令控制字,第三个字节是想要往寄存器地址中写入的值,如果主机不想发送数据了,要产生停止条件,在产生停止条件之前,先拉低SDA,会后续的上升沿做准备,然后释放SCL,再释放SDA,产生SCL高电平期间SDA的上升沿

此数据帧的作用是:对于从机地址为1101000的设备在其内部0x19地址的寄存器中,写入0xAA这个数据

当前地址读,主机接收从机

对于不指定设别(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)

在SCL高电平期间,拉低SDA,产生起始条件,主机首先发送一个字节,来进行从机的寻址和读写标志位,图示波形代表,本次寻址的目标是1101000的设备,读写标志为1,表示主机接下来想要读取设备,发送一个字节后,接收从机应答位,代表从机收到了第一个字节,把SDA的控制权交给从机,主机调用接收一个字节的时序,进行接收操作,从机接收到了主机的允许,可以在SCL低电平期间写入SDA,主机在SCL高电平期间读取SDA,主机在SCL高电平期间依次读取8位,就接收到了从机发送的一个字节数据0000 1111也就是(0x0F),没有指定地址这个环节(主机进行寻址时,一旦读写标志位给1,下一个字节立马变成读的时序,来不及指定),0x0F,(在从机中所有寄存器被分配到了一个线性区域中,会有个单独的指针变量,指示着其中一个寄存器,这个指针上电一般默认0地址,每写入一个字节或者读出一个字节后,这个指针就是自动自增一次,移动到下一个位置),从机返回的是当前指针指向的寄存器的值

指定地址读

对于指定设备(Slave Address),在指定地址(Reg Address)下读取从机数据(Data)

(复合格式),把写数据部分去掉,把前面一段设置地址,还没有指定写什么的时序追加到当前地址读时序前面,得到指定地址读的时序。前面是指定地址写,只指定了地址,没来得及写,后面是当前地址读,写了地址,在读,加起来就变成指定地址读了

指定从机地址是1101000 读写标志位是0,代表要进行写的操作,经过从机应答后,在发送一个字节第二个字节0001 1001,用来指定地址,这个数据就写入到从机的地址指针里了,从机接收到这个地址后,它的寄存器指针就指向了0x19这个位置,不给从机发要写入的数据,而是再来个起始条件,起始条件后,重新寻址并且指定读写标志位,此时读写标志位为1代表开始读,继续主机接收一个字节,这个字节数据就是0x19地址下的数据。

先开始,写入地址,停止,写入地址回存在地址指针里面,地址不会因为时序的停止而消失,在开始,读当前位置,停止。官方规定是一整个数据针,开始,重复开始,停止,两条拼接成一条

上面介绍的都是对一个字节的操作

写多个字节:最后一部分重复几次,发送一个字节和接收应答,第一个数据就写入0x19的位置(写入一个地址后地址指针会自动+1,变成0x1A)第二个数据就会写到0x1A的位置,第三个数据写入的是0x1B的位置,时序变成在指定位置写,按顺序写入多个字节

读也是,读地址指针也会自增,就可以连续读出一片区域的寄存器,读完后,要给从机发个非应答

,应答时候不把sda拉低,从机读到sda为1,知道主机没应答,从机收到非应答,就知道主机不想要继续,从机释放总线,把sda控制权交还给主机,给应答的话从机回发送下一个数据,想停止,被sda拽住,不能变成高电平。

总结:给应答从机发,给非应答,从机不发

硬件连接线路,软件操作线路,以此来实现指定地址写寄存器和指定地址读寄存器,主机就能完全掌控外挂模块,这就是i2c协议的内容,即使外挂芯片寄存器不在stm中,仍然可以通过通信协议,实现读写外挂芯片寄存器的功能

产品说明书,了解芯片原理,参数和硬件电路

可以分别测量xyz

(通过数据融合得到姿态角)欧拉角:飞机机身相对于初始XYZ轴的夹角,反应了飞机的姿态,

飞机头下倾或者上仰,轴夹角叫做俯仰,左右翻滚,轴夹角叫做滚转,机身水平,机头像右或者左转向,夹角叫做偏航,欧拉角表达了飞机此时的姿态,单个传感器不能得到准确的欧拉角,只有进行数据融合,综合多种传感器,取长补短,才能得到精确稳定的欧拉角

获得欧拉角需要多个数据,常用的数据融合算法:互补滤波、卡尔曼滤波等

加速度计本质是弹簧计,测量加速度a,找单位质量物体

正方体六个面所测的力就是加速度,例如只有芯片放在地球上,只有底面受到压力所以,xy输出为0,z输出1个g,自由落体,所有面不受力,xyz为0,但是加速的时候夹角改变

加速度计有静态稳定性,不具备动态稳定性

角动量守恒,保持旋转轴方向不变,外部动,内部旋转轴不动,平衡环连接处产生角度偏差,连接处放电位器,测量电压可以得到角度,mpu6050测量的是角速度,绕xyz旋转的角速度,角速度积分为角度,陀螺仪有动态稳定性,不具有静态稳定性,两种互补,进行动态滤波

0x68或者《1位变成0xd0融入读写位的从机地址

Xcl,扩展脚,当六轴不够用时候,MPU6050 XCL和SDA是扩展使用,通常是外接磁力计或者气压计,接上后,主机接口可以直接访问扩展芯片数据,把扩展芯片数据读取到mpu6050上

Dmp进行数据融合和姿态解算

主机从机执行逻辑不同,一般只关注主机

软件i2c只用gpio的读写即可,不用看库函数

第一步,scl,sda初始化为开漏输出 第二部,scl和sda置高电平

开漏输出仍然可以输入,输入时候,先输出1(释放sda,松手),输出0,读取都是0,外部为一被线与变成0,然后读取输入数据寄存器就行了

高电平空闲状态

然后完成i2c的六个基本单元

开始,先确保scl和sda释放,先拉低sda,再拉低scl

手动反转来完成高低电平,做个定义来简便,端口号替换名字

用宏定义,可以把整个函数换一个名字

宏定义再套函数,容易理解,方便加软件延时

先释放sda,再释放scl,然后在拉低,兼容起始条件和重复起始条件

为了确保结束时候释放sda能产生上升沿,时序单元开始时,先拉低sda,在释放scl和sda

除了终止条件scl是高电平,所有单元都会保证scl以低电平结束,方便个个单元的拼接(scl低电平,主机写入数据给sda,sda变换数据,从机读取sda,scl高电平,保证数据稳定,sda不允许变化

BITACTION把数据转换成非0即1

函数本身自带时间,拉高,从机读取

Scl低电平变化数据,高电平读写数据(数据不变),读写分离的操作,低电平定义为写的时间,高电平定义为读的时间

如果scl高电平期间,非要动sda,破坏游戏规则,这个条件就是起始和终止条件,起始和终止与数据传输过程不同,scl高电平的时候,sda变化。且一开始sda就是高电平,处于空闲状态(主机释放sda)

发送和接收应答是发送接收字节的简话,一部分

低电平时候主机将应答放到sda上,高电平从机读取应答

Scl默认低电平,接收应答,函数进来是scl低电平,主机释放sda,防止干扰,同时从机把应答位放到sda上,scl高电平,,主机读取,scl低电平,进入下一个时序单元

可能会认为主机释放sda,然后读取sda,读取为1,没意义

解释:i2c引脚是开漏输出加弱上拉,主机(代码)输出1,并不是强制sda为高电平,而是释放sda,i2c在通信,主机释放sda,从机也会起作用(而不是在旁边看戏),从机如果在的话,有义务把sda拉低,故即使主机把sda释放,再读取,读到的值也可能是0,读到0有从机,读到1,无从机

I2c是在通信,通信是有从机的,主机不断驱动scl时钟时,从机有义务去改变sda的电平,主机每次循环读取sda,读取到的数据是从机控制的

只有自己写自己读,还要通信干什么,要明白我们在通信,通信是有时序的,有些引脚电平,之前读和之后读,读的值不同

协议规定,开始后,主机必须发送一个字节,内容是从机地址+读写选择

发送后,从机接收应答(看看从机有没有接收)位,看看从机有没有接收到数据

接收应答后,继续发送一个字节,用来写入寄存器地址(mpu6050默认是睡眠模式),地址存在mpu6050当前地址指针中,用于指定具体读写哪个寄存器

再发送一个字节,指定我要写入寄存器地址下的数据

要写多个字节,for循环多执行第三次操作

发送一个字节,要进行接收应答

建好时序,建外设模块(实现指定地址读写,当前地址读)

模块建立在i2c通信上,包含底层函数,类似继承

指定地址写对应模块寄存器,先拼装时序

指定地址读对应模块寄存器,前面部分和指定地址写一样,用来从机地址+读写选择

发送后,从机接收应答(看看从机有没有接收)位,看看从机有没有接收到数据

接收应答后,继续发送一个字节,用来写入寄存器地址(mpu6050默认是睡眠模式),地址存在mpu6050当前地址指针中,用于指定具体读写哪个寄存器

(即设置mpu6050当前地址指针)然后转入读的时序,重新指定读写位

即重新开始,写地址|0x01,接收应答后,sda控制权交给从机,从机发送字节,主机接收字节,然后给从机发送应答。0给应答,1不给应答(主机收回sda控制权),要继续读就发送应答

读写寄存器,寄存器也是存储器,只不过存储器只能读和写,数据无实际意义,而寄存器的每一位数据都对应了硬件电路的状态,寄存器和硬件外设电路是可以互动的,可以通过寄存器来控制电路

用指针进行变量的地址传递,多返回值

结构体,变量打包,统一进行传递

Mpu6050当初小球放在正方体里面测量受力

最底层i2c协议程(引脚怎么配置,时序什么时候高电平,什么时候低电平-》驱动层(如何读写,配置寄存器,如何读取数据)驱动相关--》主函数应用层

通信协议时序很重要,理解时序,可以按照规定,反转通信引脚的高低电平,反转产生的时序波形满足了规定,通信双方就能理解并解析这个波形,实现通信

硬件用i2c(软件灵活,硬件不太行)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/434713
推荐阅读
相关标签
  

闽ICP备14008679号