发帖数

53

原创数

53

关注者

11

阅读数

13406

点赞数

1

黄忠

  • 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通信有很大的帮助.希望通过文章的内容分享,能给大家带来收获.


    收藏 0 回复 0 浏览 166
  • 指针和数组的恩恩怨怨

    指针和数组有没有关系呢?到底有什么关系,今天我们就来好好的看一看。

    指针就是指针,指针变量在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才是整个数组的首地址。

    指针和数组你能完全理清楚了吗?

     


    收藏 0 回复 0 浏览 162
  • 51单片机DIY抽奖-技术分享(四)

    软件设计

    延续前篇,继续分享我的DIY抽奖设计之软件部分,上一篇我介绍了我做这个小产品的PCB设计过程,然后现在就到了实物板调试的阶段了,只有把相应的程序实现了,这个产品才能真正实现自己的价值,实现的过程可能会比较曲折,但没有过程哪来的结果,没有实践哪来的经验,话说真的一点没错吧。

    这里我只能描述实现过程和碰到的一些小问题,具体实现过程以及程序源码细节我也会陆续更新出视频,感兴趣的可以关注下~自己实践下更好了。

    我们要完成一个程序设计的话需要先设计算法,然后再实现算法,实现就要用到计算机语言来表述了,不然单片机也不认不是。这里我说一个概念就是结构化程序的设计强调程序设计风格和程序结构的规范化,那么就提倡用清晰的结构,我们不管是新手还是老手,规范化还是很有必要的,对自己程序的逻辑关系的考验,或者是对阅读者都是很好的,以至于到后期的调试或者扩展和修改算法都会变得容易了。

    我要实现抽奖功能,首先不能只有大的想法就够了,既然要实现就要把每个细节的逻辑关系都要落实才行,否则就不会出现你想要的结果。

    那么接下来我从大的方向到细化的过程以及设计要点都分享下哈~

    1、程序主要实现的功能:

    image01.pngimage02.png


    简单清晰,主要的调试点在内部实现的逻辑关系上。后续精彩着呢,待更完~


    2、顺序往下吧,对于单片机设计,首先就是要初始化,对IO口和定时器等进行配置才能使得单片机正常工作,每款单片机对外设的配置都是不一样的,那么就要研读数据手册了,这是避不开的。先来看下我的初始化部分。

    image03.png


    既然前面提到了分模块,从初始化到实际处理都要分模块进行,这样思路清晰。比如第一个函数是对单片机的初始化,主要就是I2C通信的IO口部分,蜂鸣器的IO口部分,定时器的部分,那么初始化就是直接对外设寄存器的配置;后面的模块主要就是对当前模块的全局变量的初值配置了。全局变量我一般都是用结构体来实现,每个模块定义一个结构体,名称和用法一目了然,想扩展也方便。


    3、初始化了,只能说单片机可以用了,具体还要看需要的应用模块,这样单片机才能顺利完成你需要的使命。继续看下抽奖大的模块,就是显示和按键了,再有一个就是蜂鸣器模块。

    有了大的模块就可以细分,把具体功能先确定好,再去实现就方便多了,我觉得做程序一定要把模块做好,然后模块间的全局变量尽量通过结构体来实现,这样结构清晰,逻辑性好。

    这是我个人经验,当能把习惯养好的时候,一定不要随意,否则浪费时间不说,程序结果也会不稳定。



    4、那这里我就分享下我的小DIY的模块的逻辑实现,先说显示部分吧,首先我做的这个有两个按键,一个开始,一个停止。按键开始后我会把对应显示模式的变量配置好,对应不同的模式再去进行处理。

    image04.png


    接下来细化了,就是在不同模式下做些什么,先来看下我的流程图,然后我再针对设计细节把想法说一下。

    image05.png


    对于获取当前获奖者,我的处理方法是通过指针获取当前序号对应数组的值,同时把当前值从数组中删除,直到数组中数据全部被抽完为止。

    流水灯以及显示数值由慢变快是怎么做的呢,主要就是定义一个全局变量,作为时间参数配置一个大一点的初始值,定时时间到时间参数减值,直到减到一个很小的值,这就是一个渐变的过程。

    image06.png


    具体的实现过程可以关注我的视频,所有代码都会分享到。

    5、那么按键模块的话就是扫描和处理了,按键部分我当时选取数码管驱动芯片就很明确了,具体可以看下我第一篇给大家推荐的那款芯片,很实用啊。那么对于扫描不需要做过多的处理,只需要直接读取就可以了,这个就没什么好说的了。只是I2C相关的读写函数要仔细研读手册了。

    接下来看下我的处理,也是状态机模式,整个网络之间的关系通过结构体全局变量实现。逻辑关系清晰。

    image07.pngimage08.png

        框架出来了,就是具体逻辑关系了,有短按和长按,这个长按只针对停止键,短按就是常规抽奖模式的开始和停止了,那么停止键在短按情况下,要判断当前模式是转盘转动模式还是设置模式,如果是转动模式就是进入停止模式(显示当前号码,蜂鸣器唱歌),如果是设置模式(配置抽奖号码总数),号码加1;启动键短按的处理,也是要判断当前模式是停止模式还是设置模式,如果是停止模式则进入转动模式,如果是设置模式,号码减1;还有一个长按停止键了,当前模式是停止模式,长按则进入设置模式,如果当前是设置模式那么就进入停止模式。长按的处理就是进入和退出设置模式。按键处理就这些需要实现的功能。

    最后就是一个蜂鸣器的模块处理,由于蜂鸣器我做了唱歌的功能,涉及到频率问题,所以我放在中断中完成(也是经过多次调试)。蜂鸣器唱歌要分析谱子,调调,真的有点音乐人的感觉了,通过计算低音,中音,高音的频率先计算配置好,然后要有曲子的分析,查表配置周期(可以用定时器的时钟分频模式)实现唱歌。这里学问太深,有兴趣的可以深挖一下,还是通过蜂鸣器模式来进入相应的状态,比如一声响,两声响,唱歌。这个都是可以自由分配的。

       到这里抽奖程序设计主体思路就这样实现了,整个DIY的产品就完成了。下面就是成品啦。

    image09.png

           纵观看下,功能不难,其实实现起来真的没你想的那么简单,逻辑关系要确定好,手册要研读好。有兴趣的话可以自己动手实践下,每个实践过程都会有很大收获的,后续会分享我的程序实现的视频过程,会更细致些。每个项目不论大小都会经历不同的坎坷啊。当你遇到不同的问题的时候慢慢解决了才是经验的积累过程。这个抽奖DIY小产品就完成了,大家可以看下我的实现有没有需要改进的地方呢?随时欢迎交流啊,或者有更好玩的产品一起探讨实现啊,每个都有每个的特点,都能学到不一样的知识点。后面我会陆续做出更多的小产品,每一个都会有侧重点,或者可以给我启发或建议一起完成你的想法啊,感谢大家对我的持续关注,还希望能给出更多的意见和建议。















    收藏 0 回复 0 浏览 159
  • TTL通往RS232神奇之黑盒(一)

        大家好!我是张飞实战电子黄忠老师。

        做单片机通信的,这些不同通信接口转换的模块是必不可少的,都说这些模块那么多,又不贵,那我索性也来做一个,全隔离的TTL转RS232模块,在这里就跟大家分享下我的实现过程,还有需要注意的地方。如果大家有不同的看法随时欢迎交流哈~

    首先呢先来说下需要实现什么,就是TTL转RS232,那么就可以来选择芯片,有信号隔离芯片、电源隔离芯片、转换芯片,主要就是这些。

        那么接下来我就来详细分享下我的实现过程。

        1、输入端电压我设计的是5V供电,因为后端我选取的DCDC隔离电源需要5V供电,所以这里需要提示下的。输入进来加个保险丝(9V/100mA),起到保护作用。还有输入端我一般会选择加个LED灯,提示用的。

    图片1.png 

        2、信号输入后开始进行全隔离,信号通过隔离芯片实现,电源信号通过隔离电源,实现全隔离;信号线一般需要加个小电阻(10R够了)增强鲁棒性,对于隔离部分处理相对简单,看图吧

    图片2.png图片3.png 

        3、接下来就是转换芯片(SP3232EIM/TR)部分了,对于RS232的信号,我做了功课,这里简单说明下,RS232信号0和1与TTL不一样,TTL工作电压是0~3.3V,RS232逻辑1电平是低于-3V的(保证电平在-3~-15V);逻辑0电平是高于3V的(保证电平在3~15V)接通状态呢有效电平高于3V,断开状态电平低于-3V,也就是当传输电平绝对值大于3V时,电路可以有效的检查出来。

    通过阅读RS232转换芯片的数据手册,看下对于电荷泵电容的说法,

     

    图片4.png 

    图片5.png图片6.png 

    那么我的设计就参照手册的推荐设计,另外我选用的芯片可以实现两路转换,我只用了一路。对于有芯片的设计还是要多看手册。

    4、最后就是输出部分的端口了,DB9接口,这个也要查询下相关资料,这里我跟大家分享下我查询的结果还有我的端口设计。

    图片7.png 

    图片8.png图片9.png 

    图片10.png 

        最后需要注意的是:主控目标和隔离模块通讯才用 杜邦线连接,隔离模块和从控目标通讯采用带有DB9接口的线材连接,这里需要注意带有DB9接口的线分TX和RX交叉、不交叉两个版本,采购时切记分清楚。

        那么这个小模块的设计就完成了,是不是觉得其他我们平时买的黑盒内容也没想象中那么复杂,技术需要沉淀,不管做软件设计也好,硬件设计也好,真的都要去实际完成才会有不一样的收获。或者在我的设计基础上也可以有不同的见解,还可以有更好的升华。思路需要开阔,当有了丰富的经验,设计什么都会变得容易的多。更多的积累才会换来更大的财富。

        后面就开始设计PCB过程了,最后产品出来就可以通信使用了,如果大家感兴趣的话就持续关注我吧。后面会陆续分享出我的设计视频过程,随时欢迎大家跟我来探讨,也让我可以开阔下思路,或许还有其他实现方式。


    收藏 0 回复 0 浏览 148
  • 你知道“链接”吗

    在最开始人们编写程序时,都将所有的代码都写在同一个源文件中,经过长期的积累,程序可能包含了N多行的代码,程序员维护起来非常困难。迫切地希望将程序源代码分散到多个文件中,一个文件一个模块,能够更好地阅读和维护程序,这个时候,链接器就闪亮登场了。

    我们知道,数据是保存在存储器中的,对于单片机来说,必须知道这些数据的地址才能使用。变量名、函数名等仅仅是地址的一种代名词儿,旨在编程时更加方便地使用数据,当源文件被编译成可执行文件后,这些标识符都不存在了,它们都被替换成了数据的地址。

    任何程序的执行,最终都要依靠计算机硬件来完成,单片机是大规模集成电路,它只认识高低两个电平(电压),假设高电平为 3.3V,用1表示,低电平为 0V,用0表示。也就是说,在单片机底层,只有 0 和 1 两个二进制数字,这就是机器语言。

    使用机器语言编程,十分繁琐又耗时,并且很容易出错。如果程序包含了多个源文件,就很可能会有跨文件的跳转、在程序拥有多个模块时会导致更加严重的问题。于是大神们发明了汇编语言,这相比机器语言来说是个很大的进步。汇编语言使用接近人类的各种标号来帮助记忆,比如用jmp表示跳转指令,用func表示一个子程序(C语言中的函数就是一个子程序)的起始地址,标号的方法使得人们从具体的机器指令和二进制地址中解放出来。标号这个概念随着汇编语言的普及被广泛接受,它用来表示一个地址,这个地址可能是一段子程序的起始地址,也可以是一个变量的地址。

    随着软件规模的日渐庞大,代码量开始疯长,汇编语言的缺点逐渐暴露出来。汇编虽然提供了多种标号,但它依然非常接近计算机硬件,程序员要考虑很多细节问题和边界问题,而且不利于模块化开发,所以后来人们发明了C语言。C语言是比汇编更加高级的编程语言,极大地提高了开发效率,以加法为例,C语言只需要一条语句,汇编却需要四五条。

    单片机编程中,程序员通过会把很多功能分散到成许多个模块中。这些模块之间相互依赖又相互独立,原则上每个模块都可以单独开发、编译、测试,改变一个模块中的代码不需要编译整个程序。在程序被分隔成多个模块后,需要解决的一个重要问题是如何将这些模块组合成一个单一的可执行程序。在C语言中,模块之间的依赖关系主要有两种:一种是模块间的函数调用,另外一种是模块间的变量访问。函数调用需要知道函数的首地址,变量访问需要知道变量的地址,所以这两种方式可以归结为一种,那就是模块间的符号引用。这种通过符号将多个模块拼接为一个独立的可执行程序的过程就叫做链接(Linking)。
        在一个STM32项目中,代码被分为多个文件时,链接器可以链接ARM代码、Thumb代码、Thumb-2 代码,并自动生成交互操作中间代码,以便在需要时切换处理器状态。链接器还可以在需要时自动生成内联中间代码或长跳转中间代码,以扩展跳转指令的范围。

    链接器还可以生成关于链接文件的调试和引用信息、生成静态调用图并列出堆栈的使用情况、控制输出映像中符号表的内容、显示输出中代码和数据的大小。链接器针对下一次文件编译提供反馈信息,提示编译器有关未使用函数的情况。 可以根据提示在后续编译中将未使用的函数放置在各自的节中,以便链接器将来删除这些函数。

    图片38.png 

    使用链接器构建可执行映像时,链接器将解析输入对象文件之间的符号引用,从库中提取对象模块来满足还未满足的符号引用的需要,根据属性和名称排序输入节,并将属性和名称相似的节合并为相邻块,删除未使用节,删除重复的公共组和公共代码、数据及调试节,根据提供的分组和布局信息将对象片段组织为内存区,给可重定位值分配地址,最终生成可执行映像。


    收藏 0 回复 0 浏览 146
×
黄忠