发帖数

43

原创数

43

关注者

17

阅读数

9875

点赞数

2

张角

  • if和for等基本语句结构

    大家好,我是张飞实战电子张角老师!

    同学们好,我们从今天开始探讨单片机C语言,我们首先从iffor等基本语句结构开始。

    iffor,这两个从本质上来说,是不是C语言的两个关键字呀。那么我们为什么又把它称之为C语言的基本结构的一部分呢。要回答这个问题,我们是不是首先要搞清楚C语言的基本结构是什么?

    实际上,任何结构化编程语言的基本结构都是相同的,也就是三种基本的程序结构:顺序,分支和循环。由这三者最基本的结构,可以搭建出任何我们想实现的程序结构。在狄杰斯特拉(Edsger W. Dijkstra)反复研究面条式代码(spaghetti code),并在1968年给某位编辑写了一篇著名的简信,题为《Go to语句是有害的》之后,计算机科学家Corrado BohmGiuseppe Jacopini证明,使用顺序(sequencing),分支/选择(alternation)和循环(iteration)这三种流程结构就足以表达所有程序的本质。C语言作为结构化编程语言的一种,其程序结构,自然也是由这三种最基本的程序结构组成。

    顺序执行程序,这个很好理解,一条语句接着一条语句执行就可以了。那么C语言的分支和循环是如何实现的呢?

    对于分支,我们常见的关键词是不是有if/elseswitch/case两种组合呀。if/else翻译过来,是不是就是“如果,否则”,是一个条件判断。如果用伪代码的方式来进行表达的话,一般有这两种形式。

    第一种形式如下:

    if(条件为真)

    {

    代码段1

    }

    else

    {

    代码段2

    }

    这里else的含义其实就是条件不为真,那么也就是条件为假。

    image.png

    第二种形式是这样的,

    if(条件1为真)

    {

    代码段1

    }

    else if(条件2为真)

    {

    代码段2

    }

    else

    {

    代码段3

    }

    image.png

    第一种形式和第二种形式本质上的区别,其实就是第一个是双分支,第二个是多分支。两种不同的分支,我们要根据具体情况去使用。某种意义上多分支模式可以由双分支演变而来,比如我们可以在双分支模式里面的else里嵌套一个双分支结构就可以了。

    if(条件1为真)

    {

    代码段1

    }

    else

    {

    if(条件2为真)

    {

    代码段2

    }

    else

    {

    代码段3

    }

    }

    从这个意义上看,双分支和多分支其实是一回事情,本质上都是分支。分支这个概念,大家应该都是相对容易接受的,任何一件事情总有它的对立面,高对低,胖对瘦,大对小,物质对暗物质等等。分支这个概念应该是反映了事物的一种本源的状态,是描述程序是不可再进行切分的维度,也就是说分支成为了任何程序的三种基本结构之一。

    其实对于多分支的情况,C语言有另外一套关键字组合switch/case,写成伪代码的形式,大概是这样的。


    switch(变量){

    case 常数1

    代码段1

    break

    case 常数2

    代码段2

    break

    case 常数3

    代码段3

    break

     

    。。。

     

    default

    代码段n

    break

    }

    大家注意没,对于switch/case组合来说,它的条件一定要是常量,而且要是整数。这个是不是对判断的条件作出了限制呀。这里的default关键字,是和if/else里面的else对应的,表示意外情况。从表面看,switch/case适用于逻辑条件简单,但是分类较多的情况;if/else适用于判断条件复杂,但是分支较少的情况。但是从另外一个层面看来,switch/case所具备的功能,if/else完成起来,完全没有问题呀。那为什么还要搞出来这一个关键字组合呢?

    我个人的理解是switch/case关键字的执行效率,在某些情况下,要比if/else要高。

    int a = 0;

     switch(a)
     {
     case -1:
        break;
     case -2:
        break;
     case -3:
        break;
     case -4:
        break;
     case 0:
        printf("I am in switch case!n");
        break;
     default:
      break;
     }

     printf("I am between the switch case and if/else if/else!n"); 
     


     if (-1 == a)
     {
     }
     else if (-2 == a)
     {
     }
     else if (-3 == a)
     {
     }
     else if (-4 == a)
     {
     }
     else if (-5 == a)
     {
     }
     else
     {
         printf("I am in if/else if/else!n"); 
     }

    比如对于上面的代码段,对于switch/case关键词对来说,程序是直接跳到case0的情况下的;但是对于if/else而言,程序则是一句一句比较之后才达到了else”这一句,程序执行效率的高低是显而易见的。

    但是我们说,switch/case的程序执行效率可以比较高,并不是没有条件的。从汇编语言的层面来看,switch/case是建立了一张跳转表,因此需要一定的空间才行。这里某种程度上有以空间换时间的意思。

    因此,如果程序可以使用switch/case尽量使用这个,以便提高它的执行效率。其实,我们这样比对一番之后,自己也就轻而易举地牢牢记住了它们,这个可能也就是知其然知其所以然的效果,符合人的记忆规律。

    讲完了分支,我们来看一下循环。循环这个基本结构,在C语言里面,一共有两种实现方式,for循环和while循环,其中while循环还可以分为两种,一种是while循环,一种是do/while循环。我们下面分别看一下,这三种结构的程序表达大概是什么模样。

    for(循环控制变量初始化;循环终止条件;循环控制变量增量){

    循环体;

    }

    for循环的执行步骤是:首先执行循环变量的初始化,然后执行循环终止条件;如果判断条件为假(不符合终止条件),那么就开始执行循环体;然后执行循环控制变量的增量程序,执行完以后,再去判断是不是符合循环终止条件;如何符合条件,那么就退出循环;如果不符合条件,那么就继续执行循环体,并重复执行上述步骤。

    感觉用第二种方式来描述这个循环体的执行过程,更为清晰。第一,先进行循环控制变量初始化;第二,执行循环终止条件,如果判断结果为假,则进入第三步;如果为假则循环终止并退出;第三,执行循环体;第四,执行循环控制变量增量,转入第二步;

    注意,其实for循环括号中的三部分其实都可以省略,如果全部省略了,就变成了一个无限循环的死循环,跳不出来了。无限循环在操作系统中使用的是非常多的,每一个任务都是一个无限循环体,包括main函数也是一个无限循环体。

    下面,我们来看一下while循环的代码格式。

    循环控制变量初始化;

    while(循环终止条件){

    循环体;

    循环控制变量增量;

    }

    while循环的执行步骤可以描述为如下的样子。

    第一在while之前,先执行循环控制变量的初始化;第二,判断循环终止条件是否成立,如果成立那么就跳出循环,如果不成立就进入第三步;第三步,执行循环体;第四步,执行循环控制变量增量,然后进入第二步。

    do/while的代码格式是什么样子呢?

    循环控制变量初始化;

    do{

    循环体;

    循环控制变量增量;

    }while(循环终止条件)

    while循环的执行步骤可以描述为如下的样子。第一,在do(也就是执行)之前,先执行循环控制变量的初始化;第二,先执行一次循环体和循环控制变量的增量;第三步,判断循环终止条件是否成立,如果成立那么就跳出循环,如果不成立就进入第二步;

    我们来看一下呀,whiledo/while主要的区别,一个是先判断再执行,一个是先执行再判断,那么也就是说do/while这个关键字组合中,函数体至少执行一次。但是这并不影响whiledo/while之间的转化呀,同样的功能绝大多数情况下,可以用while也可以使用do/while来实现。那么有没有必须要用do/while的时候呢?答案是有的,在linux编程中,do/while常用的一个方式是do while0)。

    比如,我需要定义一个宏:

     #define SAFE_FREE(p)  do{free(p); p=NULL}  while(0)

    假设这里去掉do{....} while(0),及定义为:

    #define SAFE_FREE(p)  free(p); p=NULL

    那么以下代码:

    If(NULL!=p)

       SAFE_FREE(p)

    else

       .......

    会被展开成如下的有仇

    If(NULL!=p)

       free(p); p=NULL 

    else

       .......

    展开之后,存在两个问题。因为if分支后面有两个语句,会导致else分支没有对应的if,编译失败。即使假设没有else分支,SAFE_FREE中的第二句,无论if测试是否通过,这个都会被执行。那么如何解决上述问题呢?有人说给SAFE_FREE的定义加上{}就可以了,比如如下的样子:

     

     

    #define SAFE_FREE(p)  { free(p); p=NULL; }

     

    代码展开如下:

     

    If(NULL!=p)

        { free(p); p=NULL; }

     else

        .......

     

    这样问题又来了,else 没有对应的if了,编译还是会失败。但是使用了do while0)则可以完美解决上述问题。代码展开如下,

    If(NULL!=p)

       do { free(p); p=NULL; } while(0)

     else

        .......

    所以,do while(0)的使用是为了保证宏定义的使用者能无编译错误的使用宏。

     

    下面我们讨论一下,为什么C语言有了for,还需要while呢?除了上面的do while0)的必要性之外,好像还真得没有确切的其他原因。比如在golang编译其中,就只有for,没有while了;再比如,java的源代码里面,就是有一堆的for(;;),据说可以提高性能。所以更大程度上讲,C语言只是为了,兼容程序员的编码习惯,保留了while这个关键字而已。

    一些更现代的语言还加入了foreachfor in这种专门遍历集合的语法糖,也有pythonruby这种直接抛弃了C语言三段式for,只保留了for in,而把非遍历型的循环统统放到while循环里面的做法。

    我们这次关于程序基本结构的探讨就先到这里。

     

    参考资料: 

     基础学习C语言第四章:三种基本结构 https://zhuanlan.zhihu.com/p/97629275

     Linux - switch/caseif/else if/else的效率比较  

    http://blog.sina.com.cn/s/blog_5acb430f0100ael9.html

      if/elseswitch/case的区别

    https://www.cnblogs.com/anlia/p/11685639.html

      深度理解do{} while(0)  

    https://blog.csdn.net/weibo1230123/article/details/81904498

     


    收藏 0 回复 0 浏览 213
  • RISC-V鸿蒙开发板单片机简介2(项目连载9)

    这篇文章我们来简要的介绍一下Pygmy-E单片机使用的I2C通信接口,主要内容是通信协议的描述,框图的讲解,以及相关寄存器的介绍等等。本文介绍的时候,尽量把它和主流的STM32单片机对比,这样大家可以对两个I2C的外设都有更清晰的认识。其中红色框框选出来的关键字属于Pygmy-E单片机支持的I2C绿色框框选出来的属于STM32F030C8T6这款单片机

    1. 传输速度

    主流的STM32单片机一样,Pygmy-E系列的单片机的I2C接口,一样支持不同的传输速度,从而适配不同的外设和场景诉求。

    image.png

    image.png

    2. 组成框图

    从框图上来看,两种类型的I2C接口组成单元是类似的。要实现I2C协议相关的信息收发功能,首先必须要有这些相应的功能;再一个,按照这样的模块进行对整个功能模块进行切分,可能在电路设计上相对最为简单。但是整体上,还是STM32这款单片机对I2C各个组成模块及其相互之间的关系描述得更加清晰。

    image.png

    image.png

    image.png

    image.png

    3. 开始信号和信号传输信号

    I2C通信协议,开始信号和结束信号,是在SCLK为低的时候,通过SDA这个引脚的变化来产生的。 SDA由高到低的时候,是start信号;SDA由低到高的时候,是STOP信号。那么既然这两个信号是开始和终止信号,那么在信号传输的过程中一定要避免这样的信号出现。那么从协议上看,在数据传输的过充中,数据信号的采样(保持不变)只能是在SCL为高电平的时候,数据信号的改变只能是在SCL电平为低的时候。另外为了避免,在SCL为高的时候,万一SDA的信号发生了变化,可以适当配置SDA和相对于SCLK信号的先发和延后时间。这个误识别的诱发因素会很多,比如布线的时候,如果不是等长线,就有可能导致默认配置的先发和延后时间失去作用。Pygmy-E单片机和STM32单片机都提供了相应的寄存器来配置这个时间。

    从这个方面上来看,两款I2C电路基本是一致的,只是配置延时时间的寄存器可能是不一样的。


    image.png

    4. 寄存器

    下面是配置两款I2C接口时所使用的寄存器,大家从图上可以看出,Pygmy-E系列的单片机I2C接口所需要配置的寄存器非常复杂,总计有68个寄存器需要配置。相比之下,STM32单片机所需要配置的寄存器就要少很多,明显感觉易用性更好。Pygmy-E单片机的I2C接口使用这么多的寄存器好处也当然是有的,它可以帮助我们对I2C接口模块有相对更为深入的理解。但是需要我们对I2C通信协议以及相关的应用非常熟悉才行,否则这么多的寄存器相当容易把开发者搞晕。

    image.pngimage.png

    image.pngimage.png

    image.png

    5. 从地址

    两个I2C接口模块,都支持7位和10位地址协议。接口的通用性上都差不多。具体的通信内容的格式,这里就不在进行比较了。

    对上述几个关键的方面进行比较后,我们基本上可以感觉到,相比STM32f030C8T6这款单片机,Pygmy-E单片机的I2C接口使用起来难度相对较高。但是它数量庞大的寄存器,同时增强了使用它时的灵活度。两款接口应该是各有所长,但是从通用性上而言,STM32I2C接口可能会更加友好。




    收藏 0 回复 0 浏览 210
  • ESP8266物联网开发板设计1

        大家好,我是张飞实战电子张角老师!

    通过这个电源,我们就可以得到12V的电压了,然后我们可以通过线性电源,比如7805把这个电压降压到5V,然后在再通过AMS11175V电压降压到3.3V。这种方案适用于产品开发中,我们甚至可以自己开发一块反激电源放在我们的产品的板子上。因为实际的环境中,可能只有AC 220的电源。对于一些极端的情况,比如AC 220V不能够达到的地方,有的物联网产品甚至只能是太阳能供电,当然这个电源是另外一个方案了。

    一般情况下开发板则不同,只是大家学习的工具,对吧。那么供电部分的方案,自然也是不同的。比如我们就可以从我们的笔记本或者台式机上的USB口上获取电源。这样我们就利用了电脑自己的电源模块,是吧。AC降压的部分是电脑自己完成的,我们这里的方案是站在它的基础上的。USB电源的输出电压是5V,一般情况下,这个5V电源的输出能力可以达到500mA左右,可见它的功率还是蛮大的。

    上面我们只是分析了大体的情况,那么具体到我们这个开发板中,这些大体的方案合适么?我们下面来具体分析一下。

    首先我们来看一下,我们的这个模组的供电要求,从下图可以看出这个模块要稳定工作,供电电流要大于500mA。但从这一个数据来看,我们就没有办法仅仅使用一个USB来供电,那样单片机有可能就不可以正常工作,我们这里说得是有可能呀,也不一定不能工作。但是从产品设计的角度来看,如果只用一个USB接口来供电,这样的设计是不稳妥的。

    我们今天使用ESP8266这款芯片设计一块物联网开发板,使用的模组是ESP-12F,这款模组我们在上篇文章已经给大家介绍过,生产厂家是深圳是安信可科技有限公司。我们这款开发板,只是使用这个模组内部的单片机ESP8266,实现的功能来说也相对较为简单,换句话说,也就是没有外挂单片机。

    那么总体上要实现什么功能呢?

    第一个要实现的功能是温湿度的采集,这个是物联网场景中最为常见的。远程而且在线的数据采集功能,能够为系统决策提供实时的决策依据。我们这里主要是以温湿度的采集以及往云端上传为例,给大家演示一下物联网开发板的流程是什么样子的。

    第二个要实现的功能是远程控制继电器的开关。我们从物联网的数据终端获得了数据之后,自然需要根据数据做出决策。那么这个决策执行,一个最简单的示例就是继电器的开通和关断。我们可以通过继电器的开通和关断,来决定相应的模块是不是需要运行。

    ESP-12F这个模组要想能够正常工作,首先就是要搭建一个单片机的最小系统。这个最小系统包括如下模块,比如电源模块,程序烧录模块,时钟模块,Reset模块,boot选择模块等等。只有这些模块工作正常了,ESP-12F内部的单片机才有可能工作起来。

    我们下面分别来看一下,这些小模块用什么样的方案实现比较合适。

    第一个是程序烧录模块,我们这里使用的是USB转串口,芯片是CH340G,这个是南京沁恒公司出品的,属于非常常用的芯片,性能稳定价格相对便宜。具体的电路设计也相对来说比较简单。大体的电路设计如下:

    image.png

    我们这里只是参考示意图,具体电路实现,我们后面再分析。

    第二个是时钟模块,我们这个ESP-12F里面应该已经包含了时钟震荡电路,我们不用再外接震荡电路模块。从模组管脚功能的描述上看,ESP-12F也没有外部晶振的接口,那么我们可以断定这个模组已经自己搞定了震荡器部分,要么是单片机内部的RC震荡器,要么是模组封装的石英震荡器。后面有机会,我们再对这块进行分析。

    image.png

    image.png

    Reset模块和BOOT模块按照芯片给出的说明来就可以了。RESET这个地方一般是低复位,意思也就是说这个管脚在低电平的时候,芯片内部执行复位操作。BOOT引脚,从上图也能看出,IO0为低的时候,单片机处于下载模式。如果要让单片机处于运行模式,这个引脚必须拉高或者悬空。那么我们进行电路设计的时候,按照芯片的说明操作就可以。

    再一个就是电源模块,很显然ESP-12F这个模组要能够正常工作,它一定是需要电源的。那么这个电源电压是多少呢?我们来看一下ESP-12Fdatasheet

    image.png

    这张表里面,我们是不是可以看出来这个模组的供电电压是不是3.3V呀,Io口的最大电流驱动能力是12mA对吧。VilVih表示输入低和高的电平值,从datasheet上可以看出来输入为低的时候,最低值是-0.3V,最大值是0.25倍的Vio;输入为高的时候,最低值是0.75倍的Vio,最大值是3.6VVol的最大值是0.1倍的Vio,也就是说作为输出且输出为低的时候,它的最大值0.1倍的VioVoh的最小值是0.8Vio,这句话的意思是,IO口作为输出,且输出为高的时候,它的最小值是0.8倍的Vio。了解了这些参数,可以对我们进行外围电路的设计起到指导作用。

    那么这个3.3V的电压从哪里来最合适呢?我们这里有两条思路,一条思路是从AC 220V上通过反激电源降压到12V,然后再从12V降压到3.3V;另外一条思路是利用USB接线的5V电压,直接变换成3.3V。第一种方案,我们首先要买一个12V的电源适配器,如下图所示。

    image.pngimage.png

    通过这个电源,我们就可以得到12V的电压了,然后我们可以通过线性电源,比如7805把这个电压降压到5V,然后在再通过AMS11175V电压降压到3.3V。这种方案适用于产品开发中,我们甚至可以自己开发一块反激电源放在我们的产品的板子上。因为实际的环境中,可能只有AC 220的电源。对于一些极端的情况,比如AC 220V不能够达到的地方,有的物联网产品甚至只能是太阳能供电,当然这个电源是另外一个方案了。

    一般情况下开发板则不同,只是大家学习的工具,对吧。那么供电部分的方案,自然也是不同的。比如我们就可以从我们的笔记本或者台式机上的USB口上获取电源。这样我们就利用了电脑自己的电源模块,是吧。AC降压的部分是电脑自己完成的,我们这里的方案是站在它的基础上的。USB电源的输出电压是5V,一般情况下,这个5V电源的输出能力可以达到500mA左右,可见它的功率还是蛮大的。

    上面我们只是分析了大体的情况,那么具体到我们这个开发板中,这些大体的方案合适么?我们下面来具体分析一下。

    首先我们来看一下,我们的这个模组的供电要求,从下图可以看出这个模块要稳定工作,供电电流要大于500mA。但从这一个数据来看,我们就没有办法仅仅使用一个USB来供电,那样单片机有可能就不可以正常工作,我们这里说得是有可能呀,也不一定不能工作。但是从产品设计的角度来看,如果只用一个USB接口来供电,这样的设计是不稳妥的。

    image.png

    另外一个耗电较大的是继电器,我们这里的继电器选定的是SRA系列的,线圈的开通电压,我们可以选择12V。那么我们来看一下,这个继电器的线圈需要多大的电流。

    image.png

    从上图中可以看出来,这个电流要50mA,是吧。

    我们下面来看一下CH340G的功耗情况。从下表可以看出来,它的功耗最大值是在20mA左右,这个器件待机状态的时候,功耗更低,只有0.2mA左右。

    image.png

    SHT20(温湿度检测芯片)和其他一些LED等的模块加起来,功耗应该不会超过10mA。这样算下来,这个开发板需要的总的供电能力,差不多在600mA左右。如果再留有一点余量的话,那么我们选用的反激电源适配器,它的输出能力差不多要在1A左右。

    从上面情况来看,USB供电是肯定不合适的。我们这个开发板,不适合USB供电,只能是使用适配器从AC 220V上引电下来。

    有了12V电源以后,我们第一步要做的事情,就是把12V电压变成5V的,常用的器件就是LM7805。我们来看一下LM7805的输出能力,从下图中可以看出来,它差不多有1A的输出能力,是吧。这个能力是可以满足模组功耗需求的。但是我们可以计算一下它自身的功率。 P = 12V  5V* 500mA  = 3.5W,也就是说仅仅带载ESP-12F这个板子,就需要3.5W的功率。这个对LM7805的封装来说是不可以承受的。即使加上散热片,也难保散热片不烫手,这个功率太大了。那怎么办呢?我们这里只能选择能效比更好的电源方案,比如开关电源。我们可以选择一个buck降压方案。这个buck电路,可以直接从12V降压到3.3V,也可以从12V先降压到5V,然后再通过LDO模块把电压从5V降低到3.3V。这两个方案,我们可以都用一下,看一看哪个更合适。为什么一般情况下,单片机前端电源一般采用LDO(比如AMS1117等)呢?主要是LDO输出的电压精度比较高。还有一个,如果Buck电源坏掉了,buck上的高压信号,一般也不会直接伤到单片机,中间还有一个LDO电源起到了隔离作用。另外,Buck电路输出的电压文波率相对来说也比较大,一般在2%-5%左右,这个波动对单片机的工作也会有影响。但是呢,现在的单片机一般也是宽电压输入,就像我们这个ESP-12F,它的输入电压可以从3.0V-3.6VBuck的文波率按理说应该是可以满足要求的。我们这里可以把两个方案都试一试,通过跳线帽或者0欧姆电阻的方式选择供电电源。一个是测试一个LDO电源(AMS1117)的发热量怎么样,再一个看看buck电源能不能满足单片机的需求。

    image.png

    那么我们这里还需要一个5V3.3V的电压变换,我们这里也选择最为常用的AMS1117。这个片子上面的功耗也是蛮大的,P = 5V  3.3V* 500mA = 0.85W。我们需要对这个片子的散热做一些特殊的处理,比如在地上需要大面积敷铜或者加散热片。我们这里AMS1117选择SOT223的封装,等板子搞好之后,实测一下温升能有多少。buck芯片我们采样SY8120 DCDC的片子,具体buck电路的设计,我们有机会展开来讲。

    讲完了单片机的最小系统,我们看一看器件封装的一些问题。

    对于各个器件原理图和PCB的封装,站在开发板这个需求程度上,我们可以直接采用立创上给出来的。但是如果实际做产品的时候,元器件pcb的封装,我们就要格外注意了。pcb的封装要和实际生产工艺匹配起来,这样才能最大程度上降低生产成本,进而能够降低产品总的成本。比如封装不能过大,否则可能就会有立碑现象;再比如,如果封装大小不合适,虚焊的可能性就会加大,进而产品的质量可能就会受到影响。做产品的问题,我们暂且不说,我们先来看一下,如何直接利用立创商城提供的封装。第一步,我们要先把这些封装导出来,我们这里以DHT11为例(目前这样使用有效,后续网站可能会改版,大家留意)。

    比如我们这里首先选择直接使用厂家提供的封装,点击立即使用以后,就会出现schdocpcbdoc两个类型的文件。我们后面把可以把它导出来,变成AD软件支持的格式。这里说一下,我这里习惯使用AD软件,可能和一些同学的习惯不太一样。如果大家直接使用立创的画图软件,也就不需要导出来了。这个地方大家酌情使用。

    image.png

    image.png

    image.png

    这些文件导出来之后,我们就可以在AD软件里面生成相应的lib文件了,以便于我们在画原理图和PCB图的时候使用。

    今天关于这个开发板的分享,就先到这里。我们下篇文章会继续探讨相关原理图模块的设计。

     

    ①基于ESP8266STM32物联网开发板设计

    https://blog.csdn.net/weixin_42107954/article/details/97494269

    ②物联网开发板-ESP8266   https://lceda.cn/jixin003/iot_board_esp12

    ESP8266教程-技小新 https://www.yuque.com/lingyao/jing/ob5wia

    ESP-12F 规格书

    https://item.szlcsc.com/84052.html

    https://atta.szlcsc.com/upload/public/pdf/source/20210219/C82891_ABD84A460F4F056A2757267CCCA4B508.pdf

    SRA系列继电器说明书

    https://item.szlcsc.com/61221.html

    https://atta.szlcsc.com/upload/public/pdf/source/20180525/C60169_619C103F264B8308190CF1E1CA37960C.pdf

    CH340C datasheet https://item.szlcsc.com/85852.html

    LM7805 datasheet https://item.szlcsc.com/520584.html

     


    收藏 0 回复 0 浏览 196
  • ESP8266物联网开发板设计2

    大家好,我们今天继续物联网开发板的原理图设计部分的探讨。

    上篇文章,我们着重探讨了电源部分的工作过程,整体上确定了“反激”+  Buck + LDO三种电源配合的方式。反激部分,其实就是我们买的适配器,这部分我们已经确定通过外购的方式进行。LDO这个地方,使用的是AMS1117的方案,它主要的任务是实现5V3.3V的降压。Buck则是实现电压从12V降低到5V的任务。

    大家可能在其他地方也见到过,使用LDO的方案,把电压从12V降低到5V或者3.3V。使用这种方案有一个前提条件,那就是负载不能太重,也就是负载电流不能太大。如果负载电流太大,LDO DCDC的芯片就会发热比较严重。因为LDO这个模块的工作原理,它还是利用半导体器件(比如下图中的T4)的非线性阻抗(Rce))来降压的。压差越大,电流越大,那么LDO上面所消耗的功率也就越高。所以对大电流、高压差的情况,LDO就不太适合了。

    image.png

    那么我们必须采用一种内阻更低的能量传递方式,大家比较熟悉的就是开关电源了。降压开关电源比较常用的拓扑结构就是buck。它的原理,是使用储能器件(电容、电感)代替了耗能器件(电阻)进行能量传递,所以它的能效比非常高。另外,使用开关的开通和管断来调节能量传递的过程,实现了负载动态调节和宽电压输入,比如如果负载比较重,那么开关开通的时间就长一些,关闭的时间就短一些;如果负载毕竟轻,开关开通的时间就短一些,关闭的时间就长一些。

    我们这个开发板的buck电路是使用DCDC的芯片来做的,这样可以做到板子的体积比较小,更加实用化。如果大家想彻底搞明白buck电路的工作原理,还是得想办法自己去搭建分立器件的电源转换电路,这样才能理解得更加深刻一些。如下图所示,我们这个buck电路使用的芯片是SY8120,开关控制的思路都被封装在器件内部了。

    image.png

    如果大家一下子搞不明白这个芯片的工作原理,可以后面找时间慢慢学习。我这里需要强调的是,对于开关电源来说,开发板布局非常重要的。因为开关信号是一个阶跃信号,它可以通过展开成一系列高频的傅里叶级数叠加,也就是展开成各个频率的信号。但是呢,我们需要的其实就是基波信号,其他高频的东西对于我们来说都是干扰。那么我们在布局的时候,就要尽可能减少这些高频信号的对外部的影响,尤其是Id部分,所以Id回路要尽可能的小,一个是减少环形天线对外辐射的影响,一个减小环路上走线寄生电感带来的影响。再一个,Buck电源输出的+5V的地,一定要单点接地,因为这个地相对来说比较脏,我们要通过单点接地的方式来降低它对其他器件的影响。另外在布局的时候,我们尽可能让ESP-12F的天线部分远离buck电路,进一步降buck Id主回路的影响,下图是SY8120 datasheet中给出来的布局参考,大家可以观察一下,IN ---> LX --->L ---> Cout ---> GND 这条线,其实回路已经尽可能的短了。虽然FB引脚也是一个高频回路,但是它的电流不大,相对来说没有那么重要,所以把它放在了相对次要的位置。大家注意呀,这里只是相对而言的。

    image.png

    最小系统中的复位电路,我们前面也提到了。那么我们使用的ESP8266这款单片机的复位过程具体是怎样的呢?我们来看一下datasheet相关的说明:

    image.png

    这里只是说外面加一个RC电路就好了,同时对走线提出了一定的要求,并没有讲清楚单片机是如何实现复位的。其实我们只要按照要求做,就可以了。但是我们还是要想办法尽可能对复位电路加深一些理解。我们可以参考STM32F030系列单片机的复位电路进行对比理解。

    大家看一下下面两幅图,这两幅图说明是单片机上电时的复位过程。

    image.png

    image.png

    大家看一下,Reset信号的波形,上电的时候,Reset保持为低时间可以分为两段。第一段时间,是VCC电位上电到POR电位的时间,第二段时间是Reset temporization的时间。第一段时间相对好理解,只有电压稳定了,单片机才能工作么。第二段时间Reset temporization这段时间大部分的时间应该是晶振起振的时间,还有其他器件reset复位的时间。我们做过一个Reset的测试,从上电开始到一个管脚输出高电平(程序第一行就是这个)的时间,在STM32 F030中,这个时间是2ms左右。具体ESP8266这个片子的上电复位是不是这么一个过程,我想大概是吧。目前还没有找到更多的材料支撑。

    我们下面看一下STM32F030单片机的按键复位的说明。

    image.png

    image.png

    大家可以看一下,产品文档这里并没有对按键复位的时间进行说明,只是说电压要下降到一定的程度才行,比如电压要降低到0.8V。那么这种情况下,我们电路设计的那个RC,其实只是起到了一个滤波作用,对不对,防止外界的干扰导致单片机复位。大家看看,这个作用和ESP8266对外界阻容电路的描述是不是一样的。所以我推测ESP8266的复位过程和STM32F030差不多(这里不打包票),大家如果能有更详细的资料,可以再深入研究下。

    image.png

    具体到外界阻容电路中RC的取值,我们一般是取值R=10KC=104。从上面的分析了来看,这里去其他值应该也没有什么问题,大家可以根据自己的实际情况(比如BOM表器件种类不要太多)进行选取。

    下面我们来看一下晶振是如何起振的。因为模组已经把晶振封装到内部去了,我们不用去操心晶振部分电路的设计。但是其实晶振电路的设计非常关键,晶振毕竟是单片机的心跳呀。没有心跳信号,单片机如何工作。另外一个,我们的USB转串口电路中也用到了晶振电路部分的设计,所以这里我还是打算花一些时间把晶振的工作原理和一些设计的要点给大家讲清楚。

    image.png

    image.png

    如上图所示,这个铁盖子下面其实是集成了不少东西的,其中就包括了一个26Mhz的晶振。只不过因为已经封装过了,我们看不到而已,就像我们前面提到的一样,看不到不代表不存在,是吧,呵呵。受限于篇幅的原因,这篇文章就先到这里。下篇文章,我们重点分析晶振的起振过程还有继电器、温度传感器等外设电路的设计。

     

    ①无源晶振起振条件及其工作原理 http://m.elecfans.com/article/582154.html

    51单片机晶振电路原理是什么  

    https://www.zhihu.com/question/30930577/answer/55822425

    ③ 三点式LC正弦波震荡电路详解

    https://blog.csdn.net/weixin_42415539/article/details/88540709

    ④电容三点式震荡电路波形问题

    http://bbs.eeworld.com.cn/thread-1090538-1-1.html#pid2904297

    ⑤三点式电容震荡电路的工作原理 https://zhuanlan.zhihu.com/p/354627295

    ⑥单片机上电复位时间 https://blog.csdn.net/zyboy2000/article/details/4673955

    ESP8266文档中心 https://docs.ai-thinker.com/esp8266/docs


    收藏 0 回复 0 浏览 193
  • 斜坡补偿的由来

    大家好,我是张飞实战电子张角老师!

    在开关电源中,有一个相对不容易解释清楚,但是对整个系统的稳定性又起到重要作用的知识点-斜坡补偿。我们打算后面用一系列文章来深入探讨一下这方面的知识,争取让大家能够对这块内容有相对深入的理解。

    我们首先需要回答的问题,就是开关电源系统为什么需要斜坡补偿,或者说斜坡补偿解决了哪方面的问题?从宏观上看,开关电源的调节系统可以分为两部分,一个是内环电流环,一个是外环电压环。之所以把电流环称之为内环,主要是因为它的调节速度更快,属于单周期调节。电压环,调节速度相对电流环肯定是更慢的,对于输出滤波电容来说,电压是电流的积分。电流环调节的结果要经过数十个周期之后,才能在电压信号上做出反应。

    当系统的输出电流或者说电感电流,因为负载变化或者输入电压变化产生扰动的时候,在电压环起到调节作用之前,有可能发生电感电流的次谐波震荡,或者说是电感电流的振铃现象。

    这里之所以说是有可能,主要是因为这个现象发生在CCM模式下,而且占空比大于50%的时候。当占空比小于50%的时候,扰动电流的震荡会自动趋于收敛。我们下面用图形化的方式给大家演示一下。

    image.png

    上图是占空比小于50%的时候,负载变化引起的电流扰动。大家可以看一下,这个扰动在几个周期后,会自动收敛。具体为什么会收敛呢?我们可以推算一下电流扰动量的变化公式。

    image.png

    大家可以看一下,当占空比小于50%的时候,Ton的时间内电流上升的斜率大于Toff时间内电流下降的斜率。那么这个电流抖动量的变化趋势和这两个斜率的比值很有关系。

    具体的推导公式,如上图所示。我们这里是分两个三角形来进行计算的。在θ0的三角形内,根据正切关系可以得到δIoIt(两个三角形的公共部分)比例关系式①。实际上这个正切关系本质上是不是就是斜率呀。同样的,在θ1这个三角形内,我们也可以得到式子②。这两个式子化简以后,就会得到关系式③。大家可以看一下,δI1是不是要比δIo要小呀,因为m2小于m1,对吧。当占空比小于50%的时候,是不是m2一定小于m1呀。那么经过几个周期的迭代,是不是扰动量慢慢变小后,系统也就收敛了。

    但是当占空比大于50%的时候,也就是m2大于m1的时候,这个扰动量是不是也来越大,结果也就会出现电感电流的震荡,如下图所示。

    image.png

    对于控制系统的开关芯片而言,Ton的时间也就会出现波动,也就是会出现所谓的大小波交替现象的发生。这个波动的电流对系统的稳定性是无益的,需要想办法解决掉。那么该怎么解决呢?这个也就是斜坡补偿的由来:既然这种情况下m2大于m1,我们能不能通过斜坡补偿的方式使得整体的斜率大小关系发生改变呢?

    下篇文章,将会对这个问题进行更深入的分析。

     


    收藏 0 回复 0 浏览 186
×
张角