/* * 文件名: main.c * 功能: STC8G1K08 I2C设备扫描器 * 描述: * 1. 作为I2C主机扫描所有7位地址0x00-0x7F,接收并记录所有应答 * 2. 每5秒自动进行一次扫描 * 3. 每次扫描完成后通过UART输出所有应答地址 * 芯片: STC8G1K08 * 系统时钟: 11.0592MHz */ #include "reg51.h" #include "intrins.h" #include /* 定义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为SDA,P1.5为SCL,设置50kHz通信速率,启用中断 */ void I2C_Init() { /* 配置端口模式 */ P1M0 = 0x04; // P1.2设置为推挽输出(LED),P1.4和P1.5保持准双向口 P1M1 = 0x00; /* 访问扩展寄存器 */ P_SW2 = 0x80; /* 配置I2C参数 */ // 0xF3 = 11110011 // bit7=1: 启用I2C // bit6=1: 主机模式 // bit5=1: 启用I2C中断(关键修复:开启中断) // bit4~0=0x13: MSSPEED=19,计算50kHz:11059200/(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(); // 接收应答信号 /* 检查是否收到ACK(I2CMSST的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秒后再次扫描 } }