📄 smb_test1.c
字号:
// 两个C8051F020 器件之间用SMBus 接口的示例代码。
// 这两个器件以点对点方式工作。
//
// 该例演示每个器件如何使用操作码命令另一个器件完成:
//
// 1) 向DAC0 写一个字节
// 2) 写一个字节到一个数据缓冲区
// 3) 进行一次ADC 转换
// 4) 从一个数据缓冲区读一个字节
//
// 如果每个器件都将DAC0 连到AIN0,则这些操作码的测试是很容易的。
// 在这种配置下,READ_ADC 命令可以用于测试WRITE_DAC 的输出。
//
// 本程序假设两个CF020 器件通过SCL 和SDA 连在一起,从地址(在寄存器SMB0ADR 中)为:
// CHIP_A = 1111000
// CHIP_B = 1110000
//
// 测试代码已包含在内。为了达到测试目的,一个器件中的测试代码应被去掉,而运行另一个
// 器件的测试代码。这可以通过注释掉假定的主器件中测试代码之前的OP_CODE_HANDLER()
// 调用来完成。
//
// 请注意,常数MY_ADD 必须与当前器件对应,在向CHIP_B 下载代码时应将其改为CHIP_B。
//
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// 包含文件
//--------------------------------------------------------------------
#include <c8051f020.h> // 声明
//--------------------------------------------------------------------
// 全局常量
//--------------------------------------------------------------------
#define WRITE 0x00 // 写方向位
#define READ 0x01 // 读方向位
// 器件地址
#define CHIP_A 0xF0
#define CHIP_B 0xE0
#define MY_ADD CHIP_A // 对应当前被编程的器件
// 点对点操作码
#define READ_ADC 0x01 // OP_CODE 读从ADC
#define WRITE_DAC 0x02 // OP_CODE 写从DAC
#define WRITE_BUF 0x03 // OP_CODE 写从缓冲区
#define READ_BUF 0x04 // OP_CODE 读从缓冲区
//SMBus 状态:
// MT = 主发送器
// MR = 主接收器
// ST = 从发送器
// SR = 从接收器
#define SMB_BUS_ERROR 0x00 // (对所有方式)总线错误
#define SMB_START 0x08 // (MT & MR) 起始条件已发送
#define SMB_RP_START 0x10 // (MT & MR) 重复起始条件
#define SMB_MTADDACK 0x18 // (MT) 从地址+ W 已发送;收到ACK
#define SMB_MTADDNACK 0x20 // (MT) 从地址+ W 已发送;收到NACK
#define SMB_MTDBACK 0x28 // (MT) 数据字节已发送;收到ACK
#define SMB_MTDBNACK 0x30 // (MT) 数据字节已发送;收到NACK
#define SMB_MTARBLOST 0x38 // (MT) 竞争失败
#define SMB_MRADDACK 0x40 // (MR) 从地址+ R 已发送;收到ACK
#define SMB_MRADDNACK 0x48 // (MR) 从地址+ R 已发送;收到NACK
#define SMB_MRDBACK 0x50 // (MR) 收到数据字节;ACK 已发送
#define SMB_MRDBNACK 0x58 // (MR) 收到数据字节;NACK 已发送
#define SMB_SROADACK 0x60 // (SR) 收到自己的从地址+W;ACK 已发送
#define SMB_SROARBLOST 0x68 // (SR) 收到自己的从地址+W;竞争失败
#define SMB_SRGADACK 0x70 // (SR) 收到通用呼叫地址+W;ACK 已发送
#define SMB_SRGARBLOST 0x78 // (SR) 在作为主器件发送从地址+ R/W
// 时竞争失败。收到通用呼叫地址;
// ACK已发送
#define SMB_SRODBACK 0x80 // (SR) 收到属于自己从地址的数据字节;
// ACK已发送
#define SMB_SRODBNACK 0x88 // (SR) 收到属于自己从地址的数据字节;
// NACK 已发送
#define SMB_SRGDBACK 0x90 // (SR) 收到通用呼叫地址的数据字节;
// ACK已发送
#define SMB_SRGDBNACK 0x98 // (SR) 收到通用呼叫地址的数据字节;
// NACK 已发送
#define SMB_SRSTOP 0xa0 // (SR) 在作为从其间被访问时
// 收到停止条件或重复起始条件
#define SMB_STOADACK 0xa8 // (ST) 收到自己的从地址+R;ACK 已发送
#define SMB_STOARBLOST 0xb0 // (ST) 在作为主器件发送从地址+ R/W
// 时竞争失败。收到自己的从地址;
// ACK已发送
#define SMB_STDBACK 0xb8 // (ST) 数据字节已发送;收到ACK
#define SMB_STDBNACK 0xc0 // (ST) 数据字节已发送;收到NACK
#define SMB_STDBLAST 0xc8 // (ST) 最后数据字节已发送(AA=0);收到ACK
#define SMB_SCLHIGHTO 0xd0 // (ST & SR) SCL 时钟高电平定时器根据
// SMB0CR 已超时(FTE=1)
#define SMB_IDLE 0xf8 // (对所有方式)等待
//--------------------------------------------------------------------
//全局变量
//--------------------------------------------------------------------
char COMMAND; // 在SMBus ISR 中保存从地址+ R/W 位。
char WORD; // 保存SMBus 待发送字节或刚收到的字节
char OP_CODE; // 保存待发送或刚收到的操作码
char LOST_COMMAND, LOST_WORD, LOST_CODE; // 用于在竞争失败后保存相关数据
char DATA_BUF[16]; // 由OP_CODE_HANDLER 访问的数据缓冲区
bit LOST; // 竞争失败标志,在主方式下竞争失败时置位。
// 用于恢复失败的传输过程
bit SM_BUSY; // 该位在发送或接收开始时置1。
// 操作结束后由ISR 清0
bit VALID_OP; // 用于确定作为从器件收到的字节是操作码
// 还是数据。
bit DATA_READY; // 由OP_CODE 处理程序使用,用于指示
// 从主器件接收的数据何时有效
//--------------------------------------------------------------------
// 函数原型
//--------------------------------------------------------------------
void SMBUS_ISR (void);
char SLA_READ(char chip_select, char out_op);
void SLA_SEND(char chip_select, char out_op, char out_data);
void OP_CODE_HANDLER(void);
//--------------------------------------------------------------------
// 主程序
//--------------------------------------------------------------------
void MAIN (void)
{
char i, check_1, check_2; // 只用于测试目的的变量
WDTCN = 0xde; // 禁止看门狗定时器
WDTCN = 0xad;
XBR0 = 0x01; // 通过交叉开关将SMBus 连到通用I/O 引脚;P0.0,P0.1
XBR2 = 0x40; // 允许交叉开关和弱上拉
SMB0CN = 0x44; // 允许SMBus,应答为低电平(AA = 1)
SMB0CR = -80; // SMBus 时钟频率= 100 kHz
SMB0ADR = MY_ADD; // 设置自己的从地址
ADC0CN = 0x80; // 允许ADC,用写ADBUSY 启动转换
ADC0CN |= 0x01; // ADC 数据寄存器左对齐
DAC0CN = 0x84; // 允许DAC0,数据寄存器左对齐
REF0CN = 0x03; // 允许电压基准
EIE1 |= 2; // SMBus 中断允许
EA = 1; // 全局中断允许
SM_BUSY = 0; // 为第一次传输释放总线
SI = 0;
// OP_CODE_HANDLER(); // 该行只能在两个点对点器件中的一个里
// 被注释掉。只用于测试目的。
// 在正常设置下OP_CODE_HANDLER 将一直运行,
// 以便根据发送给该器件的操作码执行相应操作
// 测试代码-------------------------------------------------------------
// 本代码只用于测试两个器件间的接口。如果上面的OP_CODE_HANDLER 行被去掉注释标记,
// 该器件被假设为主器件。另一个器件应一直运行OP_CODE_HANDLER,响应下面的操作码:
SLA_SEND(CHIP_B, (0x40 | WRITE_BUF), 0x24); // 写到数据缓冲区下标4
SLA_SEND(CHIP_B, (0x60 | WRITE_BUF), 0x25); // 写到下标6
SLA_SEND(CHIP_B, (0x80 | WRITE_BUF), 0x26); // 写到下标8
SLA_SEND(CHIP_B, (0x10 | WRITE_BUF), 0x27); // 写到下标1
check_1 = SLA_READ(CHIP_B, (0x40 | READ_BUF)); // 从缓冲区读下标4
check_1 = SLA_READ(CHIP_B, (0x60 | READ_BUF)); // 读下标6
check_1 = SLA_READ(CHIP_B, (0x80 | READ_BUF)); // 读下标8
check_1 = SLA_READ(CHIP_B, (0x10 | READ_BUF)); // 读下标1
// 在循环内连续增加CHIP_B 的DAC 输出并在每次循环读ADC。CHIP_B 的DAC 输出应斜升
for (i=0;i<50;i++){
SLA_SEND(CHIP_B, WRITE_DAC, 2*i); // 写2* i 到CHIP_B 的DAC0
check_1 = SLA_READ(CHIP_B, READ_ADC); // 读CHIP_B 的AIN0
check_2 = 2*i;} // check_1 应与check_2
// 基本一致
// 测试代码结束---------------------------------------------------------
}
//--------------------------------------------------------------------
// 函数
//--------------------------------------------------------------------
//向从器件发送
// 发送函数向从器件发送两个字节:一个操作码和一个数据字节。有两个操作码用于发送数据:
// WRITE_DAC 和WRITE_BUF。如果操作码为WRITE_BUF,则操作码的高4 位应含有缓冲区
// 的下表。例如,为了写数据缓冲区下标2 对应的地址,操作码参数应为(0x20 | WRITE_BUF)。
//
// chip_select = 从器件地址。
// out_op = 要发送的操作码。
// out_data = 要发送的数据。
void SLA_SEND(char chip_select, char out_op, char out_data){
while(SM_BUSY); // 在SMBus 忙时等待
SM_BUSY = 1; // SMBus 忙标志置位
SMB0CN = 0x44; // 允许SMBus,ACK 为低电平
COMMAND = (chip_select | WRITE); // COMMAND = 7 个地址位+ WRITE.
OP_CODE = out_op; // WORD = 要发送的操作码。
WORD = out_data; // DATA = 要发送的数据。
STA = 1; // 启动传输过程。
}
// 读从器件
// 读函数发出一个字接的操作码,然后发出一个重复起始条件请求读一个字节。
// 可以在两个操作码READ_ADC 和READ_BUF 之间选择。如果操作码为READ_BUF,
// 则操作码的高4 位应含有缓冲区的索引下标。例如,要读数据缓冲区下标5 对应的地址,
// 则操作码应为(0x50 | READ_BUF)。
//
// chip_select = 从器件地址。
// out_op = 要发送的操作码。
char SLA_READ(char chip_select, char out_op){
while(SM_BUSY); // 在SMBus 忙时等待。
SM_BUSY = 1; // SMBus 忙标志置位
SMB0CN = 0x44; // 允许SMBus,ACK 为低电平
COMMAND = (chip_select | READ); // COMMAND = 7 个地址位+ READ
OP_CODE = out_op;
STA = 1; // 启动传输过程。
while(SM_BUSY); // 等待传输结束
return WORD; // 返回接收字
}
// OP_CODE handler.
// 对输入操作码译码并根据操作码执行相应的任务。
// 一旦被调用,将一直运行
//
// VALID_OP 位指示收到一个有效操作码。收到后,该处理程序对操作码译码并执行相应的任务,
// 然后清除VALID_OP 以等待下一个操作码。
void OP_CODE_HANDLER(void){
char index; // 数据缓冲区索引下标
while (1){ // 死循环
VALID_OP = 0; // 等待有效的OP_CODE
while (!VALID_OP);
// OP_CODE 的低4 位用于确定要执行的动作,而高4 位在收到READ_BUF 或
// WRITE_BUF 操作码时用于指示DATA_BUF 数组的下标。
// 注意SMBus 被冻结直到OP_CODE 被译码。
switch (OP_CODE & 0x0F){ // 对OP_CODE 译码
// OP_CODE = READ_ADC – 进行一次ADC 并将结果放到输出缓冲区。
// 只读ADC 的高字节。
case READ_ADC:
SI = 0; // 释放总线
AA = 0; // 使从器件'离线'
AD0INT = 0; // 清ADC 中断标志。
AD0BUSY = 1; // 启动转换。
while (!AD0INT); // 等待转换结束。
WORD = ADC0H; // 将数据放到输出缓冲区
AA = 1; // 使从器件返回'在线'状态
VALID_OP = 0; // 等待新的OP_CODE
break;
// OP_CODE=WRITE_DAC –等待一个有效数据字节,并将其写到DAC0 的高字节。
case WRITE_DAC:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -