Files
102_STC_I2C_SCAN/main.c

340 lines
8.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 文件名: main.c
* 功能: STC8G1K08 I2C设备扫描器
* 描述:
* 1. 作为I2C主机扫描所有7位地址0x00-0x7F接收并记录所有应答
* 2. 每5秒自动进行一次扫描
* 3. 每次扫描完成后通过UART输出所有应答地址
* 芯片: STC8G1K08
* 系统时钟: 11.0592MHz
*/
#include "reg51.h"
#include "intrins.h"
#include <stdio.h>
/* 定义STC8G专用寄存器 */
sfr P_SW2 = 0xba; // 端口切换寄存器
sfr AUXR = 0x8e; // 辅助寄存器
sfr T2L = 0xd7; // 定时器2低字节
sfr T2H = 0xd6; // 定时器2高字节
/* 定义I2C相关寄存器使用xdata指针访问扩展SFR */
#define I2CCFG (*(unsigned char volatile xdata *)0xfe80) // I2C配置寄存器
#define I2CMSCR (*(unsigned char volatile xdata *)0xfe81) // I2C主机控制寄存器
#define I2CMSST (*(unsigned char volatile xdata *)0xfe82) // I2C主机状态寄存器
#define I2CSLCR (*(unsigned char volatile xdata *)0xfe83) // I2C从机控制寄存器
#define I2CSLST (*(unsigned char volatile xdata *)0xfe84) // I2C从机状态寄存器
#define I2CSLADR (*(unsigned char volatile xdata *)0xfe85) // I2C从机地址寄存器
#define I2CTXD (*(unsigned char volatile xdata *)0xfe86) // I2C数据发送寄存器
#define I2CRXD (*(unsigned char volatile xdata *)0xfe87) // I2C数据接收寄存器
/* 定义端口模式寄存器 */
sfr P1M1 = 0x91; // P1模式配置寄存器1
sfr P1M0 = 0x92; // P1模式配置寄存器0
/* 定义I2C引脚 */
sbit SDA = P1^4; // I2C数据线
sbit SCL = P1^5; // I2C时钟线
/* 定义LED引脚 */
sbit LED = P1^2; // 状态指示LED
/* 全局变量 */
bit busy; // I2C忙碌标志位
/**
* @brief I2C中断服务函数
* @note 用于处理I2C通信完成事件
*/
void I2C_Isr() interrupt 24 // 关键修复中断号从24改为7
{
_push_(P_SW2); // 保存P_SW2寄存器
P_SW2 |= 0x80; // 访问扩展寄存器
if (I2CMSST & 0x40) // 检查中断标志位BIT6: 主机操作完成)
{
I2CMSST &= ~0x40; // 清除中断标志
busy = 0; // 清除忙碌标志
}
_pop_(P_SW2); // 恢复P_SW2寄存器
}
/**
* @brief 延时函数
* @param ms 延时毫秒数
* @note 系统时钟11.0592MHz
*/
void Delay_ms(unsigned int ms)
{
unsigned int i, j;
for (i = 0; i < ms; i++)
{
for (j = 0; j < 1100; j++); // 修正延时参数更准确的1ms
}
}
/**
* @brief 等待I2C操作完成
*/
void I2C_Wait()
{
while(!(I2CMSST & 0x40)); // 等待中断标志置位
I2CMSST &= ~0x40; // 清除中断标志
}
/**
* @brief 发送I2C起始信号
*/
void I2C_Start()
{
busy = 1; // 设置忙碌标志
I2CMSCR = 0x81; // 发送START命令0x80=主机模式0x01=START
while (busy); // 等待操作完成
}
/**
* @brief 发送I2C数据
* @param dat 要发送的数据
*/
void I2C_SendData(unsigned char dat)
{
I2CTXD = dat; // 写数据到发送缓冲区
busy = 1; // 设置忙碌标志
I2CMSCR = 0x82; // 发送SEND命令0x80=主机模式0x02=SEND
while (busy); // 等待操作完成
}
/**
* @brief 接收I2C应答信号
*/
void I2C_RecvACK()
{
busy = 1; // 设置忙碌标志
I2CMSCR = 0x83; // 发送读ACK命令0x80=主机模式0x03=RECV ACK
while (busy); // 等待操作完成
}
/**
* @brief 发送I2C停止信号
*/
void I2C_Stop()
{
busy = 1; // 增加busy标志保持逻辑一致
I2CMSCR = 0x86; // 发送STOP命令0x80=主机模式0x06=STOP
while (busy); // 等待操作完成
}
/**
* @brief I2C初始化函数
* @note 配置P1.4为SDAP1.5为SCL设置50kHz通信速率启用中断
*/
void I2C_Init()
{
/* 配置端口模式 */
P1M0 = 0x04; // P1.2设置为推挽输出LEDP1.4和P1.5保持准双向口
P1M1 = 0x00;
/* 访问扩展寄存器 */
P_SW2 = 0x80;
/* 配置I2C参数 */
// 0xF3 = 11110011
// bit7=1: 启用I2C
// bit6=1: 主机模式
// bit5=1: 启用I2C中断关键修复开启中断
// bit4~0=0x13: MSSPEED=19计算50kHz11059200/(2*(19+4)*2)=~50kHz
I2CCFG = 0xF3;
/* 初始化I2C状态 */
I2CMSST = 0x00;
/* 启用全局中断和I2C中断 */
EA = 1; // 全局中断使能
}
/* I2C扫描缓冲区用于存储检测到的设备地址 */
unsigned char xdata i2c_scan_buffer[128]; // 7位地址最多128个
unsigned char scan_count = 0; // 扫描到的设备数量
/**
* @brief 扫描指定I2C 7位地址
* @param addr 要扫描的7位I2C地址0x00-0x7F
* @return 1表示收到应答0表示无应答
*/
bit I2C_ScanAddress(unsigned char addr)
{
bit ack_received = 0; // 应答接收标志
/* 跳过保留地址0x00通用呼叫地址 */
if (addr == 0x00)
{
return 0;
}
I2C_Start(); // 发送起始信号
I2C_SendData(addr << 1); // 7位地址左移+读写位0=写)
I2C_RecvACK(); // 接收应答信号
/* 检查是否收到ACKI2CMSST的bit1为0表示收到ACK */
if (!(I2CMSST & 0x02))
{
ack_received = 1; // 收到应答
}
I2C_Stop(); // 发送停止信号
return ack_received;
}
/**
* @brief 扫描所有I2C 7位地址0x00-0x7F
* @note 将所有应答的设备地址存储到缓冲区中
*/
void I2C_ScanAllAddresses(void)
{
unsigned char i;
scan_count = 0; // 清零扫描计数
/* 遍历所有7位I2C地址规范扫描范围 */
for (i = 0; i < 128; i++)
{
if (I2C_ScanAddress(i)) // 如果收到应答
{
/* 防止缓冲区溢出 */
if (scan_count < 128)
{
i2c_scan_buffer[scan_count++] = i; // 将7位地址存入缓冲区
}
Delay_ms(1); // 短暂延时,避免总线冲突
}
}
}
/**
* @brief UART1初始化函数
* @note 波特率115200bps系统时钟11.0592MHz
*/
void uart1_init(void)
{
SCON = 0x50; // 8位UART允许接收
AUXR |= 0x01; // 串口1选择定时器2为波特率发生器
AUXR &= 0xFB; // 定时器2时钟为12T模式
T2L = 0xFE; // 设置定时器2初值
T2H = 0xFF; // 设置定时器2初值
AUXR |= 0x10; // 启动定时器2
TI = 1; // 设置TI标志用于printf重定向
}
/**
* @brief putchar重定向函数用于printf输出
* @param c 要输出的字符
* @return 输出的字符
*/
char putchar(char c)
{
SBUF = c; // 写入发送缓冲区
while (!TI); // 等待发送完成
TI = 0; // 清除发送完成标志
return c;
}
/**
* @brief 通过UART打印I2C扫描结果
* @note 输出格式化的扫描结果,包括设备数量和地址列表
*/
void UART_PrintScanResults(void)
{
unsigned char i;
printf("\r\n=== I2C扫描结果 ===\r\n");
// printf("检测到的7位I2C设备数量: %d\r\n", scan_count);
if (scan_count > 0)
{
printf("应答地址列表7位:\r\n");
for (i = 0; i < scan_count; i++)
{
printf("0x%02X ", i2c_scan_buffer[i]); // 以十六进制格式输出7位地址
if ((i + 1) % 8 == 0) // 每8个地址换行
{
printf("\r\n");
}
}
printf("\r\n");
printf("对应发送地址8位写模式:\r\n");
for (i = 0; i < scan_count; i++)
{
printf("0x%02X ", i2c_scan_buffer[i] << 1);
if ((i + 1) % 8 == 0) // 每8个地址换行
{
printf("\r\n");
}
}
printf("\r\n");
}
else
{
printf("未检测到任何I2C设备\r\n");
}
printf("==================\r\n");
}
void Delay5000ms(void) //@11.0592MHz
{
unsigned char data i, j, k;
_nop_();
_nop_();
i = 211;
j = 30;
k = 11;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
/**
* @brief 主函数
*/
void main()
{
/* 初始化各模块 */
I2C_Init(); // 初始化I2C
uart1_init(); // 初始化UART1
LED = 0; // 点亮LED推挽输出低电平点亮
/* 输出启动信息 */
printf("\r\n");
printf("========================================\r\n");
printf(" STC8G1K08 I2C设备扫描器\r\n");
printf("========================================\r\n");
printf("系统初始化完成...\r\n");
printf("每5秒自动扫描一次I2C总线\r\n");
printf("========================================\r\n");
/* 主循环 */
while (1)
{
/* LED闪烁指示扫描开始 */
LED = 1; // 熄灭LED
Delay_ms(100);
LED = 0; // 点亮LED
printf("\r\n[开始扫描...]\r\n");
I2C_ScanAllAddresses(); // 执行I2C扫描
printf("[扫描完成]\r\n");
UART_PrintScanResults(); // 输出扫描结果
scan_count = 0; // 清零扫描计数,准备下一次扫描
Delay5000ms(); // 等待5秒后再次扫描
}
}