1 介绍

1.1 RTC选择

  RTC选择不外乎就两种,独立外挂和CPU集成,精度要求不高或者联网的情况下使用集成RTC即可,可节约成本。独立RTC的选择则比较多,从低端到高精度的,各大厂商都有可选择,常用的如DS1302、PCF8563、DS3231等。对于时间要求严格,并且没有连接网络无法同步网络时间,则需要选择独立RTC,对于RT-Thread来说,本人针对PCF8563和DS3231都进行测试过,并且在产品上已经使用。所以选择一款经典的RTC芯片,以PCF8563为例,阐述RTT下的RTC驱动实现,举一反三,根据此更换其他RTC芯片则是依葫芦画瓢。

1.2 PCF8563

  PCF8563是一款非常经典的实时时钟(RTC)芯片,是飞利浦(PHILIPS)司推出的一款工业级内含I2C 总线接口功能的具有极低功耗的多功能时钟/日历芯片。PCF8563的驱动源码和说明文档也是非常多,特别是可以参考Linux内核的PCF8563源码。

2 RT-Thread驱动

2.1 RTT驱动模型

  RTT驱动模型和Linux比较类似,严格分为几层,而且层次分明,层与层之间都有标准的访问接口,最上层封则装成统一的接口,即是open、read、write、close

RTT 驱动类型为“struct rt_device”结构体,其原型如下:

/*** Device structure*/
struct rt_device
{struct rt_object          parent;                   /**< inherit from rt_object */enum rt_device_class_type type;                     /**< device type */rt_uint16_t               flag;                     /**< device flag */rt_uint16_t               open_flag;                /**< device open flag */rt_uint8_t                ref_count;                /**< reference count */rt_uint8_t                device_id;                /**< 0 - 255 *//* device call back */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);/* common device interface */rt_err_t  (*init)   (rt_device_t dev);rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);rt_err_t  (*close)  (rt_device_t dev);rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);rt_err_t  (*control)(rt_device_t dev, rt_uint8_t cmd, void *args);void      *user_data;        /**< device private data */
};

  其中的函数指针部分就是需要我们在底层重点实现的,其他参数则为描述驱动类型或者调用驱动的方式设置。

2.2 RTT的RTC框架模型

  RTT兼容C库的时间获取函数—“time”,并重写了time函数,其实现源码如下。

#if defined (__IAR_SYSTEMS_ICC__) &&  (__VER__) >= 6020000
#pragma module_name = "?time"
time_t (__time32)(time_t *t) /* Only supports 32-bit timestamp */
#else
time_t time(time_t *t)
#endif
{static rt_device_t device = RT_NULL;time_t time_now = 0;/* optimization: find rtc device only first. */if (device == RT_NULL){device = rt_device_find("rtc");}/* read timestamp from RTC device. */if (device != RT_NULL){if (rt_device_open(device, 0) == RT_EOK){rt_device_control(device, RT_DEVICE_CTRL_RTC_GET_TIME, &time_now);rt_device_close(device);}}/* if t is not NULL, write timestamp to *t */if (t != RT_NULL){*t = time_now;}return time_now;
}

  设置日期和时间的函数分别为“set_date”“set_time”,这个几个函数需要我们关注的地方是RTC驱动注册名称为“rtc”,及“control”函数。了解这几个函数,主要是熟悉访问RTC驱动的方式,以下面实现PCF8563驱动过程。

2.3 RTT驱动调用过程

3 RTT PCF8563驱动

  在使用RTT驱动框架控制一个外设时,原则上最基本的需要实现的函数实体分别有“init”、“open”、“close”、“read”、“write”以及“control”函数,这样应用层就可也驱动框架接口(API)访问底层外设。

  而通过上文分析RTT下的RTC设备模型可看出,访问RTC设备是循序地按照“open->read/write->close”的来处理,对于行时间的获取和设置,都是通“control”函数接口进行。因此我们在这里需要关键实现的即是“control”函数接口对应的实体函数,该函数的功能包括从PCF8563读出时间和向PCF8563写入设定的时间。而对于“open”、“close”函数,则暂保留为空,如果后期RTT框架有改动,则增加openclose的内容。

3.1 PCF8563设备接口实现——control函数实现

static rt_err_t rt_pcf8563_control(rt_device_t dev, int cmd, void *args)
{rt_err_t   ret = RT_EOK;time_t        *time;struct tm     time_temp;  rt_uint8_t  buff[7];RT_ASSERT(dev != RT_NULL);rt_memset(&time_temp, 0, sizeof(struct tm));switch (cmd){case RT_DEVICE_CTRL_RTC_GET_TIME:time = (time_t *)args;ret = pcf8563_read_reg(REG_PCF8563_SEC,buff,7);if(ret == RT_EOK){time_temp.tm_year  = bcd_to_hex(buff[6]&SHIELD_PCF8563_YEAR) + 2000 - 1900;time_temp.tm_mon   = bcd_to_hex(buff[5]&SHIELD_PCF8563_MON) - 1;time_temp.tm_mday  = bcd_to_hex(buff[3]&SHIELD_PCF8563_DAY);time_temp.tm_hour  = bcd_to_hex(buff[2]&SHIELD_PCF8563_HOUR);time_temp.tm_min   = bcd_to_hex(buff[1]&SHIELD_PCF8563_MIN);time_temp.tm_sec   = bcd_to_hex(buff[0]&SHIELD_PCF8563_SEC);*time = mktime(&time_temp);}break;case RT_DEVICE_CTRL_RTC_SET_TIME:{struct tm *time_new;time = (time_t *)args;time_new = localtime(time);buff[6] = hex_to_bcd(time_new->tm_year + 1900 - 2000);buff[5] = hex_to_bcd(time_new->tm_mon + 1);buff[3] = hex_to_bcd(time_new->tm_mday);buff[4] = hex_to_bcd(time_new->tm_wday+1);buff[2] = hex_to_bcd(time_new->tm_hour);buff[1] = hex_to_bcd(time_new->tm_min);buff[0] = hex_to_bcd(time_new->tm_sec);ret = pcf8563_write_reg(REG_PCF8563_SEC,buff,7);}break;default:break;}return RT_EOK;
}
  • “RT_DEVICE_CTRL_RTC_GET_TIME”“RT_DEVICE_CTRL_RTC_SET_TIME”分别是RTT定义的获取时间命令字和设置时间命令字。

  • RTT的RTC模型与Linux系统类似,将具体年月日换算成时间戳。

  • 关于年和月的处理:struct tm为标准C库定义的结构体,结构体中的“tm_year”(年份)是从1900年开始的,“tm_mon”(月份)范围是0—11,0表示1月;而PCF8563的年份值范围是0—99,月份值范围是1—12,因此要做相关处理。

  • “pcf8563_read_reg”“pcf8563_write_reg”是PCF8563读写寄存器函数,通过此两函数从寄存器获取时间或者设置(写入)时间。

  • 从PCF8563中获取的时间值为BCD码,故需编写“bcd_to_hex”转换函数将BCD码转换为十进制数;同理在设置时间前,需要将十进制数转换为BCD码,即“hex_to_bcd”函数,然后再写寄存器。两者函数源码如下。

/* bcd to hex */
static unsigned char bcd_to_hex(unsigned char data)
{unsigned char temp;temp = ((data>>4)*10 + (data&0x0f));return temp;
}/* hex_to_bcd */
static unsigned char hex_to_bcd(unsigned char data)
{unsigned char temp;temp = (((data/10)<<4) + (data%10));return temp;
}

3.2 PCF8563底层驱动

  PCF8563底层驱动即是通过i2c总线读写其寄存器,而i2c总线RTT已经统一好框架,通过RTT的标准框架调用i2c总线进行访问PCF8563。这里i2c总线框架与底层分离,i2c总线底层的实现则与具体cpu型号相关。

第一步,定义一个注册设备用的PCF8563结构体

struct pcf8563_device
{struct rt_device rtc_parent;struct rt_i2c_bus_device *i2c_device;
};
  • rtc_parent 是标准RTT驱动设备框架参数。
  • i2c_device 是i2c总线指针,通过该指针调用i2c总线,使用前必须初始化。

第二步,编写PCF8563读写函数

/* pcf8563 read register */
rt_uint8_t pcf8563_read_reg(rt_uint8_t reg,rt_uint8_t *data,rt_uint8_t data_size)
{struct rt_i2c_msg msg[2];msg[0].addr  = PCF8563_ARRD;msg[0].flags = RT_I2C_WR;msg[0].len   = 1;msg[0].buf   = &reg;msg[1].addr  = PCF8563_ARRD;msg[1].flags = RT_I2C_RD;msg[1].len   = data_size;msg[1].buf   = data;if(rt_i2c_transfer(pcf8563_dev.i2c_device, msg, 2) != 2)return RT_ERROR; return RT_EOK;
}
/* pcf8563 write register */
rt_err_t pcf8563_write_reg(rt_uint8_t reg, rt_uint8_t *data,rt_uint8_t data_size)
{struct rt_i2c_msg msg[2];msg[0].addr       = PCF8563_ARRD;msg[0].flags    = RT_I2C_WR;msg[0].len     = 1;msg[0].buf     = &reg;msg[1].addr     = PCF8563_ARRD;msg[1].flags    = RT_I2C_WR | RT_I2C_NO_START;msg[1].len       = data_size;msg[1].buf     = data;if (rt_i2c_transfer(pcf8563_dev.i2c_device, msg, 2) == 2){return RT_EOK;}else{rt_kprintf("i2c bus write failed!\r\n");return -RT_ERROR;}
}
  • 使用到RTT的i2c接口函数为“rt_i2c_transfer”,入口参数分别为:i2c设备指针、信息帧、帧数。
  • “struct rt_i2c_msg”是RTT定义的i2c信息帧,原型如下。
struct rt_i2c_msg
{rt_uint16_t addr;rt_uint16_t flags;rt_uint16_t len;rt_uint8_t  *buf;
};
  • addr,i2c器件地址,不包括读写位,如PCF8563的地址为0x51。
  • flags,标识位,有几种类型,包括读、写、10bit i2c地址选择、应答、非应答。
#define RT_I2C_WR                0x0000
#define RT_I2C_RD               (1u << 0)
#define RT_I2C_ADDR_10BIT       (1u << 2)  /* this is a ten bit chip address */
#define RT_I2C_NO_START         (1u << 4)
#define RT_I2C_IGNORE_NACK      (1u << 5)
#define RT_I2C_NO_READ_ACK      (1u << 6)  /* when I2C reading, we do not ACK */
  • len,数据长度。
  • buf,数据缓存指针(发送或者接收)。
  • 对于读函数,需要两帧信息,第一帧发送读寄存器地址数据,第二帧开始接收返回数据;对于写函数,同样需要两帧,第一帧发送写寄存器地址数据,第二帧发送待写入的数据。

3.3 初始化

  初始化包括两部分,一是指定i2c总线,PCF8563读写函数通过调用i2c总线进行访问;而是RTC设备初始化,上层应用从而可通过标准接口访问PCF8563。

/* pcf8563 device int  */
int rt_hw_pcf8563_init(void)
{       struct rt_i2c_bus_device *i2c_device;uint8_t data;i2c_device = rt_i2c_bus_device_find("i2c1");if (i2c_device == RT_NULL){#ifdef RT_USE_FINSH_DEBUGrt_kprintf("i2c bus device %s not found!\r\n", "i2c1");#endifreturn 1;}                  pcf8563_dev.i2c_device = i2c_device;/* register rtc device */pcf8563_dev.rtc_parent.type           = RT_Device_Class_RTC;pcf8563_dev.rtc_parent.init          = RT_NULL;pcf8563_dev.rtc_parent.open          = rt_pcf8563_open;pcf8563_dev.rtc_parent.close         = RT_NULL;pcf8563_dev.rtc_parent.read          = rt_pcf8563_read;pcf8563_dev.rtc_parent.write         = RT_NULL;pcf8563_dev.rtc_parent.control       = rt_pcf8563_control;pcf8563_dev.rtc_parent.user_data  = RT_NULL;         /* no private */rt_device_register(&pcf8563_dev.rtc_parent, "rtc", RT_DEVICE_FLAG_RDWR);/* init pcf8563 */data = 0x7f;   /* close clock out */if (pcf8563_write_reg(REG_PCF8563_CLKOUT, &data, 1) != RT_EOK){return -RT_ERROR;}return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_pcf8563_init);
  • 初始化最开始定义的“pcf8563_dev”,其中“rt_pcf8563_open”、“rt_pcf8563_read”只是空函数,没有实现内容;然后注册为“rtc”设备,RTT默认调用RTC的名称为“rtc”,采用统一命名,更换RTC芯片时保证该命名一致。
  • 调用RTT宏“INIT_DEVICE_EXPORT”完成初始化。

4 完整源码

#ifndef _DRV_PCF8563_H_
#define _DRV_PCF8563_H_#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>/* slave address */
#define     PCF8563_ARRD            0x51/* register */
#define     REG_PCF8563_STATE1      0x00
#define     REG_PCF8563_STATE2      0x01
#define     REG_PCF8563_SEC         0x02
#define     REG_PCF8563_MIN         0x03
#define     REG_PCF8563_HOUR        0x04
#define     REG_PCF8563_DAY         0x05
#define     REG_PCF8563_WEEK        0x06
#define     REG_PCF8563_MON         0x07
#define     REG_PCF8563_YEAR        0x08
#define     REG_PCF8563_CLKOUT      0x0d/* offset */
#define     SHIELD_PCF8563_STATE1   (unsigned char)0xa8
#define     SHIELD_PCF8563_STATE2   (unsigned char)0x1f
#define     SHIELD_PCF8563_SEC      (unsigned char)0x7f
#define     SHIELD_PCF8563_MIN      (unsigned char)0x7f
#define     SHIELD_PCF8563_HOUR     (unsigned char)0x3f
#define     SHIELD_PCF8563_DAY      (unsigned char)0x3f
#define     SHIELD_PCF8563_WEEK     (unsigned char)0x07
#define     SHIELD_PCF8563_MON      (unsigned char)0x1f
#define     SHIELD_PCF8563_YEAR     (unsigned char)0xffextern int rt_hw_pcf8563_init(void);#endif
/** File      : drv_pcf8563.c* This file is part of RT-Thread RTOS* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team**  This program is free software; you can redistribute it and/or modify*  it under the terms of the GNU General Public License as published by*  the Free Software Foundation; either version 2 of the License, or*  (at your option) any later version.**  This program is distributed in the hope that it will be useful,*  but WITHOUT ANY WARRANTY; without even the implied warranty of*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the*  GNU General Public License for more details.**  You should have received a copy of the GNU General Public License along*  with this program; if not, write to the Free Software Foundation, Inc.,*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.** Change Logs:* Date           Author       Notes* 2018-04-20     Acuity       the first version*/#include <board.h>
#include <rtdevice.h>
#include <time.h>
#include "drv_pcf8563.h"#define RT_USING_PCF8563#ifdef RT_USING_PCF8563       struct pcf8563_device
{struct rt_device rtc_parent;struct rt_i2c_bus_device *i2c_device;
};
static struct pcf8563_device pcf8563_dev;/* bcd to hex */
static unsigned char bcd_to_hex(unsigned char data)
{unsigned char temp;temp = ((data>>4)*10 + (data&0x0f));return temp;
}/* hex_to_bcd */
static unsigned char hex_to_bcd(unsigned char data)
{unsigned char temp;temp = (((data/10)<<4) + (data%10));return temp;
}/* pcf8563 read register */
rt_err_t pcf8563_read_reg(rt_uint8_t reg,rt_uint8_t *data,rt_uint8_t data_size)
{struct rt_i2c_msg msg[2];msg[0].addr  = PCF8563_ARRD;msg[0].flags = RT_I2C_WR;msg[0].len   = 1;msg[0].buf   = &reg;msg[1].addr  = PCF8563_ARRD;msg[1].flags = RT_I2C_RD;msg[1].len   = data_size;msg[1].buf   = data;if (rt_i2c_transfer(pcf8563_dev.i2c_device, msg, 2) == 2){return RT_EOK;}else{rt_kprintf("i2c bus write failed!\r\n");return -RT_ERROR;}
}/* pcf8563 write register */
rt_err_t pcf8563_write_reg(rt_uint8_t reg, rt_uint8_t *data,rt_uint8_t data_size)
{struct rt_i2c_msg msg[2];msg[0].addr       = PCF8563_ARRD;msg[0].flags    = RT_I2C_WR;msg[0].len     = 1;msg[0].buf     = &reg;msg[1].addr     = PCF8563_ARRD;msg[1].flags    = RT_I2C_WR | RT_I2C_NO_START;msg[1].len       = data_size;msg[1].buf     = data;if (rt_i2c_transfer(pcf8563_dev.i2c_device, msg, 2) == 2){return RT_EOK;}else{rt_kprintf("i2c bus write failed!\r\n");return -RT_ERROR;}
}static rt_err_t rt_pcf8563_open(rt_device_t dev, rt_uint16_t flag)
{if (dev->rx_indicate != RT_NULL){/* open interrupt */}return RT_EOK;
}static rt_size_t rt_pcf8563_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{return RT_EOK;
}static rt_err_t rt_pcf8563_control(rt_device_t dev, int cmd, void *args)
{rt_err_t   ret = RT_EOK;time_t        *time;struct tm     time_temp;  rt_uint8_t  buff[7];RT_ASSERT(dev != RT_NULL);rt_memset(&time_temp, 0, sizeof(struct tm));switch (cmd){case RT_DEVICE_CTRL_RTC_GET_TIME:time = (time_t *)args;ret = pcf8563_read_reg(REG_PCF8563_SEC,buff,7);if(ret == RT_EOK){time_temp.tm_year  = bcd_to_hex(buff[6]&SHIELD_PCF8563_YEAR) + 2000 - 1900;time_temp.tm_mon   = bcd_to_hex(buff[5]&SHIELD_PCF8563_MON) - 1;time_temp.tm_mday  = bcd_to_hex(buff[3]&SHIELD_PCF8563_DAY);time_temp.tm_hour  = bcd_to_hex(buff[2]&SHIELD_PCF8563_HOUR);time_temp.tm_min   = bcd_to_hex(buff[1]&SHIELD_PCF8563_MIN);time_temp.tm_sec   = bcd_to_hex(buff[0]&SHIELD_PCF8563_SEC);*time = mktime(&time_temp);}break;case RT_DEVICE_CTRL_RTC_SET_TIME:{struct tm *time_new;time = (time_t *)args;time_new = localtime(time);buff[6] = hex_to_bcd(time_new->tm_year + 1900 - 2000);buff[5] = hex_to_bcd(time_new->tm_mon + 1);buff[3] = hex_to_bcd(time_new->tm_mday);buff[4] = hex_to_bcd(time_new->tm_wday+1);buff[2] = hex_to_bcd(time_new->tm_hour);buff[1] = hex_to_bcd(time_new->tm_min);buff[0] = hex_to_bcd(time_new->tm_sec);ret = pcf8563_write_reg(REG_PCF8563_SEC,buff,7);}break;default:break;}return RT_EOK;
}/* pcf8563 device int  */
int rt_hw_pcf8563_init(void)
{       struct rt_i2c_bus_device *i2c_device;uint8_t data;i2c_device = rt_i2c_bus_device_find("i2c1");if (i2c_device == RT_NULL){#ifdef RT_USE_FINSH_DEBUGrt_kprintf("i2c bus device %s not found!\r\n", "i2c1");#endifreturn 1;}                  pcf8563_dev.i2c_device = i2c_device;/* register rtc device */pcf8563_dev.rtc_parent.type           = RT_Device_Class_RTC;pcf8563_dev.rtc_parent.init          = RT_NULL;pcf8563_dev.rtc_parent.open          = rt_pcf8563_open;pcf8563_dev.rtc_parent.close         = RT_NULL;pcf8563_dev.rtc_parent.read          = rt_pcf8563_read;pcf8563_dev.rtc_parent.write         = RT_NULL;pcf8563_dev.rtc_parent.control       = rt_pcf8563_control;pcf8563_dev.rtc_parent.user_data  = RT_NULL;         /* no private */rt_device_register(&pcf8563_dev.rtc_parent, "rtc", RT_DEVICE_FLAG_RDWR);/* init pcf8563 */data = 0x7f;   /* close clock out */if (pcf8563_write_reg(REG_PCF8563_CLKOUT, &data, 1) != RT_EOK){return -RT_ERROR;}return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_pcf8563_init);
#endif

RT-Thread驱动——RTC PCF8563相关推荐

  1. xpt 2046的触摸屏 rt thread设备驱动框架

    1 基于rtt 开发触摸屏驱动 准备使用rtt 框架 , 驱动xpt 2046的触摸屏, 翻阅大量资料发现, 大部分文章强调的是时序图, 而且很多代码要么直接操作寄存器, 要么是io 口模拟, 只能用 ...

  2. rt thread studio使用QBOOT和片外flash实现OTA升级

    我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...

  3. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

  4. 基于rt thread smart构建EtherCAT主站

    我把源码开源到到了gitee,https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开.rt thre ...

  5. rt thread系统下添加wiznet软件包后,不插网线CPU利用率100%问题

    rt thread系统下添加wiznet软件包后如果不插网线的话其他任务运行很卡,使用ps命令发现优先级低的任务很多都超时了 rt thread线程错误码 添加了一个可以查看CPU利用率的软件包CPU ...

  6. stm32f407单片机rt thread 片外spi flash OTA升级配置示例

    参考地址https://www.rt-thread.org/document/site/application-note/system/rtboot/an0028-rtboot/ 第一步,生成Boot ...

  7. 关于RT thread系统节拍时钟的配置

    关于RT thread系统节拍时钟的配置                  -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...

  8. rt thread 使用FAL遇到fal_init() undefined reference

    rt thread FAL 0.5版,之前有没有不知道,遇到一个坑. 在main.cpp里面已经 #include <fal.h> fal_init() 编译报错,说 fal_init() ...

  9. RT Thread Free Modbus移植问题整理

    RT Thread Free Modbus移植问题整理 问题描述: 在读写寄存器中,写数据正常,只能读1个寄存器的值,多个值会异常. 在移植过程中发现串口(或RS485)数据接收长度异常. 一.环境描 ...

最新文章

  1. 深入TCP/IP协议1---图解OSI参考模型
  2. 26张图带你彻底搞懂volatile关键字
  3. python对象编程例子-Python3.5面向对象编程图文与实例详解
  4. Linux学习之系统编程篇:互斥锁(pthread_mutex_init / lock / trylock / unlock / destroy)
  5. Luogu2439 [SDOI2005]阶梯教室设备利用 (动态规划)
  6. java代码如何删除文件_Java如何删除文件和目录代码? 爱问知识人
  7. 使用app-inspector时报错connect ECONNREFUSED 127.0.0.1:8001的解决方案
  8. 软件工程 第二章 可行性研究
  9. 【车牌识别】基于matlab GUI模板匹配车牌库识别【含Matlab源码 416期】
  10. 怎么升级计算机的操作系统,电脑如何升级系统版本_Windows10/7电脑升级系统版本的操作步骤...
  11. 开机引导界面grub找不到
  12. Cisco(思科)无线路由器
  13. 超分算法在 WebRTC 高清视频传输弱网优化中的应用
  14. EasyBoot制作启动光盘教程
  15. 设计模式 -- 访问者模式(Visitor)
  16. 交接读代码得过程和整理文档
  17. Squid反向代理加速WEB
  18. python做日历牌,怎么使用python tkinter制作日历?
  19. 2018三七互娱前端笔试
  20. Jetson 相机编码

热门文章

  1. 什么是Kodu---Kodu少儿编程第一天
  2. 装X神器微信朋友圈小尾巴
  3. 让我们一起为宝宝选绘本(怎么给宝宝挑选绘本,看完本文就够了)
  4. 在 Windows10 系统中安装 Homestead 本地开发环境
  5. 配音兼职平台有那些,配音兼职靠谱的平台?
  6. 社会对计算机专业学生的需求,关于计算机专业社会人才需求调查报告
  7. vue项目创建之后 ESLint导致第一行毛毛虫
  8. Redis源码初探(1)简单动态字符串SDS
  9. win11系统中如何把任务栏图标变小的方法
  10. 2414905-34-1,Thalidomide-O-PEG5-Tosyl它与亲核物质如胺、含羟基的分子反应