14193|57

7815

帖子

57

TA的资源

裸片初长成(中级)

楼主
 

一个结构 更加开放自由的 12864模块 [复制链接]

 
     写一个12864是一种很简单的事情,网上的例程一抓一大把。但是,如果你要根据你的MCU请选择不同的例程。受写uLib(前身是uS)的影响,我在写12864的时候,尽可能使其独立于单片机。
     随着不断整理,最后,我发现我习惯了一种以 结构体封装对象 的方式来实现各种各样的模块,不管是硬件模块还是纯粹软件模块都一样。

     这种方式和c++里,设计类的方式很接近,只是比起类,结构体在有些地方没有那么方便,因此略有点不爽,但这无关要紧,最重要是 面向对象 这种封装方式,在某种程度上对程序,特别是结构的容易理解有很显著的改善。

     有很多人,很简单地把 “面向对象” 和 类封装对象 这种已经很强大的编程思维 等同。我在一些面向对象的书里看到很多提醒,类封装虽然是面向对象的一个形式,但面向对象是一个很深邃的概念,并没有那么简单,随着面向对象编程的实践不断深入才能够加强理解。
     这里不再往下说,回到正题,给我,给你们看源码。

     下面展示一下这个过程,现在是直接看到结果,没有看到我一路改变封装的过程,不过鉴于以前uS的直播过程,这个过程也没太大意义,所以现在也不管顺序了,按照以下的顺序来叙述即可。代码也附在其中
      既然是 面向对象,我们就要首先 了解对象—— 什么是对象,什么可以作为对象。对象包含什么。
此帖出自编程基础论坛

最新回复

楼主把模块封装起来,移植起来就方便了。   详情 回复 发表于 2020-4-12 18:00
点赞 关注(6)
个人签名

强者为尊,弱者,死无葬身之地

 

回复
举报

7815

帖子

57

TA的资源

裸片初长成(中级)

推荐
 
在前面注册IO的时候,我们只要在main()里进行一个简单的调用,就可以把这个具体的IO连线
和一个特定的12864驱动,绑定到一起。
  1. void main(void)
  2. {
  3.   LCD12864 screen;
  4.   screen = Para_80_Driver();
  5. }
复制代码

此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

推荐
 
到现在这里为止,我们已经完成了 12864的IO驱动。
在我们继续进入 设置屏,写屏以前,我们先来试试怎么使用 已经设计好的结构。

其实,真实的设计过程,我是一边设计,一边试着考虑我会怎么用,怎么用方便来不断改进这个设计的。
所以,我们不管设计什么实现,都要不断从调用者的角度考虑问题

我们应该怎么设计,才能让它要做的事情最简单,最少,要关心的细节最少?

此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

推荐
 
同理,我们可以添加串行方式的驱动结构体
但是这里,我们不重复给出(最后我会给出这个源码的完整版本,rar压缩包或者git clone地址。)

我们接着思考的一个问题,我们现在有了 80并行时序,还有串行时序,将来我们还会有68并行时序,但我们不想一个时序一套驱动,我们希望在一个更高的层次,统一这套驱动。
毕竟,从对象的角度出发,这三个也同属12864的固有属性和选项。

有时候,就像现在,我们突然不知道接下去该怎么做,我们可以换一个角度思考问题:
我用起来要怎么用呢?

在这里,我们的提法变成了
我要提供最小一个什么信息集给结构体,才可以让它选择合适的驱动方式结构体,并完成所有我需要的工作?

我想了想,其实很简单。
我只需要告诉它我要选择哪一种方式,然后我期待,我只需要提供这组驱动,我就无须再做别的。

这个时候,我们得到这个层次或者这个总的12864驱动结构体的 输入和输出。

输入是
1.选择的驱动方式;
2.对应的一组驱动函数;

输出是
“我们需要的东西"——因为此刻我们仍然不十分确定,我们需要这个东西是一种什么存在。

但至少我们知道了输入。这就可以设计一个更高层次的 12864驱动结构体了。

  1. typedef struct
  2. {
  3.    pVoidFunVoid initial;
  4.    pVoidFunU8   SCK;
  5.    pVoidFunU8   SDA;
  6.    pVoidFunU8   CS;
  7.    pVoidFunU8   RES;
  8.    pVoidFunU8   A0;
  9. }Serial_12864;

  10. typedef struct
  11. {
  12.   pVoidFunVoid initial;
  13.   pVoidFunU8   data[8];
  14.   pVoidFunU8   CS;
  15.   pVoidFunU8   RES;
  16.   pVoidFunU8   A0;
  17.   pVoidFunU8   WR;
  18.   pVoidFunU8   RD;
  19. }Parallel_80_12864;

  20. typedef union
  21. {
  22.     Serial_12864 Serial_Mode;
  23.     Parallel_80_12864 Para_80_Mode;
  24. }LCD12864_Driver;

  25. typedef enum
  26. {
  27.    NOT_SET_YET   = 0,
  28.    SERIAL_12864  = 1,
  29.    PARA_80_12864 = 2,
  30.    PARA_68_12864 = 3,
  31. }LCD12864_DriveMode;

  32. typedef struct
  33. {
  34.    LCD12864_DriveMode mode;
  35.    LCD12864_Driver    driver;
  36. }LCD12864;
复制代码


这里解释了一下为什么我会选择用union 共同体来重定义 这两个成员。
这里,我并不是为了节省空间,我的主要目的是
利用 ”共同体“ 表达一个逻辑:

尽管12864本身有多种驱动方式,但对于一个特定的12864硬件连线方式,它事实上只可以选择其中一种,因为我用共同体表达这种结构逻辑。

至于模式,则是为了 枚举化选项。
虽然枚举并不是一个足够安全的范围检查类型。
但它可以达到一定的强化作用,让大家明确它的含义,并且只要我们坚持不用字面量去赋值,就可以保证它的行为永远在我们的预期里,不会出现 不确定行为。
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

推荐
 
回到主题,我们的LCD12864
我们可以很容易的从任何一份手册或者例程,针对我们自己的MCU类型和io连接修改成我们需要的程序。
但是现在,让我们从对象的角度来分析一下 12864作为一个对象,具有什么特性和要做的事情。
(当然这一切是建立在我几次重复实现了12864驱动以后的结果)

12864的驱动方法,我是指带STR7465这类驱动的模块。
我们只需以一定的时序 写入数据,写入命令,就可以完成驱动。

那么——
现在我可以吸收经验,并且真正理解了以前看到的一句编程箴言:
在你设计一个类以前,首先深入了解你要设计的这个对象的特性。


12864的驱动有什么特征呢?
首先,我们知道,不管我们底层如何连接,也不管我们用什么方式读写(串行还是并行,80时序还是68时序)
最后,我们对12864的任何操作,都是通过 写入命令 和 写入数据 完成的。

所以,驱动的最高层 就是这两个动作

(虽然后来真正涉及操作以后,我们还会知道,我们实质上,还需要一个对RES脚的特定时序来复位12864,在上电后复位,相当于 开始时序。)

现阶段提供的代码,都是伪代码,你无法直接编译,它只是作为一种设计过程的中间产物。

  1. typedef struct
  2. {
  3.     void WriteCommand();
  4.     void WriteData();
  5.     void PowerOn();
  6. }Op12864;

  7. //之所以说它们是伪代码,因为我只根据功能设计了它们的函数,我还没确定好形参列表。

  8. // 另外,这里,我使用了c的语法,其实这不是伪代码的好写法,因为它限制了只能用C实现,不过因为我们单片机就是用C为主,所以这里用C也无大碍,相反,这样的伪代码会更亲切。
复制代码


此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

6
 
1.什么是 对象
这里,先说明一下,这些概念并不是那么简单的,所以我只能说,这里的说法是我个人的体会和看法,可能还是很肤浅,但是我希望它有一定的意义,每一次对概念的深化,都可以对实际编程有一定程度的帮助,甚至是突破。

我对一个对象最简单的理解就是
“有数据,有动作”
一个很简单的例子:
如果串口是一个对象,那它必然有数据:收发用的缓冲,还有这个过程中需要记录的种种状态,数值
还得有动作:打开,关闭,接收,发送;

对象这种东西,它的一个重要转变(指的是从面向过程到面向对象的变化)
就在于我们思考的角度.

同样是一个串口,如果处在C的阶段,我们下意识很容易会这样想
我首先要设置好串口,我要打开串口,我要发送数据,我还要接收数据......

但是,如果我们从对象的角度看待串口,我们就会这样考虑
对于串口这个对象,我们需要怎么定义它呢——需要一组怎样的动作和数据来 模拟它的存在?

这个问题就好像,乔布斯这个人如何成其为乔布斯
他首先是个人,是个美国人,他干了什么事......

对象也是一样,不管是什么东西,比如一个串口,一个LCD12864屏,这是物理实在,还是一个虚拟存在的对象,如一个FIFO缓冲。
在编程的世界里,我们是用一组数据,一组动作,来表征它,就好比我们不敢说我们罗列了 乔布斯的成就和简历以后我们就说我们了解了一个真正的乔布斯一样
光有 打开 关闭 接收 发送 还有收发缓冲 以后,我们也不敢说,我们就完全 定义模拟了一个串口对象。

但是,对我们来说,这样一个 串口对象已经能满足我们的需求了,那就够了,到此为止。
以后还可以根据需要扩展
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

7
 
即使是我已经写了几次的12864驱动,实质上,当我完成上述这个设计的时候,我是不可能那么天才的知道接下来我要怎么设计 形参,或者什么实现 这几个op操作和具体驱动之间的分离的.......

如果是,那我一定是扯淡,骗你的——至少我现阶段做不到这个地步。
而事实上,说实话,上面这个设计,是我最后一步才想到的。

现在我们回到12864底层的IO时序操作。

为了简单,我们先以其中一种读写方式为例子。
比如 并行80时序(我之所以选择这个,是因为出差前,我只带了一块硬件上配置成80并行的屏,而我在宾馆里显然没有电烙铁给我用。)

标准的12864模块,都有16个引脚,它们是标准的(我好像还见过一种20引脚的)——不过,如果你和我一样,走完了一次完整的实现,你会发现,不管它硬件上怎么连接,对我的驱动程序都是无关紧要的。

(对我来说,一个真正意义上的模块化程序,只有做到可以单独封装成lib库,然后无须也无法改动仍然可以适应硬件上,设计上的改动,那才叫真正的独立模块——而现在我们就要朝着这个目标努力)


我就不再上引脚图和上时序图了。

采用80并行时序时,需要操作的IO有(不包括 背光控制脚)
  DB[0:7]; 这里表示DB0~DB7
  CS;
  RES;
  A0;
  WR;
  RD;

  每个引脚都将连着一个IO.
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

429

帖子

84

TA的资源

纯净的硅(初级)

8
 
学习了 楼主讲的很不错呢

我写代码只是为了满足应用需求而已 从没想的太深 受教了
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

9
 
对象,12864是对象,一个IO也是对象,处处都是对象。

我们来看这些引脚。它们非常简单的对应于某一个IO;

让我们把它们也看成一个对象。
这个对象非常简单,它们只不过是一个IO脚,只有高低电平变化。

而且,它们都是输出的,不需要输入,也就是说,我们对它们的操作只有两个:写高,写低。

当然,具体到IO操作,除了51以外,我们大多数时候还要设置它的IO方向以及IO配置(推挽,开漏之类的)。

对于上述,进一步分析,我们发现,IO方向和配置的设置只需要操作一次,而写高写低却随时随地都需要操作的。
我们已经很习惯了一种模式:
初始化 --> 操作。

那么初始化 就是 设置方向为输出,这里因为硬件上懒得接个上拉电阻,我们设置成强推挽。
而操作,自然就是 写高写低。考虑到IO对象非常简单,我们不需要再为它设计一个结构体,并且把写高写低合并成一个函数即可。

这样,整体来设计,我们把这一系列IO驱动 合并成一个结构体。

(在看具体的实现以前,老规矩,回味,确定一下,它要提供什么功能?)


每次,我们要做的是
把这些功能引脚 和 通用IO脚 绑定在一起——注意的是,我们不能固定死这种绑定,应使设计满足随时改动。

绑定了后,在使用前,我们还要完成 设置成输出和推挽模式的初始化。

所以,我们需要这样一个 12864 IO驱动结构

  1. typedef struct
  2. {
  3.   pVoidFunVoid initial;
  4.   pVoidFunU8   data[8];
  5.   pVoidFunU8   CS;
  6.   pVoidFunU8   RES;
  7.   pVoidFunU8   A0;
  8.   pVoidFunU8   WR;
  9.   pVoidFunU8   RD;
  10. }Parallel_80_12864;

  11. 这里特别主顶一下,pVoidFunU8不是什么特别的东西,它只是我的一个 类型重定义
  12. typedef void (*pVoidFunU8)(unsigned char );
  13. 简单说, pVoidFunU8 RD;
  14. 就是 void (*RD)(U8);

  15. 至于这里为什么用指针,下一个楼接着说。
复制代码
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

10
 
本帖最后由 辛昕 于 2014-3-21 23:55 编辑

这里,我们来解释一下,为什么上述的 io写高写低函数,我要用指针,作为成员放在 驱动的结构体 里。

这个地方是一个关键,正是它实现了 这个驱动结构 可以独立于单片机。

这个地方我不具体解释我是怎么从开始到最终演变成这种思路的过程了。
只简单解释这种操作方法。

下面直接以这个12864(80时序并行)的驱动代码为例子说明:
请注意,对于下面的这段代码,如果我改变了引脚位置,甚至改用了别的单片机,比如51,我的所有改动都只紧紧锁定在下列这几个函数里而已,此外,任何代码都无须改变。

80方式的12864 IO驱动结构
  1. typedef struct
  2. {
  3.   pVoidFunVoid initial;
  4.   pVoidFunU8   data[8];
  5.   pVoidFunU8   CS;
  6.   pVoidFunU8   RES;
  7.   pVoidFunU8   A0;
  8.   pVoidFunU8   WR;
  9.   pVoidFunU8   RD;
  10. }Parallel_80_12864;
复制代码

实现
  1. void LCM12864_Para_Initial(void)
  2. {
  3.      GPIOA->DDR |= 1<<1;GPIOA->CR1 |= 1<<1;
  4.      GPIOA->DDR |= 1<<2;GPIOA->CR1 |= 1<<2;
  5.      GPIOA->DDR |= 1<<3;GPIOA->CR1 |= 1<<3;
  6.      
  7.      GPIOC->DDR |= 1<<3;GPIOC->CR1 |= 1<<3;
  8.      GPIOC->DDR |= 1<<4;GPIOC->CR1 |= 1<<4;
  9.      GPIOC->DDR |= 1<<5;GPIOC->CR1 |= 1<<5;
  10.      GPIOC->DDR |= 1<<6;GPIOC->CR1 |= 1<<6;
  11.      GPIOC->DDR |= 1<<7;GPIOC->CR1 |= 1<<7;
  12.    
  13.      GPIOD->DDR |= 1<<2;GPIOD->CR1 |= 1<<2;  
  14.      GPIOD->DDR |= 1<<3;GPIOD->CR1 |= 1<<3;
  15.      GPIOD->DDR |= 1<<4;GPIOD->CR1 |= 1<<4;
  16.      GPIOD->DDR |= 1<<5;GPIOD->CR1 |= 1<<5;
  17.      GPIOD->DDR |= 1<<6;GPIOD->CR1 |= 1<<6;
  18. }

  19. void LCM12864_Para_DB0(U8 Logic)
  20. {
  21.      if(Logic == HIGH)
  22.         GPIOC->ODR |= 1<<6;
  23.      else
  24.         GPIOC->ODR &= ~(1<<6);   
  25. }

  26. void LCM12864_Para_DB1(U8 Logic)
  27. {
  28.      if(Logic == HIGH)
  29.         GPIOC->ODR |= 1<<7;
  30.      else
  31.         GPIOC->ODR &= ~(1<<7);   
  32. }

  33. void LCM12864_Para_DB2(U8 Logic)
  34. {
  35.      if(Logic == HIGH)
  36.         GPIOD->ODR |= 1<<3;
  37.      else
  38.         GPIOD->ODR &= ~(1<<3);   
  39. }

  40. void LCM12864_Para_DB3(U8 Logic)
  41. {
  42.      if(Logic == HIGH)
  43.         GPIOD->ODR |= 1<<2;
  44.      else
  45.         GPIOD->ODR &= ~(1<<2);   
  46. }

  47. void LCM12864_Para_DB4(U8 Logic)
  48. {
  49.      if(Logic == HIGH)
  50.         GPIOC->ODR |= 1<<5;
  51.      else
  52.         GPIOC->ODR &= ~(1<<5);   
  53. }

  54. void LCM12864_Para_DB5(U8 Logic)
  55. {
  56.      if(Logic == HIGH)
  57.         GPIOD->ODR |= 1<<4;
  58.      else
  59.         GPIOD->ODR &= ~(1<<4);   
  60. }

  61. void LCM12864_Para_DB6(U8 Logic)
  62. {
  63.      if(Logic == HIGH)
  64.         GPIOD->ODR |= 1<<5;
  65.      else
  66.         GPIOD->ODR &= ~(1<<5);   
  67. }

  68. void LCM12864_Para_DB7(U8 Logic)
  69. {
  70.      if(Logic == HIGH)
  71.         GPIOD->ODR |= 1<<6;
  72.      else
  73.         GPIOD->ODR &= ~(1<<6);   
  74. }

  75. void LCM12864_Para_CS(U8 Logic)
  76. {
  77.      if(Logic == HIGH)
  78.         GPIOA->ODR |= 1<<1;
  79.      else
  80.         GPIOA->ODR &= ~(1<<1);   
  81. }

  82. void LCM12864_Para_RES(U8 Logic)
  83. {
  84.      if(Logic == HIGH)
  85.         GPIOA->ODR |= 1<<2;
  86.      else
  87.         GPIOA->ODR &= ~(1<<2);   
  88. }

  89. void LCM12864_Para_A0(U8 Logic)
  90. {
  91.      if(Logic == HIGH)
  92.         GPIOA->ODR |= 1<<3;
  93.      else
  94.         GPIOA->ODR &= ~(1<<3);   
  95. }

  96. void LCM12864_Para_WR(U8 Logic)
  97. {
  98.      if(Logic == HIGH)
  99.         GPIOC->ODR |= 1<<4;
  100.      else
  101.         GPIOC->ODR &= ~(1<<4);   
  102. }

  103. void LCM12864_Para_RD(U8 Logic)
  104. {
  105.      if(Logic == HIGH)
  106.         GPIOC->ODR |= 1<<3;
  107.      else
  108.         GPIOC->ODR &= ~(1<<3);   
  109. }

  110. //------------------------------------------------------
  111. LCD12864 Para_80_Driver(void)
  112. {
  113.     LCD12864 Reg;
  114.     Parallel_80_12864 Port;
  115.    
  116.     Port.initial = LCM12864_Para_Initial;
  117.    
  118.     Port.data[0] = LCM12864_Para_DB0;
  119.     Port.data[1] = LCM12864_Para_DB1;
  120.     Port.data[2] = LCM12864_Para_DB2;
  121.     Port.data[3] = LCM12864_Para_DB3;
  122.     Port.data[4] = LCM12864_Para_DB4;
  123.     Port.data[5] = LCM12864_Para_DB5;
  124.     Port.data[6] = LCM12864_Para_DB6;
  125.     Port.data[7] = LCM12864_Para_DB7;
  126.    
  127.     Port.A0  = LCM12864_Para_A0;
  128.     Port.CS  = LCM12864_Para_CS;
  129.     Port.RD  = LCM12864_Para_RD;
  130.     Port.RES = LCM12864_Para_RES;
  131.     Port.WR  = LCM12864_Para_WR;
  132.    
  133.     Reg.driver.Para_80_Mode = Port;
  134.     Reg.mode = PARA_80_12864;
  135.    
  136.     return Reg;
  137. }
复制代码

此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

11
 
有了这个 screen
我们等于拥有了 直接对我们的12864的 引脚,如RD 如DB0 DB1等的 IO写高写低。
当然了,我们也就自然的想到
写命令,写数据 的过程,实质上就是这些 引脚 之间的时序的组合而成。

因此,我们现在可以定义 这两个函数。
这两个函数的意义重大,因为,透过它们,我们可以完成所有的12864应用操作,这个接口
也是12864的IO驱动和应用操作的 接口点。


  1. typedef struct
  2. {
  3.     void (*WriteCommand)(void *driver,U8 Command);
  4.     void (*WriteData)(void *driver,U8 Data);
  5.     void (*PowerOn)(void *driver);
  6.     void *driver;
  7. }Op12864;
复制代码


这个其实是设计好的接口结构体。

刚才,我把帖子发给虚V界看,感觉到这么罗罗嗦嗦说一通好像没啥意义。
还是直接看结果再解释更好。

此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

12
 
首先补充一下
poweron这个操作对应 12864上电后,RES脚要有一个 先低后高 的 负脉冲启动时序。
所以加上 写命令写数据,实质上是三个操作。

至于driver,它就是我们前面实现的那个 LCD12864.

这里有一个问题是
我为什么会把driver定义为void *指针,而不是直接定义为 LCD12864。

这里有一个”历史原因“。

我最初写这个12864驱动的时候,我只写了串行方式,我曾答应给 卖给我这个12864屏的朋友一套我自己写的例程。
所以我打算发给她的时候,她问我,是否还有并行的驱动,我说还没,我写完就给你吧。

然后我发现,由于我这种封装驱动的方式,导致我需要重新写一次 诸如 在屏上画 国际象棋棋盘  这样的画屏函数
而原因仅仅只是因为我的驱动结构体不一样!

这一点点的区别,就让我需要重新写一次,这恰好暴露了这个驱动的一个缺陷。还是不够灵活。

我思考的直接结果就是 我上面最开始定义的那套 用void *取代特定驱动结构体 的 那个 Op12864结构体定义。

我的设计思路是

对于具体的12864操作(所有12864的操作都由单纯的3个操作组合而成,PowerOn,writecommand,writedata),
而不管是哪一个操作,都需要特定的引脚时序组合而成,它们永远是逃不开 具体的 驱动的。

但是如果我要实现它们和 具体的驱动隔离,我就必须设计一个可以适应不同驱动结构体的 操作组合结构体。

于是我想到了 有通用指针之称 的 void *

这个时候还有一个小难题。
那就是,即使我可以用void *通用驱动 去写 12864应用操作,我最终还是要还原到真实驱动结构体里去。
我该怎么做呢?

简单的说就是
Op12864并不知道 真正的 LCD12864结构体信息,它却要去调用LCD12864.
但随即我就想到这个很容易实现。
还是类似之前 RD引脚自己也不知道自己可能会被连到GPIOA PIN3上还是 GPIOC PIN4 或者PA4 甚至P12上去一样。
我只要用一个函数指针来传递。

就可以实现 实现和调用的先后顺序颠倒。

具体来说,看这个地方。

  1. void main(void)
  2. {
  3.   static LCD12864 screen;
  4.   static Op12864 ScreenOp;
  5.   
  6.   screen = Para_80_Driver();
  7.   
  8.   ScreenOp = Op12864_Reg(&screen);      
  9.   Op12864_Initial(&ScreenOp);
  10.   Draw12864_Chess(&ScreenOp);

  11.   while(1);
  12. }
复制代码
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

13
 
我们已经得到 screen这样一个携带12864 IO驱动的结构体。

现在我们需要沟通它和 应用操作 Op12864的连接(或者说是注册)


  1. // 这个函数是一定不能封进实现里的;

  2. Op12864 Op12864_Reg(void *lcd)
  3. {
  4.     Op12864 op;
  5.     LCD12864 *plcd = (LCD12864 *)lcd;
  6.    
  7.     op.driver = plcd;
  8.    
  9.     if(isLCD12864_Regist(*plcd) == True)
  10.         (*plcd->driver.Para_80_Mode.initial)();
  11.       
  12.     op.WriteCommand = u12864_WriteCommand;
  13.     op.WriteData = u12864_WriteData;
  14.     op.PowerOn = u12864_PowerOn;
  15.    
  16.     return op;
  17. }
复制代码


这个函数,不是实现在 定义Op12864和写 具体的画屏这些 应用函数 的地方
它是实现在 设计和实现 LCD12864 驱动结构体 的地方。

这其实合情合理。
一方面:除了这个地方,没有地方应该知道或者说,需要去了解具体的 驱动结构设计;
另一方面:
我们期待 应用操作,也就是使用Op12864的那些 画屏函数 可以以一个更开放的姿态接纳不一样的 12864 IO驱动结构设计。

就好比,它应该可以对我一开始的 串行方式 的IO结构
或者我后来希望新增的 80并行方式的IO结构 一样有效。

所以它采用void *通用指针去接纳 驱动。
自然也就不应该去关心具体的实现和内部结构。
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

14
 
END

上面罗罗嗦嗦说了一通。
基本上的设计思路也写完了。

至于具体的代码包。
我把我的程序整理一下,去了一些以前遗留的代码,再把一些 指针检查一并完整后,预计明天就会上传上来。

还有git也是,电脑重装了,git也的重装。
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

9176

帖子

5

TA的资源

管理员

15
 
写的太好了
此帖出自编程基础论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
 
 
 

回复

954

帖子

0

TA的资源

纯净的硅(初级)

16
 
楼主,高手也!
此帖出自编程基础论坛
 
 
 

回复

954

帖子

0

TA的资源

纯净的硅(初级)

17
 
我有一个疑问,这样会不会很费内存呢?
此帖出自编程基础论坛
 
 
 

回复

242

帖子

1

TA的资源

一粒金砂(高级)

18
 
支持楼主,思维很好啊。
此帖出自编程基础论坛
 
 
 

回复

7815

帖子

57

TA的资源

裸片初长成(中级)

19
 
247153481 发表于 2014-3-22 08:33
我有一个疑问,这样会不会很费内存呢?

你可以分析一下。
稍微多一点点,比如二三十B应该是要的。
但这二三十B有那么重要么?
此帖出自编程基础论坛
 
个人签名

强者为尊,弱者,死无葬身之地

 
 

回复

34

帖子

0

TA的资源

一粒金砂(中级)

20
 
写得不错,我一直就是缺少这种思想,
此帖出自编程基础论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表