【问题标题】:Writing Device Drivers for Microcontrollers, where to define IO Port pins?为微控制器编写设备驱动程序,在哪里定义 IO 端口引脚?
【发布时间】:2010-05-27 12:53:50
【问题描述】:

在为 MCU 编写低级代码时,我似乎总是遇到这种困境。 我永远不知道在哪里声明引脚定义以使代码尽可能可重用。

在这种情况下,我正在编写一个驱动程序来将 8051 连接到 MCP4922 12 位串行 DAC。 我不确定我应该如何/在哪里声明 DAC 的 CS(芯片选择)和 LDAC(数据锁存器)的引脚定义。目前在头文件中声明了驱动程序。

我做了很多研究,试图找出最好的方法,但还没有真正找到任何东西。

我基本上想知道最佳实践...如果有一些值得阅读的书籍或在线信息、示例等,欢迎任何建议。

只需对驱动程序进行 sn-p 了解即可

/**
    @brief  This function is used to write a 16bit data word  to DAC B -12 data bit plus 4 configuration bits
    @param  dac_data A 12bit word 
    @param  ip_buf_unbuf_select Input Buffered/unbuffered  select bit. Buffered = 1; Unbuffered = 0
    @param  gain_select Output Gain Selection bit. 1 = 1x (VOUT = VREF * D/4096).  0 =2x (VOUT = 2 * VREF * D/4096)
*/
void MCP4922_DAC_B_TX_word(unsigned short int dac_data, bit ip_buf_unbuf_select, bit gain_select)
{                                             

    unsigned char low_byte=0, high_byte=0;
    CS = 0;                                               /**Select the chip*/

    high_byte |= ((0x01 << 7) | (0x01 << 4));            /**Set bit to select DAC A and Set SHDN bit high for DAC A active operation*/
    if(ip_buf_unbuf_select) high_byte |= (0x01 << 6);
    if(gain_select)         high_byte |= (0x01 << 5);

    high_byte |= ((dac_data >> 8) & 0x0F);
    low_byte |= dac_data;
    SPI_master_byte(high_byte);
    SPI_master_byte(low_byte);

    CS = 1;                                               
    LDAC = 0;                                             /**Latch the Data*/
    LDAC = 1;                                         
}

【问题讨论】:

    标签: c embedded device-driver microcontroller


    【解决方案1】:

    这是我在类似情况下所做的,此示例用于编写 I²C 驱动程序:

    // Structure holding information about an I²C bus
    struct IIC_BUS
    {
        int pin_index_sclk;
        int pin_index_sdat;
    };
    
    // Initialize I²C bus structure with pin indices
    void iic_init_bus( struct IIC_BUS* iic, int idx_sclk, int idx_sdat );
    
    // Write data to an I²C bus, toggling the bits
    void iic_write( struct IIC_BUS* iic, uint8_t iicAddress, uint8_t* data, uint8_t length );
    

    所有引脚索引都在依赖于应用程序的头文件中声明,以便快速概览,例如:

    // ...
    #define MY_IIC_BUS_SCLK_PIN 12
    #define MY_IIC_BUS_SCLK_PIN 13
    #define OTHER_PIN 14
    // ...
    

    在此示例中,I²C 总线实现是完全可移植的。它只依赖于可以通过索引写入芯片引脚的 API。

    编辑:

    这个驱动是这样使用的:

    // main.c
    #include "iic.h"
    #include "pin-declarations.h"
    
    main()
    {
        struct IIC_BUS mybus;
        iic_init_bus( &mybus, MY_IIC_BUS_SCLK_PIN, MY_IIC_BUS_SDAT_PIN );
    
        // ...
    
        iic_write( &mybus, 0x42, some_data_buffer, buffer_length );
    }
    

    【讨论】:

    • 这是我一直在寻找的东西,谢谢。我将总结我的理解以确保我理解......如果没问题。 1) 保存 PIN 信息的结构在驱动程序头文件中定义 2) 该结构的一个实例在您的主应用程序中声明 3) 在另一个头文件中定义的 pining 信息分配给该结构 4) 结构被传递给驱动程序例程必要的。对吗?
    • 我添加了一个用法示例以进一步说明,但您明白了。我并不声称这是为嵌入式系统编写设备驱动程序的绝对最佳方式,但在我的应用程序中似乎是合适的。
    • 谢谢,现在知道了,我看到任务在 iic_init_bus() 中完成了 - 使它变得漂亮和整洁.. 即使它不是“绝对最好的”,它肯定比我拥有的更好..我真的需要找到一本关于嵌入式编程良好实践的好书,以免我问这些类型的问题......再次感谢
    • 我刚刚发现无法在我的平台 keil+8051 上传递引脚定义所以我回到第一方,我认为最好的方法(在这种情况下)可能是除非有人有更好的想法,否则单独的配置头中的引脚定义。
    【解决方案2】:

    在我工作的一家商店中,引脚定义被放入特定于处理器的头文件中。在另一家商店,我将头文件分解为与处理器中的模块相关的主题,例如 DAC、DMA 和 USB。处理器的主包含文件包括所有这些主题头文件。我们可以通过在处理器文件中包含不同的模块头文件来对同一处理器的不同品种进行建模。

    您可以创建一个实现头文件。该文件将根据处理器头文件定义 I/O 管脚。这为您在应用程序和硬件之间提供了一层抽象。这个想法是尽可能松散地耦合应用程序与硬件。

    【讨论】:

    • 谢谢...一些有价值的提示。我倾向于实现头文件类型的解决方案,它似乎是我当前平台唯一合理的解决方案。谢谢
    【解决方案3】:

    如果只有驱动程序需要知道 CS 引脚,那么声明不应出现在标题中,而应出现在驱动程序模块本身中。代码重用的最佳方式是在尽可能严格的范围内隐藏数据。

    如果外部模块需要控制 CS,请在设备驱动模块中添加访问功能,以便您进行单点控制。如果在调试期间您需要知道 I/O 引脚的位置和时间,这将非常有用;您只有一个点可以应用检测或断点。

    【讨论】:

    • 是的,只有驱动程序需要了解 CS 引脚,但我想实现某种 API,允许我在不同的项目中使用不同的 MCU 端口引脚或移植到其他平台而不会造成混乱使用驱动模块(显然我必须更改 CS 地址..)
    • 改header还是.c都没关系,都是源代码,无论如何都需要重新编译。
    • 事实上隐藏硬件细节的更多原因是,头文件对于所有实现将保持相同。在另一个平台上,无论如何实施都可能需要更改;如果您遵循您的建议,两个 文件将需要更改,而不仅仅是一个。理想情况下,头文件应该定义接口而不是内部。
    【解决方案4】:

    运行时配置的答案适用于像 ARM、PowerPC 这样的体面 CPU……但作者在这里运行的是 8051。 #define 可能是最好的方法。以下是我将如何分解它:

    blah.h:
    
    #define CSN_LOW()   CS = 0
    #define CSN_HI()    CS = 1
    #define LATCH_STROBE() \
     do { LDAC = 0; LDAC = 1; } while (0)
    
    blah.c:
    #include <blah.h>
    void blah_update( U8 high, U8 low ) 
    {
       CSN_LOW();
       SPI_master_byte(high);
       SPI_master_byte(low);
       CSN_HI();
       LATCH_STROBE();
    } 
    

    如果您需要更改引脚定义,或移动到不同的 CPU,那么您需要更新的地方应该很明显。当您必须调整总线上的时间(即在这里和那里插入延迟)时,它也很有帮助,因为您不需要在整个地方进行更改。希望对您有所帮助。

    【讨论】:

      猜你喜欢
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-06
      • 2013-03-23
      • 1970-01-01
      相关资源
      最近更新 更多