关于集成电路IO引脚电平

具体的IO引脚的实现可以百度,有很多电路图,会用到场效应管和其它元器件来实现IO接口。这里讨论的是IO输入和输出高电平。特别是对电平的一些理解。

IO引脚有两种模式:输入输出。

输入:外部到单片机内部

输出:单片机内部到外部

IO一般输入输出指的是电平输入输出。

电平的解释:两点的电功率或者电压之比的对数。

电压:两点间的电势或者电位的差。(实际很好理解,一个电源一个电阻串联,测电阻两端电压是多少,根据欧姆定律很容易算出。电压就是我们正常生活中的,只不过概念理解起来很多。涉及到一个又一个细小概念分支)

电位:又叫做电势或电位,单位是V。(涉及到静电学和电动力学,自行百度。)

电功率:电流在单位时间内所做的功,单位:瓦特 简称:瓦 符号:W 表示方式:P(公式上)

电能:电流以各种形式做功的能力,单位:度 学名:KW.H 也就是一小时消耗多少千瓦。

电功率和电能看这容易混淆,学校的知识我也忘得差不多了,百度了很多,发现有一个解释比较容易理解。

电能是路程,电功率是速度。一个人一个小时可以跑100米这是速度,而这100米是路程。某元器件的电能是100电能,那么在一个小时内他可以产生100K的电功率。在两个小时就是200K的电功率。

对数:幂的逆运算。比如2的3次方是8。那么8以2为底的对数就是3。

电平也分为高电平和低电平,高电平代表:1,低电平代表:0。由于1和0是二进制,所以就可以组成一些运算,比如逻辑门:非门、或门、与门等。高电平是大于3v,低电平是小于1.5v而1.5-3v之间的是中间电平,一般不做处理。

高低电平用方形图表示

屏幕快照 2019-09-23 上午12.53.40.png

由于是连续的产生电压或者电功率所以电平也是连续的,方形图表示的话也会是一直连续下去这里只给出一个,连续的方形图也可以叫做脉冲。这里引出一个概念叫做总线,总线可以用来传输数据,具体的实现方式实际也是利用电平。视频资料:https://www.bilibili.com/video/av7230090?from=search&seid=5145470121650526078。还有一点,图形上看着是直棱直角的直线,实际电流过程中比如在刚通电的时候,电流是不稳的所以画出来的线应该是有波动的。不过这种波动非常小或者说非常快的趋于平稳,所以画图的时候近似的画成直线,但是实际中它并非直线。

上拉电阻:上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起限流作用。下拉同理,也是将不确定的信号通过一个电阻钳位在低电平。这里的信号指的是模拟信号例如声音或者数字信号例如1和0的高低电平,设计到模电和数电。想更深入的了解可以看一看模电和数电,这里我还没看,所以没办法解释信号。

上拉电阻一般接在电源位置起到拉高电平输出,下拉是接在接地端,起到拉低电平的作用,可以通过欧姆定律串并联来理解。

钳位:钳位是指将某点的电位限制在规定电位的措施。

总的来说,上拉电阻我接上以后这点的电平会变高,因为会分压,分到很大一部分的电压,所以相对于某一点,我是高电平。

一张图理解一下(个人理解):

屏幕快照 2019-09-23 上午1.22.51.png


当IO为输出的时候,A点相对于B点的电压一定要小,所以这里要输出低电平。如果输出高电平,也就是A点的电压比b点的电压大的话,那么LED是不会亮的。因为LED的负极比正极电压还大了。这里的LED是负极接IO正极接VCC。

如果是LED正极接IO,而负极接地,那么就要输出高电平。因为A点相对于接地端的电压一定要大。才能让电流流通。

而作为输入的时候我觉得是A点的电压相对于集成电路内部的电平也就是电功率或者电压的比值。这里的B点只是一个参考,也可能这个B点在LED负极的前一点。




跑马灯电路板仿真

222222.gif

工具

Proteus:MCU仿真软件

ICC AVR : AVR单片机代码编写软件

元器件

ATMEGA-8 :AVR MCU

LED:发光二极管

电源:这里用VCC代替

元器件在proteus的名字

LED-RED

ATMEGA8

CHIPRES470R

这里的VCC是逻辑端子,没有具体的物理(实体)模型,只是电路图中代表电源,所有取名一样的端子将会在PCB中进行连接。另外电路图中没有加入电阻,所以对二极管的输入电流没有做控制,实际操作中需要加上的。另外还有物理端子,代表有实体的物理模型。

ATMEGA-8上面有很多的输入输出(I\O)端口,这里用的是PD1、PD3、PD4端口。代码使用ICC AVR 来编写,不同的版本有不同的书写方式。新版本好像套了一个开源的编辑器,旧的版本还是自己的编辑器。大概了解一下就可以了。然后就是编译输出为HEX,这些设置都可以在Project---Option里进行设置。

微信截图_20190920144931.png

还有需要注意的是ICC AVR 编辑器需要先新建工程,才能编译文件。不新建工程的话,即使代码写完,也无法编译。

跑马灯代码

#include <iom8v.h>
void delay_1ms(void){
    unsigned int i;
    for(i=1;i<(unsigned int )(1144-2);i++);
} 
void delay_1ma(unsigned int n){
    unsigned int i=0;
    while(i<n){
        delay_1ms();
        i++;
    }
}
void main(){
    unsigned char i;
    DDRD=0xFF; //设置D端口为输出模式 还有输入模式0xFF
    while(1){
        PORTD =~(1<<1); // << 是位移操作 相应位置对应的端口 1000 : 1<<4 即第四位  
        delay_1ma(30); // 延迟
           PORTD =~(1<<3);  
            delay_1ma(30);
            PORTD =~(1<<4);
            delay_1ma(30);
    }
}

关于输出高电平点亮led的解释

0824ab18972bd4079415dc097d899e510eb30977.png

 当I/O口输出高电平时,LED两端的电位相同,因此电压为0V,不能构成电流回路,所以LED不亮。当I/O口输出低电平时,LED左侧电位为0,而右侧则在R1的上拉作用下电位提高,因此LED两端有正向电压,可以点亮发光。

  MCS-51单片机的I/O口具有比较强的灌电流能力,但拉电流能力却很弱,所以并不适合用输出高电平的方法点亮LED,大多采用这种负逻辑的驱动方法。


为了更好的理解下面是说明书中的内容

本节所有的寄存器和位以通用格式表示:小写的 “x” 表示端口的序号,而小写的 “n” 代表

位的序号。但是在程序里要写完整。例如, PORTB3 表示端口 B 的第 3 位,而本节的通

用格式为 PORTxn。物理 I/O 寄存器和位定义列于 P63“I/O 端口寄存器的说明 ” 。

每个端口都有三个 I/O 存储器地址 : 数据寄存器 – PORTx、 数据方向寄存器 – DDRx 和端

口输入引脚 – PINx。数据寄存器和数据方向寄存器为读 / 写寄存器,而端口输入引脚为只

读寄存器。但是需要特别注意的是,对 PINx 寄存器某一位写入逻辑 "1“ 将造成数据寄存

器相应位的数据发生 "0“ 与 “1“ 的交替变化。当寄存器 MCUCR 的上拉禁止位 PUD 置位

时所有端口引脚的上拉电阻都被禁止。

编译好HEX文件以后,双击proteus仿真软件中的MCU ATmega8 图形 然后选择这个HEX。

微信截图_20190920151047.png

然后点击仿真就可以了 ,电源VCC逻辑端子的话,可以点击终端模式选择POWER就可以了。


个人理解,如有错误还望指出。

php获取明天后天下星期时间戳

function futureTime($command){
    list($com,$n,$x)=explode("-",$command);
    if($com=="xq"){
        $time=date("w",time( ));
        $array = ["7","1","2","3","4","5","6"];
        $time=date("w",time( )); 
        $tmp=strtotime(date('Y-m-d',strtotime('+'.($n*7).' day')))-(($array[$time]-$x)*24*60*60)-8*60*60;
        return [$tmp,$tmp-time()];
    }
    if($com=="rq"){
        $tmp=strtotime(date('Y-m-d',strtotime('+'.$n.' day')))-8*60*60; 
        return [$tmp,$tmp-time()];
    }
}

获取下星期三:futureTime("xq-1-3")

xq:星期标识

1:代表下星期 2:代表下下星期 n:代表第n个星期

3:代表星期三 (1.2.3.4.5.6.7 星期一到星期天)

获取明天:futureTime("rq-1")

rq:日期标识

1:代表下一天

返回值:索引数组,一个是日期的时间戳,另一个是现在距离日期的秒数。

注意:所有的时间都是秒数,并且是从00:00:00开始计算的。


redis 死循环为什么CPU很低?

redis的事件循环代码

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}

让我不理解的是,这是一个死循环,但是CPU占用很低。所以就看看代码学习一下。由于我只需要时间事件(processTimeEvents)。所以我将aeProcessEvents删掉了一些代码,只保留时间事件。然后CPU暴增。

微信图片_20190918182652.png

可以看到CPU从0.0涨到了90.3于是往上回溯代码。最终发现其中一个比较关键的:aeApiPoll函数。

网上有一段话:aeProcessEvents 都会先 计算最近的时间事件发生所需要等待的时间 ,然后调用 aeApiPoll 方法在这段时间中等待事件的发生,在这段时间中如果发生了文件事件,就会优先处理文件事件,否则就会一直等待,直到最近的时间事件需要触发。跟进去会发现是一个select,它是用来等待文件描述词(普通文件、终端、伪终端、管道、FIFO、套接字及其他类型的字符型)这些的改变事件。

在传递的时候会将tvp传入进来,tvp是最近发生的时间,也被设置成了select的过期时间,所以如果没有发生文件事件,然后又select又过期了,自动会走如下代码

    if (retval > 0) {
        for (j = 0; j <= eventLoop->maxfd; j++) {
            int mask = 0;
            aeFileEvent *fe = &eventLoop->events[j];
            if (fe->mask == AE_NONE) continue;
            if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds))
                mask |= AE_READABLE;
            if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds))
                mask |= AE_WRITABLE;
            eventLoop->fired[numevents].fd = j;
            eventLoop->fired[numevents].mask = mask;
            numevents++;
        }
    }

所以这里的等待就会造成while的一些时间片的释放,然后会降低CPU。

layui form on触发多次的问题

ThinkAdmin后台使用的layui框架,其中的data-open,和data-modal存在一些使用上的问题。例如table表有两个功能项。编辑,增加。

编辑是data-modal

增加是data-open

其中都使用了form.on

复现步骤如下:先点击编辑,然后点击增加。会发现增加里面触发了编辑的form.on事件。百度了一下也没有结果,没办法去查看了layui的源码。

绑定事件源码:

//表单事件监听
  Form.prototype.on = function(events, callback){
    return layui.onevent.call(this, MOD_NAME, events, callback);
  };

调用了一个call

Layui.prototype.onevent = function(modName, events, callback){
    if(typeof modName !== 'string' 
    || typeof callback !== 'function') return this;
    return Layui.event(modName, events, null, callback);
  };

查看event事件

//执行自定义模块事件
  Layui.prototype.event = Layui.event = function(modName, events, params, fn){
    var that = this
    ,result = null
    ,filter = events.match(/\((.*)\)$/)||[] //提取事件过滤器字符结构,如:select(xxx)
    ,eventName = (modName + '.'+ events).replace(filter[0], '') //获取事件名称,如:form.select
    ,filterName = filter[1] || '' //获取过滤器名称,,如:xxx
    ,callback = function(_, item){
      var res = item && item.call(that, params);
      res === false && result === null && (result = false);
    };
    
    //添加事件
    if(fn){
      config.event[eventName] = config.event[eventName] || {};
      //这里不再对多次事件监听做支持,避免更多麻烦
      //config.event[eventName][filterName] ? config.event[eventName][filterName].push(fn) : 
      config.event[eventName][filterName] = [fn];
      return this;
    }
    
    //执行事件回调
    layui.each(config.event[eventName], function(key, item){
      //执行当前模块的全部事件
      if(filterName === '{*}'){
        layui.each(item, callback);
        return;
      }
      
      //执行指定事件
      key === '' && layui.each(item, callback);
      (filterName && key === filterName) && layui.each(item, callback);
    });
    
    return result;
  };
  win.layui = new Layui();

其中可以看到 

config.event[eventName][filterName] = [fn];

可以将事件重置,所以在增加里面重新绑定编辑的事件就可以了,function可以设置为空。