- #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指示等
- }
- }
- }
復制代碼 |