发新帖本帖赏金 3.00元(功能说明)我要提问
123下一页
返回列表
打印

论Keil C51下如何实现模块驱动文件复用

[复制链接]
4089|51
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
最近在一个项目里需要使用多个数字式温度传感器DS18B20,在编程写完并调试好一个传感器后,就琢磨怎么来控制多个温度传感器。当然,其中最明显的方法就是所谓的单总线方式,这种方式下所有的DS18B20的数据管脚都均通过一条数据总线连接。但这种连接方式并不太适合本应用,且一直觉得有些不足之处:一是在每次操作温度传感器前,都需要先发送一堆ROM指令并和DS18B20按照协议交流,最后选中某个温度传感器再进行操作,较为繁琐和耗费时间;二是单总线方式下,不易区分和知道每个传感器对应所在的位置。所以我还是考虑以每个MCU 的IO控制一个DS18B20的方式来做多点温度测控,大致如下图所示:

一般地,在Keil C51中,写这类模块的驱动程序都会以这种模式:
先映射管脚:
sbit DS18B20_1_DQ_PORT = P4^0;
然后写IO控制语句:
#define DS18B20_Master_1_Control()                       (DS18B20_1_DQ_PORT = 0)
#define DS18B20_Master_1_Release()                       (DS18B20_1_DQ_PORT = 1)
然后其它的读、写、初始化等接口函数再以此为基础来构成。但这种方式下,貌似只能驱动单个模块,如果是有多个模块,在目前还没想到办法之前,岂不是需要写多个驱动文件,如ds18b20_1.c,ds18b20_2.c ...... ds18b20_8.c,这样既繁琐,还会大大地增加代码量,实在是不可取。(最主要是很笨的方法...)
如果要实现复用功能,那么势必需要一种方式来实现IO控制和管脚映射的切换。目前,我的想法是先把每个传感器对应的IO控制动作做成两个函数:控制总线和释放总线,然后分别放入到两个函数指针数组中去。在调用时,通过函数指针数组来选取需要的IO控制函数来实现切换,不知道这样做是否合理?不知道是否有更好的方法来实现此类驱动文件的复用,我觉得,在Keil C51下,大家使用多个相同模块时都会或多或少碰到这个问题吧。

打赏榜单

21ic小喇叭 打赏了 3.00 元 2016-01-26
理由:精彩问题讨论~~~

相关帖子

沙发
幸福至上|  楼主 | 2016-1-14 12:31 | 只看该作者
试了下之前解释过的方法:
先映射管脚:
sbit DS18B20_1_DQ_PORT = P7^2;
sbit DS18B20_2_DQ_PORT = P7^3;

再写管脚控制函数:
static void DS18B20_1_Master_Control(void)
{
    DS18B20_1_DQ_PORT = 0;
}
static void DS18B20_2_Master_Control(void)
{
    DS18B20_2_DQ_PORT = 0;
}

static void DS18B20_1_Master_Release(void)
{
    DS18B20_1_DQ_PORT = 1;
}
static void DS18B20_2_Master_Release(void)
{
    DS18B20_2_DQ_PORT = 1;
}

static bit DS18B20_1_ReadIO(void)
{
    return (DS18B20_1_DQ_PORT);
}

static bit DS18B20_2_ReadIO(void)
{
    return (DS18B20_2_DQ_PORT);
}

再来构成函数指针数组:
//总线Control操作函数指针数组
code void (*DS18B20_Master_Control_Array[DS18B20_NUM])(void) =
{
    DS18B20_1_Master_Control,
    DS18B20_2_Master_Control,
};

//总线Release操作函数指针数组
code void (*DS18B20_Master_Release_Array[DS18B20_NUM])(void) =
{
    DS18B20_1_Master_Release,
    DS18B20_2_Master_Release,  
};
//总线IO读操作函数指针数组
code bit (*DS18B20_ReadIO[DS18B20_NUM])(void) =
{
    DS18B20_1_ReadIO,
    DS18B20_2_ReadIO,
};

然后在API函数中,以
(*DS18B20_Master_Control_Array[ds18b20_index])();
的方式进行调用

使用特权

评论回复
板凳
幸福至上|  楼主 | 2016-1-15 11:35 | 只看该作者
麻烦各位朋友看下是否还有更好的方式呢?@玄德 @chunyang @mmuuss586 @cauhorse

使用特权

评论回复
地板
chunyang| | 2016-1-15 12:15 | 只看该作者
共同的操作当然是可以共用同一段代码的,分别调用即可。

使用特权

评论回复
5
cauhorse| | 2016-1-15 13:25 | 只看该作者
照这样你用8个18B20,就得定义8个DS18B20_1_Master_Control与DS18B20_2_Master_Release的函数原型;冗余代码还是比较多;
可以定义一个18B20类,在内部定义操作函数;用的时候把控制用的IO端口也当作操作参数传入。

使用特权

评论回复
6
幸福至上|  楼主 | 2016-1-15 13:44 | 只看该作者
cauhorse 发表于 2016-1-15 13:25
照这样你用8个18B20,就得定义8个DS18B20_1_Master_Control与DS18B20_2_Master_Release的函数原型;冗余代 ...

嗯嗯,8个模块的话就有8组操作函数了。你说的定义一个类,然后在类的内部定义操作函数,再将控制IO作为参数传入,这个能在Keil C51中实现吗?能否贴一个简单的代码结构示例呢,谢谢

使用特权

评论回复
7
幸福至上|  楼主 | 2016-1-15 14:01 | 只看该作者
chunyang 发表于 2016-1-15 12:15
共同的操作当然是可以共用同一段代码的,分别调用即可。

是的,有什么好的,比较简洁的分别调用方式吗?谢谢

使用特权

评论回复
8
shaoziyang| | 2016-1-15 14:19 | 只看该作者
写一个单总线的基本函数,参数是IO的序号。后面的操作,就可以通过不同的参数访问不同IO上的芯片。在AVR上有这样的例子。

对于51,这样可以最多挂8各芯片了。

使用特权

评论回复
9
玄德| | 2016-1-15 14:25 | 只看该作者

可以考虑宏定义。搜“带参数的宏定义”。


使用特权

评论回复
10
幸福至上|  楼主 | 2016-1-15 14:41 | 只看该作者
shaoziyang 发表于 2016-1-15 14:19
写一个单总线的基本函数,参数是IO的序号。后面的操作,就可以通过不同的参数访问不同IO上的芯片。在AVR上 ...

你说的是这样吗:
void DS18B20_Master_Control(u8 index)
{
switch(index)
{
   case 1:  
   DS18B20_1_DQ_PORT = 0;
    break;
      case 2:  
   DS18B20_2_DQ_PORT = 0;
    break;
    .....
}
}

使用特权

评论回复
11
幸福至上|  楼主 | 2016-1-15 15:07 | 只看该作者
玄德 发表于 2016-1-15 14:25
可以考虑宏定义。搜“带参数的宏定义”。

谢谢提示,我研究了下,使用带参宏定义的话,我也就能将函数指针数组给隐藏起来,如这样:
#define DS18B20_Master_Control(index)    (*DS18B20_Master_Control_Array[index])()
然后在功能函数中,通过调用DS18B20_Master_Control(1...8)这种方式来实现切换。
这种方式下,仍需要建立函数指针数组,运行中也还是需要根据指针跳转。只是看起来规范了一点,请指正。

使用特权

评论回复
12
shaoziyang| | 2016-1-15 15:37 | 只看该作者

用switch当然可以,就是效率低一点,占用空间大。

可以这样做啊,主要针对最底层的操作,剩下的就是调用底层操作函数,比如:

#define OWIPORT P1


#define OWI_PULL_BUS_LOW(pin) \
            OWIDDR |= (1 << pin); \
            OWIPORT &= ~(1 << pin);

#define OWI_RELEASE_BUS(pin) \
            OWIDDR &= ~(1 << pin); \
            OWIPORT |= (1 << pin);

void OWI_WriteBit0(unsigned char pins)
{
  unsigned char intState;
   
  // Disable interrupts.
  intState = __save_interrupt();
  cli();
   
  // Drive bus low and delay.
  OWI_PULL_BUS_LOW(pins);
  _delay_us(OWI_DELAY_C_STD_MODE);
   
  // Release bus and delay.
  OWI_RELEASE_BUS(pins);
  _delay_us(OWI_DELAY_D_STD_MODE);
   
  // Restore interrupts.
  __restore_interrupt(intState);
}

这段程序不完整,是从AVR的程序复杂过来的,只做参考,应该很好移植到51。

使用特权

评论回复
13
chunyang| | 2016-1-15 15:49 | 只看该作者
幸福至上 发表于 2016-1-15 14:01
是的,有什么好的,比较简洁的分别调用方式吗?谢谢

建议你去看看C语言方面的书,任何高级语言都支持这类调用,哪怕是汇编都可以用子程序模块来解决。

使用特权

评论回复
14
玄德| | 2016-1-15 17:04 | 只看该作者

是想写出精炼的C代码,
还是想得到占用程序空间最小的机器语言?
感觉后者做不到,或者有限。

另外,如果把8位端口同时操作,会如何?


使用特权

评论回复
15
幸福至上|  楼主 | 2016-1-15 17:11 | 只看该作者
shaoziyang 发表于 2016-1-15 15:37
用switch当然可以,就是效率低一点,占用空间大。

可以这样做啊,主要针对最底层的操作,剩下的就是调用 ...

明白了,嗯,这种方式确实比switch方式好很多。移位操作是很省事的,而且我用51还不需要设置管口方向。
不过这种方式下,能够复用的模块数目是受限的,比如DS18B20有一根数据线,那么一个IO就只能接8个或着16个,如果还需要接更多的模块进来,就不好弄了。我这只有8个,那就完全绰绰有余了,这也比函数指针数组方式有效率得多了。

使用特权

评论回复
16
幸福至上|  楼主 | 2016-1-15 17:28 | 只看该作者
玄德 发表于 2016-1-15 17:04
是想写出精炼的C代码,
还是想得到占用程序空间最小的机器语言?
感觉后者做不到,或者有限。

想要力求实现一种比较简洁有效率的驱动复用方式,我觉得以函数指针数组方式来调用,每一个IO动作都需要跳转指针,入栈,出栈,显然是很繁琐的。shaoziyang朋友提出的管脚按位移动来进行选中的方式就比我之前提出的方式好。
你说的8位端口同时操作,还真是一个犀利的方法,一次性就可以操作完所有的模块。但需要硬件上要配合编程来连接管脚,最大数量受限于一个管口所具有的IO数量。有必要试一下,以前一个一个地处理流程要变通一下。

使用特权

评论回复
17
玄德| | 2016-1-15 17:39 | 只看该作者
幸福至上 发表于 2016-1-15 17:28
想要力求实现一种比较简洁有效率的驱动复用方式,我觉得以函数指针数组方式来调用,每一个IO动作都需要跳 ...


说实话:
C语言我是自学,用哪里学哪里,实用主义,平时开发没问题;高点就不行了。
你们说的“函数指针”。。。。我还是闪吧。


使用特权

评论回复
18
幸福至上|  楼主 | 2016-1-15 17:48 | 只看该作者
玄德 发表于 2016-1-15 17:39
说实话:
C语言我是自学,用哪里学哪里,实用主义,平时开发没问题;高点就不行了。
你们说的“函数指针 ...

我大学上课只是学了下理论,出来也是用到哪儿学到哪儿,函数指针数组是以前看别人的代码顺到的。你说的这种操作方法确实很不错的,谢谢了。

使用特权

评论回复
19
zhy808zhy| | 2016-1-21 10:29 | 只看该作者
好东西,值得学习

使用特权

评论回复
20
whtwhtw| | 2016-1-21 10:47 | 只看该作者
本帖最后由 whtwhtw 于 2016-1-21 10:49 编辑

比如P1口接8个18B20,能不能以数据流的方式与这8个传感器通信?即给P1口发数据和接收数据的方式,接收后的数据再后处理

使用特权

评论回复
发新帖 本帖赏金 3.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:重庆电子交流圈:521107487,欢迎各位工程师朋友们加入,一起交流讨论,互动分享,共同进步。

38

主题

854

帖子

10

粉丝