在为MCU编写低级代码时,我似乎总是遇到这种困境。 我永远不知道在哪里声明引脚定义,以使代码尽可能可重用。
在这种情况下,我编写驱动程序将 8051 连接到 MCP4922 12位串行DAC 。 我不确定如何/在何处声明DAC的 CS (芯片选择)和 LDAC (数据锁存器)的引脚定义。目前在头文件中声明了驱动程序。
我做了很多研究,试图找出最好的方法,但还没有找到任何东西。
我基本上想知道什么是最佳实践...如果有一些值得阅读的书籍或在线信息,例子等,欢迎提出任何建议。
只是一个驱动程序片段,以便您明白
/**
@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;
}
答案 0 :(得分:3)
这是我在类似情况下所做的,这个例子用于编写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 :(得分:2)
在我工作的一家商店中,引脚定义被放入处理器特定的头文件中。在另一家商店,我将头文件分解为与处理器中的模块相关的主题,例如DAC,DMA和USB。处理器的主包含文件包括所有这些主题头文件。我们可以通过在处理器文件中包含不同的模块头文件来为同一处理器的不同种类建模。
您可以创建一个实现头文件。该文件将根据处理器头文件定义I / O引脚。这为您提供了应用程序和硬件之间的一层抽象。我们的想法是尽可能地将应用程序与硬件松散耦合。
答案 2 :(得分:2)
如果只有驱动程序需要知道CS引脚,则声明不应出现在标题中,而应出现在驱动程序模块本身内。通过在尽可能最严格的范围内隐藏数据,可以最好地实现代码重用。
如果外部模块需要控制CS,请向设备驱动程序模块添加访问功能,以便进行单点控制。如果在调试期间需要知道I / O引脚的置位位置和时间,这非常有用。你只有一点可以应用仪器或断点。
答案 3 :(得分:2)
运行时配置的答案适用于像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,则应该明确需要更新的位置。当你必须调整总线上的时序(即在这里和那里插入一个延迟)时,它也会有所帮助,因为你不需要在整个地方进行更改。希望它有所帮助。