- #include <STC32G.H>
- #include <intrins.h>
- // CAN 相关寄存器定义(STC32G 内置)
- sfr CANCON = 0x90; // CAN 控制寄存器
- sfr CANSTA = 0x91; // CAN 状态寄存器
- sfr CANINT = 0x92; // CAN 中断寄存器
- sfr CANBTR0 = 0x93; // 波特率配置0
- sfr CANBTR1 = 0x94; // 波特率配置1
- sfr CANID = 0x95; // ID 寄存器(扩展帧需配合 CANID1/CANID2)
- sfr CANID1 = 0x96;
- sfr CANID2 = 0x97;
- sfr CANDAT = 0x98; // 数据寄存器
- sfr CANDAT1 = 0x99;
- sfr CANDAT2 = 0x9A;
- sfr CANDAT3 = 0x9B;
- sfr CANDAT4 = 0x9C;
- sfr CANDAT5 = 0x9D;
- sfr CANDAT6 = 0x9E;
- sfr CANDAT7 = 0x9F;
- // CAN 模式定义
- #define CAN_MODE_NORMAL 0x00 // 正常模式
- #define CAN_MODE_LOOPBACK 0x40 // 回环模式(自测)
- #define CAN_BAUD_500K 0x01 // 500kbps(8MHz晶振)
- // 全局变量
- unsigned char can_rx_buf[8]; // 接收缓冲区
- bit can_rx_flag = 0; // 接收完成标志
- /**
- * @brief 系统初始化(8MHz晶振)
- */
- void Sys_Init(void)
- {
- CLKSEL = 0x00; // 选择外部晶振(8MHz)
- _nop_();
- _nop_();
- }
- /**
- * @brief CAN 初始化
- * @param mode: 工作模式(正常/回环)
- */
- void CAN_Init(unsigned char mode)
- {
- // 1. 配置IO口(P1.0=CAN_TX,P1.1=CAN_RX)
- P1M1 &= 0xFC; P1M0 |= 0x03; // P1.0/P1.1 推挽输出
- P1PU |= 0x03; // 上拉使能
-
- // 2. 进入初始化模式
- CANCON = 0x80; // 置位INIT位,进入初始化模式
- while(!(CANSTA & 0x80)); // 等待初始化模式确认
-
- // 3. 波特率配置(8MHz晶振 → 500kbps)
- // 分频系数: BRP=0 → TQ = 1/(8MHz/(0+1)) = 0.125μs
- // 同步段: 1TQ, 时间段1: 6TQ, 时间段2: 1TQ → 总8TQ
- // 波特率 = 1/(8*0.125μs) = 1Mbps → 调整为500kbps(BRP=1)
- CANBTR0 = 0x01; // BRP[5:0] = 1 → 分频系数=2 → TQ=0.25μs
- CANBTR1 = 0x1C; // SJW=1TQ, BS1=6TQ, BS2=1TQ → 总8TQ → 500kbps
-
- // 4. 工作模式配置
- CANCON = mode | 0x00; // 清除INIT位,退出初始化模式
- while(CANSTA & 0x80); // 等待退出初始化模式
- }
- /**
- * @brief CAN 发送数据(标准帧,8字节)
- * @param id: 标准ID(11位)
- * @param data: 发送数据缓冲区
- * @param len: 数据长度(1-8)
- * @return 0:成功 1:失败
- */
- unsigned char CAN_Send(unsigned int id, unsigned char *data, unsigned char len)
- {
- if(len > 8) len = 8;
-
- // 1. 等待发送缓冲区空闲
- if(CANSTA & 0x08) return 1; // 发送缓冲区忙
-
- // 2. 写入ID(标准帧,11位)
- CANID = (id >> 3) & 0xFF; // ID[10:3]
- CANID1 = (id << 5) & 0xE0; // ID[2:0]
- CANID1 &= ~0x10; // 标准帧(IDE=0)
-
- // 3. 写入数据长度
- CANID1 |= len & 0x0F; // DLC[3:0]
-
- // 4. 写入数据
- CANDAT = data[0];
- CANDAT1 = data[1];
- CANDAT2 = data[2];
- CANDAT3 = data[3];
- CANDAT4 = data[4];
- CANDAT5 = data[5];
- CANDAT6 = data[6];
- CANDAT7 = data[7];
-
- // 5. 启动发送
- CANCON |= 0x08; // 置位TR位,启动发送
- while(CANSTA & 0x08); // 等待发送完成
-
- // 6. 检查发送结果
- if(CANSTA & 0x10)
- {
- CANSTA &= ~0x10; // 清除发送成功标志
- return 0;
- }
- else
- {
- return 1; // 发送失败
- }
- }
- /**
- * @brief CAN 接收中断服务函数
- */
- void CAN_ISR(void) interrupt 19 // CAN中断号为19
- {
- unsigned char i, len;
-
- // 检查接收中断标志
- if(CANINT & 0x01)
- {
- // 读取数据长度
- len = CANID1 & 0x0F;
- if(len > 8) len = 8;
-
- // 读取数据
- can_rx_buf[0] = CANDAT;
- can_rx_buf[1] = CANDAT1;
- can_rx_buf[2] = CANDAT2;
- can_rx_buf[3] = CANDAT3;
- can_rx_buf[4] = CANDAT4;
- can_rx_buf[5] = CANDAT5;
- can_rx_buf[6] = CANDAT6;
- can_rx_buf[7] = CANDAT7;
-
- // 清除接收中断标志
- CANINT &= ~0x01;
- can_rx_flag = 1; // 设置接收完成标志
- }
- }
- /**
- * @brief 主函数(测试流程)
- */
- void main(void)
- {
- unsigned char tx_data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
- unsigned char rx_len, i;
-
- // 1. 初始化
- Sys_Init();
- CAN_Init(CAN_MODE_NORMAL); // 正常模式(自测用CAN_MODE_LOOPBACK)
-
- // 2. 开启CAN中断
- EA = 1; // 总中断使能
- CANINT = 0x01; // 使能接收中断
- CANCON |= 0x20; // 使能CAN中断
-
- // 3. 循环发送+接收测试
- while(1)
- {
- // 每500ms发送一次数据
- CAN_Send(0x123, tx_data, 8);
-
- // 发送数据自增(便于观察)
- for(i=0; i<8; i++) tx_data[i]++;
-
- // 延时500ms(简易延时,实际建议用定时器)
- for(i=0; i<200; i++) _nop_();
-
- // 检查接收数据
- if(can_rx_flag)
- {
- can_rx_flag = 0; // 清除标志
- // 处理接收数据(此处仅示例,可根据需求修改)
- // 例如:串口打印接收数据、LED指示等
- }
- }
- }
复制代码 |