个人成就
- 发布了53篇内容
- 获得了1次赞同
- 获得了2次收藏
个人简介
擅长领域
暂时没有设置哦~
-
抛开CRC校验的“神秘面纱”
大家好!我是张飞实战电子黄忠老师!今天给大家抛开CRC校验的“神秘面纱”!
模2运算是一种二进制算法,CRC校验技术中的核心部分。与四则运算相同,模2运算也包括模2加法、模2减法、模2乘法、模2除法四种运算。
与四则运算不同的是模2运算不考虑进位和借位,模2运算是编码理论中多项式运算的基础。下面我们就来看一看什么是模2运算。
模2运算使用与四则运算相同的运算符,即“+”表示模2加,“-”表示模2减,“×”或“·”表示模2乘,“÷”或“/”表示模2除。与四则运算不同的是模2运算不考虑进位和借位,即模2加法是不带进位的二进制加法运算,模2减法是不带借位的二进制减法运算。这样,两个二进制位相运算时,这两个位的值就能确定运算结果,不受前一次运算的影响,也不对下一次造成影响。
“模2加法”就是0和1之间的加法,其中0+0 =0,1+0 =0+1 =1,1+1=0。这种运算是比较常用的,并不神秘。对于任意多个数a1,a2,…,an(每个都是0或1),可以把它们做模2加法a1+a2+…+an。当这n个数中有奇数个1时,结果为1,否则结果为0。例如0101+0011=0110,列竖式计算:
模2减法是一种不考虑借位的减法,其定义如下:0-0=0,1-1=0,1-0=1,0-1=1。同样,第四式代表了模2减法的特征。在多位模减法中,每位都按上述定义进行运算,不考虑借位问题。根据上面减法可以得出一个结论:奇数个1相减得1,偶数个1相减得0。例如0110-0011=0101,列竖式计算:
一位数的模2乘法定义如下:0*0=0,0*1=0,1*0=0,1*1=1,多位数的模2乘法与普通乘法一样演算,区别是,部分积相加时按模2加,即奇数个1相加得1,偶数个1相加得0。例如1011×101=100111,列竖式计算:
模2除法是模2乘法的逆运算,定义如下:0÷1=0,1÷1=1,类似于普通的多位二进制除法,但是在如何确定商的问题上两者采用不同的规则。普通多位二进制除法按带借位的二进制减法,根据余数减除数够减与否确定商1还是商0,若够减则商1,否则商0。多位模2除法采用模2减法,不带借位的二进制减法,因此考虑余数够不够减除数是没有意义的。实际上,在CRC运算中,总能保证除数的首位为1,则模2除法运算的商是由余数首位与除数首位的模2除法运算结果确定。因为除数首位总是1,按照模2 除法运算法则,那么余数首位是1就商1,是0就商0。例如1011÷101=110101...001,列竖式计算:
模2算术是编码理论中多项式运算的基础。大家一定要掌握哦,下篇文章我们就一起看看它到底在CRC中是如何发挥作用的。
-
讲解ADC模数转白话
首先我们来简单介绍一下ADC,ADC即AnalogToDigitalConverter,简单来讲就是它可以把模拟量转化为数字量,方便程序去处理。
下面我们来看一下ADC的框图,我们从框图上来介绍ADC采样的原理
1. 外部被采样信号从单片机特定的引脚输入ADC模块,具体信号从哪个采样引脚输入,取决于‘通道选择’配置。
2. 当正常开始采样后,被采样信号在规定的时间(即采样时间)内对图中的电容(即采样保持电容,简称采保电容)充电,当充完电之后会控制图中的开关断开,这个过程就像科学家提取了一份标本,拿回实验室研究一样。
3. 当开关断开之后,图中的转换模块会花费一定的时间(即转换时间)对电容中的存的电压进行转换,并把转换的结果存入缓冲器,供程序员读取使用,同理,就像科学家对提取的标本花时间研究,得出一个结果。
以上就是整个的ADC工作过程,虽然草草几段话了解,但是其中不乏很多关键的细节问题,什么细节问题呢,很多老铁会想,这不都是自动的嘛,有嘛要注意的,那么我们先来看一下这样几个问题。
1. 采样时间设置多久?
2. 采样时间是越短越好还是越长越好?
3. 采样时间设置的大小和被采样信号的源内阻有什么关系?
4. 如何从有干扰的信号中提取有用的采样信号,实现正确采样?
5. 采样的结果和参考电压有什么关系?
现在我们就上面几个问题,展开说明:
从上面的描述可以得到:从采样到转换完成中间有2个时间:采样时间+转换时间(转换时间是固定的,采样时间可以设置),这两个时间决定了ADC采样的速度问题,当需要高速AD采样的时候,这两个时间尤为重要,因为它决定了采样速度。
我们再回头来想,这里是被采样信号先对采保电容充电,因为转换时间是固定的,我们能配置的就只有采样时间了,是不是说采样时间我们配置的越小越好呢?答案是:不是的,最起码得保证采保电容电压和被采样信号的电压非常接近吧,如果一味的小,追求速度快,那采保电容充不满,转换出来的数据也不对。
那就带来了一个问题,即这个被采样信号源内阻的问题,说白了就是对这个采保电容充电的电流够不够大,只有充电的电流大了,才能在最短的时间内充满。如果充电的电流非常小,恰恰配置的采样时间又很小,结果只有一个,采样不准确,我们经常看到,有的设计方案,做电压采样,分压电阻设计的阻值非常大,当这样的大阻抗遇到高速采样,那就有可能会不准确了
大家看上面的R1、R2设置大了,那么充电电流就小了,那么R1、R2的阻值设计小了,充电电流大了,那么带来的问题功耗就大了,当需要高速采样的时候,就需要注意这个阻值分配的问题,对功耗要求比较高的产品,如可穿戴产品,蓝牙耳机,智能手表等,这个就需要大的阻值了,一般情况下功耗要求不高的场合,我们基本上设计在1-2mA,所以从此可以得出一个结论,采样时间不是越快越好,也不是越慢越好,恰到好处即可,需要你去实际调试。
如何从带有干扰杂波的信号中提取到有用的信号呢,一般情况下,我们会根据系统的特性进行定点采样,即避开干扰区来采样提取有用的信号,再配软件或者硬件滤波手段来提取有用信号。
采保电容得到了准确的采样信号,就一定意味着转换结果准确吗?答案肯定
是NO,这个和参考电压也有关系,参考电压就像一标准样品一样,举例来讲:老板说照着这个样品给我做100个,那首先前提是你的样品得准确,如果样品都不准确,那做出来的东西也必然会有偏差。
想必通过上面的描述我们已经对ADC采样有了一定的理解认知,上面的描述只是ADC的一部分要点,介于篇幅原因,这里就不再赘述了,文末留个大家几个问题:大家看图中绿色地方加的电容,这个电容加了好还是不加好?加多大好?在布板的时候这个电容应该摆放在哪里比较好?
-
STM32中断如此简单
大家好,我是张飞实战电子黄忠老师,下面我们先来了解一些基本概念:
中断:中断是什么?举个例子来说,当我们正在工作时,突然电话响了,这时你会把手里的工作先停下来,然后去接电话,当接完电话后,电话里的人安排你马上做一件事,这时你需要立刻去做这件事,当把这件事做完后你会继续之前被打断的工作,这个过程为一次中断。
异常:一个系统本应该正常的运行,但由于某些条件使系统产生了错误,就会使系统运行不正常,我们称之为异常。就好比一个健康的人,如果身体某个器官出现了问题,那他将会生病,不能像以前那样健康生活,称他的身体出现了异常。系统出现异常,我们必须对异常做出处理,才能让系统正常运行。
事件:比如一个老师在教室里给学生上课,下面的学生会做出各种不同的动作,如有认真记笔记的,有讲小话的,有翻自己书包的等等,我们把学生的这些行为称为事件。但老师对这些事件有些是不会有动作的,有些事件是需要老师干预的,比如两个学生讲话,影响了老师上课,老师需要警告讲话的学生,然后再继续上课。
优先级:当我们接到了两个电话,两个电话都安排你去做别的事,这时你需要先完成比较急的事,然后再完成不是太急的事,这就是优先级的问题。当有多个中断时,我们需要根据中断优先级判断先响应优先级高的中断,然后再响应优先级低的中断。
中断与事件的联系与区别:有些事件需要响应,称这个事件为可中断事件,但有些事件不需要做出响应称这些事件为不可中断事件。当硬件正常连接时,对应事件会自动产生,但中断则需软件配置相应的中断使能位。
抢占式优先级和响应优先级:所谓抢占式优先级和响应优先级,具有高抢占式优先级的中断可以在低抢占式优先级中断处理过程中被响应,即中断嵌套。当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后 才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。每一个中断源都必须定义2个优先级。
STM32的中断管理利用了NVIC(Nested Vectored Interrupt Controller)嵌套向量中断控制器,它把所有的外设中断和系统异常用一张向量表来管理,每个系统异常和外设中断都被分配相应的地址,除了一些系统异常的优先级不能改变外,其余的系统异常和中断的优先级都可变化。具体向量表部分截图如下图所示:
其中包含10个系统异常,有82个外部中断地址,其中有11个被保留,没有使用,从上表可查出对应的默认优先级和地址分配情况。
NVIC是嵌套向量中断控制器,它控制芯片所有中断功能,是Cortex-Mx内核里的一个外设下图为Cortex-M4内核NVIC寄存器的分布图:
从上图可以看出,Cortex-M4内核NVIC嵌套向量中断控制器总的有7个类型的寄存器,其中有1个控制器类型的寄存器,8个中断使能寄存器,8个中断失能寄存器,8个中断挂起设定/清除寄存器,8个中断有效位寄存器,60个中断优先级寄存器。而ST对CM4内核NVIC控制器寄存器做了一定缩减,其寄存器分布图如下:
在中断编程时,我们一般使用ST提供的固件库,对中断配置在程序编写时我们一般就使用中断使能、中断失能、中断优先级设定三个寄存器。
NVIC嵌套向量中断控制器里有一个用于管理中断优先级的寄存器NVIC_IPRx(x=0,1,20),其数据位宽度为8bit,如果8位全使用,则可配置的优先级为0-255,数值小的优先级越高,但在STM32F373中,只是使用了高4位,可配置的优先级为0-15。
这4位又被分为抢占优先级和响应优先级。对这4位又有5种搭配方式,定义抢占优先级和响应优先级的位数,其分组由内核外设SCB模块的应用程序中断及复位控制寄存器AIRCR的bit8-bit10(PRIGROUP[0:2])三位决定,分组可用下图所示:
ST官方已经把设定优先级分组封装成了一个库函数,我们设置优先级分组时,可直接调用相关库函数即可,中断库函数可在官方库文件misc.c和misc.h中找到,其优先级分组设定函数如下图所示:
如我们想设置0位响应优先级、4位抢占优先级即优先级分组0,其函数可写为:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
那我们仅仅是配置了优先级就够了吗?我们还需要配置通道,即配置中断源,按照前面中断向量表中的Position中断编号来查询即可配置即可。配置了中断源后,我们还需对中断源使能中断,ST官方把通道配置、优先级设定、通道使能定义为了一个结构体,其定义如下:
下面我们以USART1为例,配置中断优先级分组为3,即3位响应优先级,1位抢占优先级,抢占优先级级数为0,响应优先级级数为1,对其编写程序如下:
Void NVIC_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3); //配置优先级分组
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn; //USART1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为最高 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为 1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
NVIC_Init(&NVIC_InitStructure); //将结构体成员的值写入对应的寄存器
}
至此,中断篇讲解完毕,更多详细的NVIC的介绍,可查阅官方技术手册NVIC部分。
-
指针和数组的恩恩怨怨
指针和数组有没有关系呢?到底有什么关系,今天我们就来好好的看一看。
指针就是指针,指针变量在32位系统下,永远占4字节,其值为某一个内存的地址。指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到呢?
数组就是数组,其大小与元素的类型和个数有关;定义数组时必须指定其元素的类型和个数;数组可以存任何类型的数据,但不能存函数。
既然它们之间没有任何关系,那为何很多人经常把数组和指针混淆,甚至很多人认为指针和数组是一样的呢?我们先来看下吧。
1、以指针的形式访问和以下标的形式访问
下面我们就详细讨论讨论它们之间似是而非的一些特点。例如,函数内部有如下定义:(A)char *p = “abcdef”;
(B)char a[ ] = “abcdef”;
①以指针的形式访问指针和以下标的形式访问指针
以指针的形式:*(p+4)
以下标的形式:p[4]
这里的4 是偏移量,都是先取出p里存储的地址值,加上偏移量,计算出新的地址,然后从新的地址中取出值。那么上面形式不同,访问的本质是一样的。
②以指针的形式访问数组和以下标的形式访问数组
以指针形式:*(a+4)
以下标形式:a[4]
我们都知道数组名代表数组首元素的首地址,加上4个元素的偏移量,得到新的地址,然后取出新地址上的值。
由此得出指针和数组都是可以“以指针的形式”或“以下标的形式”进行访问,但是是完全不一样的东西。
还有需要注意的是这个偏移量代表的是元素,而不是字节,偏移元素的个数再计算新的地址取值。
2、a 和 &a 的区别
先来举个例子:
int a[5] = {1,2,3,4,5},b,c;
int *prt = (int *)(&a + 1);
b = *(a+1);
c = *(ptr-1);
对指针进行加1操作,得到的是下一个元素的地址,而不是原有地址值直接加1,所以一个类型为T的指针的移动,以sizeof(T)为移动单位。因此,对上面例子来说,a是一个一维数组,数组中有5个元素,所以&a+1是取数组a的首地址,该地址的值加上sizeof(a)的值,也就是&a+5*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。
那么*(a+1):a和&a的值是一样的,但是意思不一样,a是数组首元素的地址,也就是a[0]的首地址,&a是数组的首地址,a+1是数组下一个元素的首地址,也就是a[1]的首地址,&a+1是下一个数组的首地址,所以b的值应该是输出2,*(ptr-1),因为前面我们分析ptr是指向a[5]的,并且ptr是int*类型,所以*(ptr-1)是指向a[4],输出为5。
由此我们可以得知数组名a代表的是数组首元素的首地址,而不是数组的首地址,&a才是整个数组的首地址。
指针和数组你能完全理清楚了吗?
-
USB之特殊包
前面文章我们说了令牌包、数据包、握手包,今天我们来看最后一个特殊包,本篇文章主要说明特殊包以及如何处理数据包,下面们开说.
特殊包是一些在特殊场合使用的包。总共有4种:pE、r、 SPLIT和PNG其中PRE、 SPLIT、PING是令牌包,ERR是握手包。ERR、 SPLIT、PING三个是在USB2.0协议中新增的。
PRE是通知集线器打开其低速端口的一种前导包。PRE只使用在全速模式中。平时,为了防止全速信号使低速设备误动作,集线器是没有将全速信号传送给低速设备的。只有当收到PRE令牌包时,才打开其低速端口。PRE令牌包与握手包的结构一样,只有同步域、PID和EOP。当需要传送低速事务时,主机首先发送一个PRE令牌包(以全速模式发送)。对于全速设备,将会忽略这个令牌包。集线器在收到这个令牌包后,打开其连接了低速设备的端口。接着,主机就会以低速模式给低速设备发送令牌包、数据包等。
PING令牌包与OUT令牌包具有一样的结构,但是PING令牌包后并不发送数据,而是等待设备返回ACK或者NAK,以判断设备是否能够传送数据。在USB1.1中,是没有PING令牌包的。只有在USB.0高速环境中才会使用PING令牌包,它只被使用在批量传输和控制传输的输出事务中直接使用OUT令牌包发送数据时,不管设备是否有空间接收数据,都会在OUT令牌包之后跟着发送一个数据包,如果设备没有空间接收数据,就返回一个NAK这样的结果就是浪费了总线带宽,白白传送了数据。在高速设备中增加了这个PING机制,主机先用PING令牌包试试设备是否有空间接收数据,而不用事先把数据发送出去。在全速模式下,有时会遇到一个很有趣的现象,就是下位机程序慢了一点点处理完数据,结果传输速度却下降了很多。这就是前面所说的OUT过程直接发送数据导致的,也就是说,虽然程序只慢了一点,但是却丢弃了整个数据包。
接下来我们来说说如何处理数据包. SPLIT令牌包是高速事务分裂令牌包,通知集线器将高速数据包转化为全速或者低速数据包发送给其下面的端口。ERR握手包是在分裂事务中表示错误使用。由于高速分裂事务过程比较复杂,主要属于集线器的功能,在此就不详述了,感兴趣的读者可以阅读USB2.0协议相关部分。
这么多类型的包以及传输过程,那我们该怎么去处理呢?其实,如果使用现成的USB接口芯片,很多过程,USB接口芯片都已经处理好了,可以不用太关心这些细节,只要知道有这么一个过程就行了。
一般的USB接口芯片会完成如CRC校验、位填充、PD识别、数据包切换、握手等协议的处理。
当USB接口芯片正确接收到数据时,如果有空间保存,则它将数据保存并返回ACK,同时,设置一个标志表示已经正确接收到数据;如果没有空间保存数据,则自动会返回NAK。
收到输入请求时,如果有数据需要发送,发送数据,并等待接收ACK。只有当数据成功发送出去(即接收到应答信号AC)之后,它才设置标志,表示数据已成功发送;如果无数据需要发送,则它自动返回NAK。
通常只需要根据芯片提供的一些标志,准备要发送的数据到端点,或者从端点读取接收到的数据即可。所要发送和接收的数据是指数据包中的数据,至于同步域、包标识、地址、端点、CRC等是看不到的在 BUS Hound中抓到数据也是如此,仅是数据包;并且, BUS Hound中只能看到成功传输的数据,即只有ACK确认过的数据包。在USB接口芯片中,通过一些标志可以知道是哪个端点接收或者成功发送了数据。另外,由于控制传输比较特殊, SETUP包也会有相应的标志供我们使用。
至此为止我们的包结构和包分类就全部说完了,通过对这些包的了解,我们会更加清楚的认识USB的通信知识.对我们做USB通信有很大的帮助.希望通过文章的内容分享,能给大家带来收获.