找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 549|回复: 3
打印 上一主题 下一主题
收起左侧

STC32G单片机CAN通讯测试完整实现方案 源程序

[复制链接]
跳转到指定楼层
楼主
ID:880573 发表于 2025-12-12 20:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  1. #include <STC32G.H>
  2. #include <intrins.h>

  3. // CAN 相关寄存器定义(STC32G 内置)
  4. sfr CANCON  = 0x90;  // CAN 控制寄存器
  5. sfr CANSTA  = 0x91;  // CAN 状态寄存器
  6. sfr CANINT  = 0x92;  // CAN 中断寄存器
  7. sfr CANBTR0 = 0x93;  // 波特率配置0
  8. sfr CANBTR1 = 0x94;  // 波特率配置1
  9. sfr CANID   = 0x95;  // ID 寄存器(扩展帧需配合 CANID1/CANID2)
  10. sfr CANID1  = 0x96;
  11. sfr CANID2  = 0x97;
  12. sfr CANDAT  = 0x98;  // 数据寄存器
  13. sfr CANDAT1 = 0x99;
  14. sfr CANDAT2 = 0x9A;
  15. sfr CANDAT3 = 0x9B;
  16. sfr CANDAT4 = 0x9C;
  17. sfr CANDAT5 = 0x9D;
  18. sfr CANDAT6 = 0x9E;
  19. sfr CANDAT7 = 0x9F;

  20. // CAN 模式定义
  21. #define CAN_MODE_NORMAL   0x00  // 正常模式
  22. #define CAN_MODE_LOOPBACK 0x40  // 回环模式(自测)
  23. #define CAN_BAUD_500K     0x01  // 500kbps(8MHz晶振)

  24. // 全局变量
  25. unsigned char can_rx_buf[8];  // 接收缓冲区
  26. bit can_rx_flag = 0;          // 接收完成标志

  27. /**
  28. * @brief 系统初始化(8MHz晶振)
  29. */
  30. void Sys_Init(void)
  31. {
  32.     CLKSEL = 0x00;  // 选择外部晶振(8MHz)
  33.     _nop_();
  34.     _nop_();
  35. }

  36. /**
  37. * @brief CAN 初始化
  38. * @param mode: 工作模式(正常/回环)
  39. */
  40. void CAN_Init(unsigned char mode)
  41. {
  42.     // 1. 配置IO口(P1.0=CAN_TX,P1.1=CAN_RX)
  43.     P1M1 &= 0xFC; P1M0 |= 0x03;  // P1.0/P1.1 推挽输出
  44.     P1PU |= 0x03;                // 上拉使能
  45.    
  46.     // 2. 进入初始化模式
  47.     CANCON = 0x80;  // 置位INIT位,进入初始化模式
  48.     while(!(CANSTA & 0x80));     // 等待初始化模式确认
  49.    
  50.     // 3. 波特率配置(8MHz晶振 → 500kbps)
  51.     // 分频系数: BRP=0 → TQ = 1/(8MHz/(0+1)) = 0.125μs
  52.     // 同步段: 1TQ, 时间段1: 6TQ, 时间段2: 1TQ → 总8TQ
  53.     // 波特率 = 1/(8*0.125μs) = 1Mbps → 调整为500kbps(BRP=1)
  54.     CANBTR0 = 0x01;  // BRP[5:0] = 1 → 分频系数=2 → TQ=0.25μs
  55.     CANBTR1 = 0x1C;  // SJW=1TQ, BS1=6TQ, BS2=1TQ → 总8TQ → 500kbps
  56.    
  57.     // 4. 工作模式配置
  58.     CANCON = mode | 0x00;  // 清除INIT位,退出初始化模式
  59.     while(CANSTA & 0x80);  // 等待退出初始化模式
  60. }

  61. /**
  62. * @brief CAN 发送数据(标准帧,8字节)
  63. * @param id: 标准ID(11位)
  64. * @param data: 发送数据缓冲区
  65. * @param len: 数据长度(1-8)
  66. * @return 0:成功 1:失败
  67. */
  68. unsigned char CAN_Send(unsigned int id, unsigned char *data, unsigned char len)
  69. {
  70.     if(len > 8) len = 8;
  71.    
  72.     // 1. 等待发送缓冲区空闲
  73.     if(CANSTA & 0x08) return 1;  // 发送缓冲区忙
  74.    
  75.     // 2. 写入ID(标准帧,11位)
  76.     CANID = (id >> 3) & 0xFF;    // ID[10:3]
  77.     CANID1 = (id << 5) & 0xE0;   // ID[2:0]
  78.     CANID1 &= ~0x10;             // 标准帧(IDE=0)
  79.    
  80.     // 3. 写入数据长度
  81.     CANID1 |= len & 0x0F;        // DLC[3:0]
  82.    
  83.     // 4. 写入数据
  84.     CANDAT  = data[0];
  85.     CANDAT1 = data[1];
  86.     CANDAT2 = data[2];
  87.     CANDAT3 = data[3];
  88.     CANDAT4 = data[4];
  89.     CANDAT5 = data[5];
  90.     CANDAT6 = data[6];
  91.     CANDAT7 = data[7];
  92.    
  93.     // 5. 启动发送
  94.     CANCON |= 0x08;  // 置位TR位,启动发送
  95.     while(CANSTA & 0x08);       // 等待发送完成
  96.    
  97.     // 6. 检查发送结果
  98.     if(CANSTA & 0x10)
  99.     {
  100.         CANSTA &= ~0x10;  // 清除发送成功标志
  101.         return 0;
  102.     }
  103.     else
  104.     {
  105.         return 1;  // 发送失败
  106.     }
  107. }

  108. /**
  109. * @brief CAN 接收中断服务函数
  110. */
  111. void CAN_ISR(void) interrupt 19  // CAN中断号为19
  112. {
  113.     unsigned char i, len;
  114.    
  115.     // 检查接收中断标志
  116.     if(CANINT & 0x01)
  117.     {
  118.         // 读取数据长度
  119.         len = CANID1 & 0x0F;
  120.         if(len > 8) len = 8;
  121.         
  122.         // 读取数据
  123.         can_rx_buf[0] = CANDAT;
  124.         can_rx_buf[1] = CANDAT1;
  125.         can_rx_buf[2] = CANDAT2;
  126.         can_rx_buf[3] = CANDAT3;
  127.         can_rx_buf[4] = CANDAT4;
  128.         can_rx_buf[5] = CANDAT5;
  129.         can_rx_buf[6] = CANDAT6;
  130.         can_rx_buf[7] = CANDAT7;
  131.         
  132.         // 清除接收中断标志
  133.         CANINT &= ~0x01;
  134.         can_rx_flag = 1;  // 设置接收完成标志
  135.     }
  136. }

  137. /**
  138. * @brief 主函数(测试流程)
  139. */
  140. void main(void)
  141. {
  142.     unsigned char tx_data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
  143.     unsigned char rx_len, i;
  144.    
  145.     // 1. 初始化
  146.     Sys_Init();
  147.     CAN_Init(CAN_MODE_NORMAL);  // 正常模式(自测用CAN_MODE_LOOPBACK)
  148.    
  149.     // 2. 开启CAN中断
  150.     EA = 1;          // 总中断使能
  151.     CANINT = 0x01;   // 使能接收中断
  152.     CANCON |= 0x20;  // 使能CAN中断
  153.    
  154.     // 3. 循环发送+接收测试
  155.     while(1)
  156.     {
  157.         // 每500ms发送一次数据
  158.         CAN_Send(0x123, tx_data, 8);
  159.         
  160.         // 发送数据自增(便于观察)
  161.         for(i=0; i<8; i++) tx_data[i]++;
  162.         
  163.         // 延时500ms(简易延时,实际建议用定时器)
  164.         for(i=0; i<200; i++) _nop_();
  165.         
  166.         // 检查接收数据
  167.         if(can_rx_flag)
  168.         {
  169.             can_rx_flag = 0;  // 清除标志
  170.             // 处理接收数据(此处仅示例,可根据需求修改)
  171.             // 例如:串口打印接收数据、LED指示等
  172.         }
  173.     }
  174. }
复制代码
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:123917 发表于 2025-12-13 10:02 | 只看该作者
好资料,学习了
回复

使用道具 举报

板凳
ID:1164914 发表于 2025-12-13 21:34 | 只看该作者
很好,学习学习
回复

使用道具 举报

地板
ID:433219 发表于 2025-12-16 14:15 | 只看该作者
最简化模式么?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表