微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 单片机编程之 - 程序模块化及可复用性(1)转载资料!

单片机编程之 - 程序模块化及可复用性(1)转载资料!

时间:10-02 整理:3721RD 点击:
转载一个网上资料---electrlife网友的资料,觉得不错,发一下!

单片机编程之 - 程序模块化及可复用性(一)
关于这个贴子的原由请查看http://www.amobbs.com/thread-5580903-1-1.html
另外申明:技术只是实现产品的一种工具,因此你应该花更多的时间去关注产品的本身!
    当你接触单片机编程时,我想模块化,可复用,面象对象你应该都听说过吧!对于像我们这些
电工出生的来说,那些所谓的软件工程、设计模式等可能比较晦涩难懂,甚至是天书,至少对于我
来说是这样的!这里我想讲的模块化,和可复用性和纯软件工程是不可比的,不是一个数量级,
呵呵!我只说说我对这些的理解!因为模块化和可复用性就我个人而言,目前认为是一意思,也即
模块化是为了最大限度的可复用,而可复用的代码的实现方法之一就是模块化,哈哈有点绕啊!
    在谈模块化和可复用性之前,先假设一下应用环境,因为这几年写程序都是基于RTOS模式来
写的,对于RTOS我这里多说两句,有些工程师可能不太愿意使用RTOS,他们觉得裸机程序更加效率、
写好了也确实很优雅,但就我个人而言,我是强烈建议学习和理解RTOS的,就算你不使用,RTOS的
一些思想也是可以帮助对编程有更深一步的认识,因此以后谈及的所有程序代码也都会考虑在RTOS下
运行所需要的互斥处理,即临界段问题。如果你是裸机编程那也没关系,因为理解临界段问题对
你来说也不是什么坏事!后面我会在“单片机编程之-RTOS临界段处理” 中再详细说下临界段的问题!
再者为了抓住重点,强调思想与结构,因此叙述中所出现的代码将不会作为详细的注解。
    如果你接触过C++或类似面向对象的语言的话,你会面向对象这个概念无孔不入,就连单片机编程的书籍
也会提到或是介绍。面向对象是一个很大很得复杂的概念,但也可以是一个很简单的东西,就像下面使用
一样:
struct my_is_obj {
  char *name;
  int type;
};
可以认为struct my_is_obj就是一个对象,简单吧,但对应的实际上如何使用呢,那我们就以IIC的使用来
说明:
    IIC顾名思义,在单片机编程中IIC可以说基本都会用的一个总线,比如EEPROM其接口大部分都是基于IIC,当然
也有SPI接口的。现在的音片机基本上都带有支持IIC协议的控制器,也即硬件IIC,但可能是我个人比较懒不喜欢
去读每种MCU的IIC控制器的寄存器,使用方式等等,我希望我的程序写过一次后就可以重复使用不用再写第二次,
你懒吧!所以软件形式的IIC就成了我的最爱!
程序模块化及可复用性的原则一:面像接口编程
程序模块化及可复用性的原则二:硬件的抽象接口尽量通用简单
所谓的面像接口编程,简单的理解就是在你写程序前,把所需要实现的东西看成一个具有一些数据与函数操作的集合,
并认真抽象出其函数原型,这话听着简单,可是如果要想抽象出一个比较合理且全面的函数接口谈何容易,你看LINUX
的驱动框架中那些函数指针,你就能感受到了!对于软件IIC的实现可抽象如下所示:
  • struct i2c_bus {
  •     void (*bus_sda)(const struct i2c_bus *i2c, int level);
  •     void (*bus_scl)(const struct i2c_bus *i2c, int level);
  •     int (*read_sda)(const struct i2c_bus *i2c);
  •     unsigned long speed_hz;
  •     unsigned long ack_timeout_us;
  •     void *priv;
  • };

[color=rgb(51, 102, 153) !important]复制代码

struct i2c_bus 里定义了IIC的底层操作函数指针及一些描述IIC的数据,因些通过struct i2c_bus结构体就能
很好把硬件相关的IO操作和上层IIC协议逻辑操作隔离开来,而具体的IIC提供的函数接口如下所示:
  • void i2cbus_send_start(const struct i2c_bus *i2c);
  • void i2cbus_send_stop(const struct i2c_bus *i2c);
  • void i2cbus_send_noack(const struct i2c_bus *i2c);
  • void i2cbus_send_ack(const struct i2c_bus *i2c);
  • char i2cbus_wait_ack(const struct i2c_bus *i2c);
  • void i2cbus_send_byte(const struct i2c_bus *i2c, char c);
  • char i2cbus_recv_byte(const struct i2c_bus *i2c);

[color=rgb(51, 102, 153) !important]复制代码

这里给出了IIC协议里最常用的几个操作,其具体的实现如下:
  • #include <drivers/softi2c_bus.h>
  • /**
  • * @brief  None.
  • *
  • * @param  us
  • * @Return None.
  • * @NOTE   None.
  • */
  • static void udelay(unsigned long us)
  • {
  •     volatile unsigned long delay = 10;
  •     while (us) {
  •         for (unsigned long i = 0; i < delay; i++) {
  •             ;
  •         }
  •         us--;
  •     }
  • }
  • /**
  • * @brief  None.
  • *
  • * @note   None.
  • */
  • static void i2cbus_delay(const struct i2c_bus *i2c)
  • {
  •     volatile unsigned long us = (1000 * 1000 ) / i2c->speed_hz;
  •     if (us == 0) {
  •         us = 10;
  •     }
  •     udelay(us);
  • }
  • /**
  • * @brief  None.
  • *
  • * @note   None.
  • */
  • void i2cbus_send_start(const struct i2c_bus *i2c)
  • {
  •     i2c->bus_scl(i2c, 0);
  •     i2cbus_delay(i2c);
  •     i2c->bus_sda(i2c, 1);
  •     i2cbus_delay(i2c);
  •     i2c->bus_scl(i2c, 1);
  •     i2cbus_delay(i2c);
  •     i2c->bus_sda(i2c, 0);
  •     i2cbus_delay(i2c);
  • }
  • /**
  • * @brief  sclk为高时sdat的上升沿表示“停止.
  • *
  • * @note   None.
  • */
  • void i2cbus_send_stop(const struct i2c_bus *i2c)
  • {
  •     i2c->bus_scl(i2c, 0);
  •     i2cbus_delay(i2c);
  •     i2c->bus_sda(i2c, 0);
  •     i2cbus_delay(i2c);
  •     i2c->bus_scl(i2c, 1);
  •     i2cbus_delay(i2c);
  •     i2c->bus_sda(i2c, 1);
  •     i2cbus_delay(i2c);
  • }
  • /**
  • * @brief  不拉低数据线,即不给于应答.
  • *
  • * @note   None.
  • */
  • void i2cbus_send_noack(const struct i2c_bus *i2c)
  • {
  •     i2c->bus_scl(i2c, 0);
  •     i2cbus_delay(i2c);
  •     i2c->bus_sda(i2c, 1);
  •     i2cbus_delay(i2c);
  •     i2c->bus_scl(i2c, 1);
  •     i2cbus_delay(i2c);
  •     i2cbus_delay(i2c);
  • }
  • /**
  • * @brief  拉低数据线,即给于应答.
  • *
  • * @param  i2c
  • * @return None.
  • * @note   None.
  • */
  • void i2cbus_send_ack(const struct i2c_bus *i2c)
  • {
  •     i2c->bus_scl(i2c, 0);
  •     i2cbus_delay(i2c);
  •     i2c->bus_sda(i2c, 0);   //拉低数据线,即给于应答
  •     i2cbus_delay(i2c);
  •     i2c->bus_scl(i2c, 1);
  •     i2cbus_delay(i2c);
  •     i2cbus_delay(i2c);
  • }
  • /**
  • * @brief  None.
  • *
  • * @param  i2c
  • * @return
  • * @note   None.
  • */
  • char i2cbus_wait_ack(const struct i2c_bus *i2c)
  • {
  •     char sda;
  •     unsigned long ack_timeout = i2c->ack_timeout_us;
  •     i2c->bus_scl(i2c, 0);
  •     i2cbus_delay(i2c);
  •     i2c->bus_sda(i2c, 1);   //释放数据线
  •     i2cbus_delay(i2c);
  •     i2c->bus_scl(i2c, 1);
  •     i2cbus_delay(i2c);
  •     /* 数据线未被拉低,即未收到应答 */
  •     while ((sda = i2c->read_sda(i2c)) && ack_timeout) {
  •         ack_timeout--;
  •         udelay(1);
  •     }
  •     /* 数据线被拉低,即收到应答 */
  •     return (sda ? 0 : 1);
  • }
  • /**
  • * @brief  None.
  • *
  • * @param  i2c
  • * @param  c
  • * @return None.
  • * @note   None.
  • */
  • void i2cbus_send_byte(const struct i2c_bus *i2c, char c)
  • {
  •     for (char i = 0; i < 8; i++) {
  •         i2c->bus_scl(i2c, 0);
  •         i2cbus_delay(i2c);
  •         if (c & 0x80) {
  •             i2c->bus_sda(i2c, 1);
  •         } else {
  •             i2c->bus_sda(i2c, 0);
  •         }
  •         c <<= 1;
  •         i2cbus_delay(i2c);
  •         i2c->bus_scl(i2c, 1);
  •         i2cbus_delay(i2c);
  •     }
  • }
  • /**
  • * @brief  None.
  • *
  • * @param  i2c
  • * @return
  • * @note   None.
  • */
  • char i2cbus_recv_byte(const struct i2c_bus *i2c)
  • {
  •     char c = 0;
  •     for (char i = 0; i < 8; i++) {
  •         i2c->bus_scl(i2c, 0);
  •         i2cbus_delay(i2c);
  •         i2c->bus_sda(i2c, 1);
  •         i2cbus_delay(i2c);
  •         i2c->bus_scl(i2c, 1);
  •         i2cbus_delay(i2c);
  •         c <<= 1;
  •         c |= i2c->read_sda(i2c) ? 0x01 : 0x00;
  •     }
  •     return c;
  • }

[color=rgb(51, 102, 153) !important]复制代码

    有了上述的软件IIC代码,那现在就可以和实际的器件相连系了,如EEPROM。再讨论EEPROM之前,首先我们
需要注意一下EEPROM的性质,
性质1:PAGE写功能,即EEPROM器件可以一次写入一页的数据,但进行PAGE写时需要页对齐
性质2:EEPROM每次编程时需要等待10MS左右
    EEPROM可以说是我写程序当中都会用到的一个器件,因此对于EEPROM的代码
我想是只写一次那以后只要COPY+C就可以了,因此对于EEPROM我们也需要进行抽象处理,下面以24LCXX为例:
  • struct dev_24lcxx {
  •     unsigned int slave_addr;
  •     unsigned int cmd_rd;
  •     unsigned int cmd_wr;
  •     unsigned int page_size;
  •     unsigned int page_num;
  •     const struct i2c_bus *i2c_bus;
  •     void *priv;
  • };

[color=rgb(51, 102, 153) !important]复制代码

对于以上的结构体成员,我想大家应该不陌生吧,如果你陌生建议去看下EEPROM手册,:-)!
const struct i2c_bus *i2c_bus;
这个成员眼熟吧,对,就是上面的软件IIC的抽象,现在看明白EEPROM是如何和IIC连系了的吧!
通过IIC的抽象,我们就彻底把EEPROM的操作和硬件分离开来了,从EEPROM的角度看EEPROM只依赖
于IIC,因此当我更改硬件时,我们只要重新赋值一个新的IIC即可。
而对于EEPROM的操作函数抽象如下:
  • int dev_24lcxx_write(const struct dev_24lcxx *dev, unsigned long offset_addr,
  •                      const char *buffer, uint32_t n_byte);
  • int dev_24lcxx_read (const struct dev_24lcxx *dev, unsigned long offset_addr,
  •                      char *buffer, uint32_t n_byte);

[color=rgb(51, 102, 153) !important]复制代码

这里说明下,以后不注明所有的函数成功则返回0,失败则返回非0。其函数的实现如下所示,
整个实现不难理解,唯一难懂的可能就是PAGE写的问题!但这不是本文的重点,你只要明白
以下实现都是通过const struct i2c_bus *i2c_bus接口来操作具体硬件的即可!
  • #include <drivers/dev_24lcxx_softi2c.h>
  • /**
  • * @brief  向EEPROM中写入一页数据,此函数不用考虑页对齐,而由调用者处理页对齐问题.
  • *
  • * @param  dev
  • * @param  buffer 指向写入的缓冲区
  • * @param  write_addr EEPROM基地址
  • * @param  n_byte 写入的字节数量
  • * @return
  • * @note   None.
  • */
  • static int write_page(const struct dev_24lcxx *dev, const char *buffer,
  •                       uint16_t write_addr, uint16_t n_byte)
  • {
  •     uint8_t addr_hi, addr_low, cmd_wr, slave_addr;
  •     cmd_wr = (uint8_t)dev->cmd_wr;
  •     slave_addr = (uint8_t)dev->slave_addr;
  •     addr_hi  = (write_addr >> 8) & 0x00FF;
  •     addr_low =  write_addr & 0x00FF;
  •     i2cbus_send_start(dev->i2c_bus);
  •     // Send SLAVE addr with write request
  •     i2cbus_send_byte(dev->i2c_bus, slave_addr | cmd_wr);
  •     if (!i2cbus_wait_ack(dev->i2c_bus)) {
  •         i2cbus_send_stop(dev->i2c_bus);
  •         return -1;
  •     }
  •     // Send memory addr
  •     i2cbus_send_byte(dev->i2c_bus, addr_hi);
  •     if (!i2cbus_wait_ack(dev->i2c_bus)) {
  •         i2cbus_send_stop(dev->i2c_bus);
  •         return -1;
  •     }
  •     i2cbus_send_byte(dev->i2c_bus, addr_low);
  •     if (!i2cbus_wait_ack(dev->i2c_bus)) {
  •         i2cbus_send_stop(dev->i2c_bus);
  •         return -1;
  •     }
  •     // Send SendData to memory addr
  •     for (uint16_t i = 0; i < n_byte; i++) {
  •         i2cbus_send_byte(dev->i2c_bus, buffer);
  •         if (!i2cbus_wait_ack(dev->i2c_bus)) {
  •             i2cbus_send_stop(dev->i2c_bus);
  •             return -1;
  •         }
  •     }
  •     i2cbus_send_stop(dev->i2c_bus);
  •     // 这里使用延时10MS来完成页写入操作
  •     OS_ERR os_err;
  •     OSTimeDlyHMSM(0, 0, 0, 10, 0, &os_err);
  •     return 0;
  • }
  • /**
  • * @brief  向EEPROM中写入数据,并处理页对齐问题.
  • *
  • * @param  dev
  • * @param  buffer 指向写入的缓冲区
  • * @param  write_addr EEPROM基地址
  • * @param  n_byte 写入的字节数量
  • * @return
  • * @note   None.
  • */
  • static int write_buffer(const struct dev_24lcxx *dev, const char *buffer,
  •                         uint16_t write_addr, uint16_t n_byte)
  • {
  •     int r = 0;
  •     uint16_t n_page = 0, n_single = 0, counter = 0;
  •     uint16_t addr = 0;
  •     unsigned int page_size;
  •     page_size = dev->page_size;
  •     addr     = write_addr % page_size;
  •     counter  = page_size - addr;
  •     n_page   = n_byte / page_size;
  •     n_single = n_byte % page_size;
  •     // 此时地址是对齐的,所以可以直接使用页写就可以
  •     /*!< If write_addr is page_size aligned  */
  •     if (addr == 0) {
  •         /*!< If n_byte < page_size */
  •         if (n_page == 0) {
  •             /* Start writing data */
  •             if (write_page(dev, buffer, write_addr, n_single) < 0) {
  •                 return -1;
  •             }
  •         } else {
  •             /*!< If n_byte > page_size */
  •             while (n_page--) {
  •                 /* Store the number of data to be written */
  •                 if (write_page(dev, buffer, write_addr, page_size) < 0) {
  •                     return -1;
  •                 }
  •                 write_addr += page_size;
  •                 buffer   += page_size;
  •             }
  •             if (n_single != 0) {
  •                 if (write_page(dev, buffer, write_addr, n_single) < 0) {
  •                     return -1;
  •                 }
  •             }
  •         }
  •     } else {
  •         /*!< If write_addr is not page_size aligned  */
  •         /*!< If n_byte < page_size */
  •         if (n_page == 0) {
  •             /*!< If the number of data to be written is more than the remaining space
  •             in the current page: */
  •             if (n_byte > counter) {
  •                 /*!< Write the data conained in same page */
  •                 if (write_page(dev, buffer, write_addr, counter) < 0) {
  •                     return -1;
  •                 }
  •                 /*!< Write the remaining data in the following page */
  •                 if (write_page(dev, buffer + counter, (write_addr + counter), (n_byte - counter)) < 0) {
  •                     return -1;
  •                 }
  •             } else {
  •                 if (write_page(dev, buffer, write_addr, n_single) < 0) {
  •                     return -1;
  •                 }
  •             }
  •         } else {
  •             /*!< If n_byte > page_size */
  •             n_byte  -= counter;
  •             n_page   = n_byte / page_size;
  •             n_single = n_byte % page_size;
  •             if (counter != 0) {
  •                 if (write_page(dev, buffer, write_addr, counter) < 0) {
  •                     return -1;
  •                 }
  •                 write_addr += counter;
  •                 buffer   += counter;
  •             }
  •             while (n_page--) {
  •                 if (write_page(dev, buffer, write_addr, page_size) < 0) {
  •                     return -1;
  •                 }
  •                 write_addr += page_size;
  •                 buffer   += page_size;
  •             }
  •             if (n_single != 0) {
  •                 if (write_page(dev, buffer, write_addr, n_single) < 0) {
  •                     return -1;
  •                 }
  •             }
  •         }
  •     }
  •     return r;
  • }
  • /**
  • * @brief  向EEPROM中读出数据.
  • *
  • * @param  dev
  • * @param  buffer 指向读出的缓冲区
  • * @param  read_addr EEPROM基地址
  • * @param  n_byte 读出的字节数量
  • * @return
  • * @note   None.
  • */
  • static int read_buffer(const struct dev_24lcxx *dev, char *buffer,
  •                        uint16_t read_addr, uint16_t n_byte)
  • {
  •     uint8_t addr_hi, addr_low, cmd_rd, cmd_wr, slave_addr;
  •     cmd_rd = (uint8_t)dev->cmd_rd;
  •     cmd_wr = (uint8_t)dev->cmd_wr;
  •     slave_addr = (uint8_t)dev->slave_addr;
  •     addr_hi  = (read_addr >> 8) & 0x00FF;
  •     addr_low =  read_addr & 0x00FF;
  •     i2cbus_send_start(dev->i2c_bus);
  •     // Send SLAVE addr with write request
  •     i2cbus_send_byte(dev->i2c_bus, slave_addr | cmd_wr);
  •     if (!i2cbus_wait_ack(dev->i2c_bus)) {
  •         i2cbus_send_stop(dev->i2c_bus);
  •         kprintf("i2cbus NO ack line %u", __LINE__);
  •         return -1;
  •     }
  •     // Send memory addr
  •     i2cbus_send_byte(dev->i2c_bus, addr_hi);
  •     if (!i2cbus_wait_ack(dev->i2c_bus)) {
  •         i2cbus_send_stop(dev->i2c_bus);
  •         kprintf("i2cbus NO ack line %u", __LINE__);
  •         return -1;
  •     }
  •     i2cbus_send_byte(dev->i2c_bus, addr_low);
  •     if (!i2cbus_wait_ack(dev->i2c_bus)) {
  •         i2cbus_send_stop(dev->i2c_bus);
  •         kprintf("i2cbus NO ack line %u", __LINE__);
  •         return -1;
  •     }
  •     i2cbus_send_start(dev->i2c_bus);
  •     i2cbus_send_byte(dev->i2c_bus, slave_addr | cmd_rd);
  •     while (!i2cbus_wait_ack(dev->i2c_bus)) {
  •         i2cbus_send_stop(dev->i2c_bus);
  •         kprintf("i2cbus NO ack line %u", __LINE__);
  •         return -1;
  •     }
  •     buffer[0] = i2cbus_recv_byte(dev->i2c_bus);
  •     for (uint16_t i = 1; i < n_byte; i++) {
  •         i2cbus_send_ack(dev->i2c_bus);
  •         buffer = i2cbus_recv_byte(dev->i2c_bus);
  •     }
  •     i2cbus_send_stop(dev->i2c_bus);
  •     return 0;
  • }
  • /**
  • * @brief  None.
  • *
  • * @param  dev
  • * @param  offset_addr
  • * @param  buffer
  • * @param  n_byte
  • * @return
  • * @note   None.
  • */
  • int dev_24lcxx_write(const struct dev_24lcxx *dev, unsigned long offset_addr,
  •                      const char *buffer, uint32_t n_byte)
  • {
  •     unsigned int page_size, page_num;
  •     page_size = dev->page_size;
  •     page_num  = dev->page_num;
  •     if (offset_addr > ((page_num * page_size) - 1)) {
  •         return -1;
  •     }
  •     if (!buffer) {
  •         return -1;
  •     }
  •     if ((offset_addr + n_byte) > ((page_num * page_size) - 1)) {
  •         return -1;
  •     }
  •     return write_buffer(dev, buffer, offset_addr, n_byte);
  • }
  • /**
  • * @brief  None.
  • *
  • * @param  dev
  • * @param  offset_addr
  • * @param  buffer
  • * @param  n_byte
  • * @return
  • * @note   None.
  • */
  • int dev_24lcxx_read (const struct dev_24lcxx *dev, unsigned long offset_addr,
  •                      char *buffer, uint32_t n_byte)
  • {
  •     unsigned int page_size, page_num;
  •     page_size = dev->page_size;
  •     page_num  = dev->page_num;
  •     if (offset_addr > ((page_num * page_size) - 1)) {
  •         return -1;
  •     }
  •     if (!buffer) {
  •         return -1;
  •     }
  •     if ((offset_addr + n_byte) > ((page_num * page_size) - 1)) {
  •         return -1;
  •     }
  •     return read_buffer(dev, buffer, offset_addr, n_byte);
  • }

[color=rgb(51, 102, 153) !important]复制代码

     如果你认真看完上述代码,或许你会有这样的疑问,怎么没有初始化函数,对的,确实没有初始化函数,
一般的程序编写方式都会有一个初始化的函数接口,这里之所以没有是因为关于初始化的问题,我也有
自己的一套方法,会开一个专题进行讨论。这里你只需知道其实硬件的初始化工作已经在这之前完成了。
     有了上述的代码,接下来是要看看如何使用了,一般在项目中我会按如下方式使用:
     
  • #include <drivers/softi2c_bus.h>
  • #include <drivers/dev_24lcxx_softi2c.h>
  • #define EE_ADDR             0x00
  • #define EE_CMD_RD           0xA1
  • #define EE_CMD_WR           0xA0
  • #define EE_PAGESIZE         128
  • #define EE_PAGENUM          512
  • static void bus_sda(const struct i2c_bus *i2c, int level)
  • {
  •     if (level) {
  •     // 硬件操作
  •     } else {
  •     // 硬件操作
  •     }
  • }
  • static void bus_scl(const struct i2c_bus *i2c, int level)
  • {
  •     if (level) {
  •         // 硬件操作
  •     } else {
  •         // 硬件操作
  •     }
  • }
  • static int read_sda(const struct i2c_bus *i2c)
  • {
  •     uint8_t c = (// 硬件操作) ? 1 : 0;
  •     return c;
  • }
  • const struct i2c_bus ee24lc512_i2cbus = {
  •     .bus_sda        = bus_sda,
  •     .bus_scl        = bus_scl,
  •     .read_sda       = read_sda,
  •     .speed_hz       = 400000,
  •     .ack_timeout_us = 5,
  •     .priv           = 0,
  • };
  • const struct dev_24lcxx dev_ee24lc512 = {
  •     .slave_addr = EE_ADDR,
  •     .cmd_rd     = EE_CMD_RD,
  •     .cmd_wr     = EE_CMD_WR,
  •     .page_size  = EE_PAGESIZE,
  •     .page_num   = EE_PAGENUM,
  •     .i2c_bus    = &ee24lc512_i2cbus,
  •     .priv       = 0,
  • };

[color=rgb(51, 102, 153) !important]复制代码

看到这儿,你应该会明白了吧,其实面象对象,也可以如此简单!有了const struct dev_24lcxx dev_ee24lc512对象,
我们就可以使用dev_24lcxx_write接口对其进行读写了!通过以上的代码演示,我们可以总结出写模块化及可复用的程序
几点原则:
1、面象接口编程
a) 即当你在写一个设备的驱动程序时,你应该首先想下提供给用户的函数接口是怎么样的,
b) 其接口应尽量通用且统一,而且接口尽量简单
2、硬件的抽象接口尽量通用简单
a) 抽象其与硬件相关的接口,也应简单明了,把这些接口和数据通过struct组合起来
或许到这里,还不能完全体现其可复用性,后续讲解中,你会慢慢发现!也许对于初学者来说
到这里已经觉得不错了,以后我也会写EEPROM的程序了,至少曾经我是这样认为的,呵呵!其实要想写
好EEPROM程序,还差的远,而EEPROM的可复用性还差的远!如果你认为你已经会了,请考虑以下问题:
1、EEPROM一般做什么使用
2、EEPROM的操作如何在多任务中使用
3、如何避免错误程序的多次误写(EEPROM有擦除次数的)
4、EEPROM如何考虑写时掉电,且如何识别错误并恢复
5、如何提高EEPROM的写效率,即那写的10MS延时
其实关于EEPROM的内容,接下来的讲述中会对EEPROM的操作进行全面的描述,
通过一个EEPROM的操作实例来阐述程序的模块化及可复用性。

看来这是大多数电工追求的。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top