|
單片機(jī)源程序如下:- #include "stc32g.h" //頭文件見下載軟件
-
- #define MAIN_Fosc 11059200L //定義主時(shí)鐘
-
- /************* 功能說明 **************
-
- 請(qǐng)先別修改程序, 直接下載"08-串口 1 中斷收發(fā)-C 語言-MODBUS 協(xié)議"里的"UART1.hex"測試, 主頻選擇 11.0592MHZ.
- 測試正常后再修改移植.
-
- 串口 1 按 MODBUS-RTU 協(xié)議通信. 本例為從機(jī)程序, 主機(jī)一般是電腦端.
-
- 本例程只支持多寄存器讀和多寄存器寫, 寄存器長度為 64 個(gè), 別的命令用戶可以根據(jù)需要按 MODBUS-RTU 協(xié)議自行添
- 加.
-
- 本例子數(shù)據(jù)使用大端模式(與 C51 一致), CRC16 使用小端模式(與 PC 一致).
-
- 默認(rèn)參數(shù):
- 串口 1 設(shè)置均為 1 位起始位, 8 位數(shù)據(jù)位, 1 位停止位, 無校驗(yàn).
- 串口 1(P3.0 P3.1): 9600bps.
-
- 定時(shí)器 0 用于超時(shí)計(jì)時(shí). 串口每收到一個(gè)字節(jié)都會(huì)重置超時(shí)計(jì)數(shù), 當(dāng)串口空閑超過 35bit 時(shí)間時(shí)(9600bps 對(duì)應(yīng) 3.6ms)則接
- 收完成.
- 用戶修改波特率時(shí)注意要修改這個(gè)超時(shí)時(shí)間.
-
- 本例程只是一個(gè)應(yīng)用例子, 科普 MODBUS-RTU 協(xié)議并不在本例子職責(zé)范圍, 用戶可以上網(wǎng)搜索相關(guān)協(xié)議文本參考.
- 本例定義了 64 個(gè)寄存器, 訪問地址為 0x1000~0x103f.
- 命令例子:
- 寫入 4 個(gè)寄存器(8 個(gè)字節(jié)):
- 10 10 1000 0004 08 1234 5678 90AB CDEF 4930
- 返回:
- 10 10 10 00 00 04 4B C6
- 讀出 4 個(gè)寄存器:
- 10 03 1000 0004 4388
- 返回:
- 10 03 08 12 34 56 78 90 AB CD EF 3D D5
-
- 命令錯(cuò)誤返回信息(自定義):
- 0x90: 功能碼錯(cuò)誤. 收到了不支持的功能碼.
- 0x91: 命令長度錯(cuò)誤.
- 0x92: 寫入或讀出寄存器個(gè)數(shù)或字節(jié)數(shù)錯(cuò)誤.
- 0x93: 寄存器地址錯(cuò)誤.
-
- 注意: 收到廣播地址 0x00 時(shí)要處理信息, 但不返回應(yīng)答.
-
- ******************************************/
-
- typedef unsigned char u8;
- typedef unsigned int u16;
- typedef unsigned long u32;
-
- /************* 本地常量聲明 **************/
- #define RX1_Length 128 /* 接收緩沖長度 */
- #define TX1_Length 128 /* 發(fā)送緩沖長度 */
- //
- //
- //uint gnmbl=7;//1|?ü??±?á?
- u16 FREQ; //
- float temp;
- //sbit OUTPUT1=P26; //PWM1
- //sbit OUTPUT2=P27;
- /************* 本地變量聲明 **************/
- u8 xdata RX1_Buffer[RX1_Length]; //接收緩沖
- u8 xdata TX1_Buffer[TX1_Length]; //發(fā)送緩沖
-
- u8 RX1_cnt; //接收字節(jié)計(jì)數(shù).
- u8 TX1_cnt; //發(fā)送字節(jié)計(jì)數(shù)
- u8 TX1_number; //要發(fā)送的字節(jié)數(shù)
- u8 RX1_TimeOut; //接收超時(shí)計(jì)時(shí)器
-
- bit B_RX1_OK; // 接收數(shù)據(jù)標(biāo)志
- bit B_TX1_Busy; // 發(fā)送忙標(biāo)志
-
-
- /************* 本地函數(shù)聲明 **************/
- void UART1_config(u32 brt, u8 timer, u8 io); // brt: 通信波特率, timer=2: 波特率使用定時(shí)器 2, 其它值: 使用 Timer1做波特率. io=0: 串口 1 切換到 P3.0 P3.1, =1: 切換到 P3.6 P3.7, =2: 切換到 P1.6 P1.7, =3: 切換到 P4.3 P4.4.
- u8 Timer0_Config(u8 t, u32 reload); //t=0: reload 值是主時(shí)鐘周期數(shù), t=1: reload 值是時(shí)間(單位 us), 返回 0 正確, 返回 1 裝載值過大錯(cuò)誤.
- u16 MODBUS_CRC16(u8 *p, u8 n);
- u8 MODBUS_RTU(void);
-
-
-
- #define SL_ADDR 0x01 /* 本從機(jī)站號(hào)地址 */
- #define REG_ADDRESS 0x0000 /* 寄存器首地址 */
- #define REG_LENGTH 64 /* 寄存器長度 */
- u16 xdata modbus_reg[REG_LENGTH]; /* 寄存器地址 */
- //========================================================================
- // 函數(shù): void main(void)
- // 描述: 主函數(shù)
- // 參數(shù): none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 備注:
- //========================================================================
- void main(void)
- {
- u8 i;
- u16 crc;
-
- EAXFR = 1; //使能訪問 XFR
- WTST = 0x00; //設(shè)置程序代碼等待參數(shù),
- //賦值為 0 可將 CPU 執(zhí)行程序的速度設(shè)置為最快
-
- Timer0_Config(0, MAIN_Fosc / 10000); //t=0: reload 值是主時(shí)鐘周期數(shù), (中斷頻率, 20000 次/秒)
- UART1_config(9600UL, 1, 0);// brt: 通信波特率, timer=2: 波特率使用定時(shí)器2, 其它值: 使用Timer1做波特率. io=0: 串口 1 切換到 P3.0 P3.1, =1: 切換到 P3.6 P3.7, =2: 切換到 P1.6 P1.7, =3: 切換到 P4.3 P4.4.
- // init();
- Timer3_Init();
- calculate_F();
- EA = 1;
-
- while (1)
- {
- if(B_RX1_OK && !B_TX1_Busy) //收到數(shù)據(jù), 進(jìn)行 MODBUS-RTU 協(xié)議解析
- {
- if(MODBUS_CRC16(RX1_Buffer, RX1_cnt) == 0) //首先判斷 CRC16 是否正確, 不正確則忽略, 不處理也不返回信息
- {
- if((RX1_Buffer[0] == 0x00) || (RX1_Buffer[0] == SL_ADDR)) //然后判斷站號(hào)地址是否正確, 或者是否廣播地址(不返回信息)
- {
- if(RX1_cnt > 2) RX1_cnt -= 2; //去掉 CRC16 校驗(yàn)字節(jié)
- i = MODBUS_RTU(); //MODBUS-RTU 協(xié)議解析
- if(i != 0) //錯(cuò)誤處理
- {
- TX1_Buffer[0] = SL_ADDR; //站號(hào)地址
- TX1_Buffer[1] = i; //錯(cuò)誤代碼
- crc = MODBUS_CRC16(TX1_Buffer, 2);
- TX1_Buffer[2] = (u8)(crc>>8); //CRC 是小端模式
- TX1_Buffer[3] = (u8)crc;
- B_TX1_Busy = 1; //標(biāo)志發(fā)送忙
- TX1_cnt = 0; //發(fā)送字節(jié)計(jì)數(shù)
- TX1_number = 4; //要發(fā)送的字節(jié)數(shù)
- TI = 1; //啟動(dòng)發(fā)送
- }
- }
- }
- RX1_cnt = 0;
- B_RX1_OK = 0;
- }
- }
- }
-
-
- /****************************** MODBUS_CRC (shift) *************** past test 06-11-27 *********
- 計(jì)算 CRC,調(diào)用方式 MODBUS_CRC16(&CRC,8); &CRC 為首地址,8 為字節(jié)數(shù)
- CRC-16 for MODBUS
- CRC16=X16+X15+X2+1
- TEST: ---> ABCDEFGHIJ CRC16=0x0BEE 1627T
- */
- //========================================================================
- // 函數(shù): u16 MODBUS_CRC16(u8 *p, u8 n)
- // 描述: 計(jì)算 CRC16 函數(shù).
- // 參數(shù): *p: 要計(jì)算的數(shù)據(jù)指針.
- // n: 要計(jì)算的字節(jié)數(shù).
- // 返回: CRC16 值.
- // 版本: V1.0, 2022-3-18 梁工
- //========================================================================
- u16 MODBUS_CRC16(u8 *p, u8 n)
- {
- u8 i;
- u16 crc16;
-
- crc16 = 0xffff; //預(yù)置 16 位 CRC 寄存器為 0xffff(即全為 1)
- do
- {
- crc16 ^= (u16)*p; //把 8 位數(shù)據(jù)與 16 位 CRC 寄存器的低位相異或,把結(jié)果放于 CRC 寄存器
- for(i=0; i<8; i++) //8 位數(shù)據(jù)
- {
- if(crc16 & 1) crc16 = (crc16 >> 1) ^ 0xA001; //如果最低位為 0,把 CRC 寄存器的內(nèi)容右移一位(朝低位),用 0填補(bǔ)最高位,
- //再異或多項(xiàng)式 0xA001
- else crc16 >>= 1; //如果最低位為 0,把 CRC 寄存器的內(nèi)容右移一位(朝低位),用 0 填補(bǔ)最高位
- }
- p++;
- }while(--n != 0);
- return (crc16);
- }
-
- /********************* modbus 協(xié)議 *************************/
- /***************************************************************************
- 寫多寄存器
- 數(shù)據(jù): 地址 功能碼 寄存地址 寄存器個(gè)數(shù) 寫入字節(jié)數(shù) 寫入數(shù)據(jù) CRC16
- 偏移: 0 1 2 3 4 5 6 7~ 最后 2 字節(jié)
- 字節(jié): 1 byte 1 byte 2 byte 2 byte 1byte 2*n byte 2 byte
- addr 0x10 xxxx xxxx xx xx....xx xxxx
-
- 返回
- 數(shù)據(jù): 地址 功能碼 寄存地址 寄存器個(gè)數(shù) CRC16
- 偏移: 0 1 2 3 4 5 6 7
- STC32G 系列技術(shù)手冊(cè) 官方網(wǎng)站: STCMCUDATA
- 深圳國芯人工智能
- 字節(jié): 1 byte 1 byte 2 byte 2 byte 2 byte
- addr 0x10 xxxx xxxx xxxx
-
-
- 讀多寄存器
- 數(shù)據(jù):站號(hào)(地址) 功能碼 寄存地址 寄存器個(gè)數(shù) CRC16
- 偏移: 0 1 2 3 4 5 6 7
- 字節(jié): 1 byte 1 byte 2 byte 2 byte 2 byte
- addr 0x03 xxxx xxxx xxxx
-
- 返回
- 數(shù)據(jù):站號(hào)(地址) 功能碼 讀出字節(jié)數(shù) 讀出數(shù)據(jù) CRC16
- 偏移: 0 1 2 3~ 最后 2 字節(jié)
- 字節(jié): 1 byte 1 byte 1byte 2*n byte 2 byte
- addr 0x03 xx xx....xx xxxx
-
- 返回錯(cuò)誤代碼
- 數(shù)據(jù):站號(hào)(地址) 錯(cuò)誤碼 CRC16
- 偏移: 0 1 最后 2 字節(jié)
- 字節(jié): 1 byte 1 byte 2 byte
- addr 0x03 xxxx
- ***************************************************************************/
- u8 MODBUS_RTU(void)
- {
- u8 i,j,k;
- u16 reg_addr; //寄存器地址
- u8 reg_len; //寫入寄存器個(gè)數(shù)
- u16 crc;
-
- if(RX1_Buffer[1] == 0x06)//寫多寄存器
- {
- if(RX1_cnt < 9) return 0x91; //命令長度錯(cuò)誤
- if((RX1_Buffer[4] != 0) || ((RX1_Buffer[5] *2) != RX1_Buffer[6])) return 0x92; //寫入寄存器個(gè)數(shù)與字節(jié)數(shù)錯(cuò)誤
- if((RX1_Buffer[5]==0) || (RX1_Buffer[5] > REG_LENGTH)) return 0x92; //寫入寄存器個(gè)數(shù)錯(cuò)誤
-
- reg_addr = ((u16)RX1_Buffer[2] << 8) + RX1_Buffer[3]; //寄存器地址
- reg_len = RX1_Buffer[5]; //寫入寄存器個(gè)數(shù)
- if((reg_addr+(u16)RX1_Buffer[5]) > (REG_ADDRESS+REG_LENGTH)) return 0x93; //寄存器地址錯(cuò)誤
- if(reg_addr < REG_ADDRESS) return 0x93; //寄存器地址錯(cuò)誤
- if((reg_len*2+7) != RX1_cnt) return 0x91; //命令長度錯(cuò)誤
-
- j = reg_addr - REG_ADDRESS; //寄存器數(shù)據(jù)下標(biāo)
- for(k=7, i=0; i<reg_len; i++,j++)
- {
- modbus_reg[j] = ((u16)RX1_Buffer[k] << 8) + RX1_Buffer[k+1]; //寫入數(shù)據(jù), 大端模式
- k += 2;
- }
-
- if(RX1_Buffer[0] != 0) //非廣播地址則應(yīng)答
- {
- for(i=0; i<6; i++) TX1_Buffer[i] = RX1_Buffer[i]; //要返回的應(yīng)答
- crc = MODBUS_CRC16(TX1_Buffer, 6);
- TX1_Buffer[6] = (u8)(crc>>8); //CRC 是小端模式
- TX1_Buffer[7] = (u8)crc;
- B_TX1_Busy = 1; //標(biāo)志發(fā)送忙
- TX1_cnt = 0; //發(fā)送字節(jié)計(jì)數(shù)
- TX1_number = 8; //要發(fā)送的字節(jié)數(shù)
- TI = 1; //啟動(dòng)發(fā)送
- }
- }
- else if(RX1_Buffer[1] == 0x03) //讀多寄存器
- {
- if(RX1_Buffer[0] != 0) //非廣播地址則應(yīng)答
- {
- if(RX1_cnt != 6) return 0x91; //命令長度錯(cuò)誤
- if(RX1_Buffer[4] != 0) return 0x92; //讀出寄存器個(gè)數(shù)錯(cuò)誤
- if((RX1_Buffer[5]==0) || (RX1_Buffer[5] > REG_LENGTH)) return 0x92; //讀出寄存器個(gè)數(shù)錯(cuò)誤
-
- reg_addr = ((u16)RX1_Buffer[2] << 8) + RX1_Buffer[3]; //寄存器地址
- reg_len = RX1_Buffer[5]; //讀出寄存器個(gè)數(shù)
- if((reg_addr+(u16)RX1_Buffer[5]) > (REG_ADDRESS+REG_LENGTH)) return 0x93; //寄存器地址錯(cuò)誤
- if(reg_addr < REG_ADDRESS) return 0x93; //寄存器地址錯(cuò)誤
-
- j = reg_addr - REG_ADDRESS; //寄存器數(shù)據(jù)下標(biāo)
- TX1_Buffer[0] = SL_ADDR; //站號(hào)地址
- TX1_Buffer[1] = 0x03; //讀功能碼
- TX1_Buffer[2] = reg_len*2; //返回字節(jié)數(shù)
-
- for(k=3, i=0; i<reg_len; i++,j++)
- {
- TX1_Buffer[k++] = (u8)(modbus_reg[j] >> 8);//數(shù)據(jù)為大端模式
- TX1_Buffer[k++] = (u8)modbus_reg[j];
- }
- crc = MODBUS_CRC16(TX1_Buffer, k);
- TX1_Buffer[k++] = (u8)(crc>>8); //CRC 是小端模式
- TX1_Buffer[k++] = (u8)crc;
- B_TX1_Busy = 1; //標(biāo)志發(fā)送忙
- TX1_cnt = 0; //發(fā)送字節(jié)計(jì)數(shù)
- TX1_number = k; //要發(fā)送的字節(jié)數(shù)
- TI = 1; //啟動(dòng)發(fā)送
- }
- }
- else return 0x90; //功能碼錯(cuò)誤
-
- return 0; //解析正確
- }
-
-
- //========================================================================
- // 函數(shù):u8 Timer0_Config(u8 t, u32 reload)
- // 描述: timer0 初始化函數(shù).
- // 參數(shù): t: 重裝值類型, 0 表示重裝的是系統(tǒng)時(shí)鐘數(shù), 其余值表示重裝的是時(shí)間(us).
- // reload: 重裝值.
- // 返回: 0: 初始化正確, 1: 重裝值過大, 初始化錯(cuò)誤.
- // 版本: V1.0, 2018-3-5
- //========================================================================
- u8 Timer0_Config(u8 t, u32 reload) //t=0: reload 值是主時(shí)鐘周期數(shù), t=1: reload 值是時(shí)間(單位 us)
- {
- TR0 = 0; //停止計(jì)數(shù)
-
- if(t != 0) reload = (u32)(((float)MAIN_Fosc * (float)reload)/1000000UL); //重裝的是時(shí)間(us), 計(jì)算所需要的系統(tǒng)時(shí)鐘數(shù).
- if(reload >= (65536UL * 12)) return 1; //值過大, 返回錯(cuò)誤
- if(reload < 65536UL) AUXR |= 0x80; //1T mode
- else
- {
- AUXR &= ~0x80; //12T mode
- reload = reload / 12;
- }
- reload = 65536UL - reload;
- TH0 = (u8)(reload >> 8);
- TL0 = (u8)(reload);
-
- ET0 = 1; //允許中斷
- TMOD &= 0xf0;
- TMOD |= 0; //工作模式, 0: 16 位自動(dòng)重裝, 1: 16 位定時(shí)/計(jì)數(shù), 2: 8 位自動(dòng)重裝, 3: 16 位自動(dòng)重裝, 不可屏蔽中斷
- TR0 = 1; //開始運(yùn)行
- return 0;
- }
-
- //========================================================================
- // 函數(shù): void timer0_ISR (void) interrupt TIMER0_VECTOR
- // 描述: timer0 中斷函數(shù).
- // 參數(shù): none.
- // 返回: none.
- // 版本: V1.0, 2016-5-12
- //========================================================================
- void timer0_ISR (void) interrupt 1
- {
- if(RX1_TimeOut != 0)
- {
- if(--RX1_TimeOut == 0) //超時(shí)
- {
- if(RX1_cnt != 0) //接收有數(shù)據(jù)
- {
- B_RX1_OK = 1; //標(biāo)志已收到數(shù)據(jù)塊
- }
- }
- }
- }
-
-
- //========================================================================
- // 函數(shù): SetTimer2Baudraye(u16 dat)
- // 描述: 設(shè)置 Timer2 做波特率發(fā)生器。
- // 參數(shù): dat: Timer2 的重裝值.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 備注:
- //========================================================================
- void SetTimer2Baudraye(u16 dat) // 選擇波特率, 2: 使用 Timer2 做波特率, 其它值: 使用 Timer1 做波特率.
- {
- AUXR &= ~(1<<4); //Timer stop
- AUXR &= ~(1<<3); //Timer2 set As Timer
- AUXR |= (1<<2); //Timer2 set as 1T mode
- T2H = (u8)(dat >> 8);
- T2L = (u8)dat;
- IE2 &= ~(1<<2); //禁止中斷
- AUXR |= (1<<4); //Timer run enable
- }
-
-
- //========================================================================
- // 函數(shù): void UART1_config(u32 brt, u8 timer, u8 io)
- // 描述: UART1 初始化函數(shù)。
- // 參數(shù): brt: 通信波特率.
- // timer: 波特率使用的定時(shí)器, timer=2: 波特率使用定時(shí)器 2, 其它值: 使用 Timer1 做波特率.
- // io: 串口 1 切換到的 IO, io=0: 串口 1 切換到 P3.0 P3.1, =1: 切換到 P3.6 P3.7, =2: 切換到 P1.6 P1.7, =3: 切換到 P4.3 P4.4.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 備注:
- //========================================================================
- void UART1_config(u32 brt, u8 timer, u8 io) // brt: 通信波特率, timer=2: 波特率使用定時(shí)器 2, 其它值: 使用 Timer1做波特率. io=0: 串口 1 切換到 P3.0 P3.1, =1: 切換到 P3.6 P3.7, =2: 切換到 P1.6 P1.7, =3: 切換到 P4.3 P4.4.
- {
- brt = 65536UL - (MAIN_Fosc / 4) / brt;
- if(timer == 2) //波特率使用定時(shí)器 2
- {
- AUXR |= 0x01; //S1 BRT Use Timer2;
- SetTimer2Baudraye((u16)brt);
- }
-
- else //波特率使用定時(shí)器 1
- {
- TR1 = 0;
- AUXR &= ~0x01; //S1 BRT Use Timer1;
- AUXR |= (1<<6); //Timer1 set as 1T mode
- TMOD &= ~(1<<6); //Timer1 set As Timer
- TMOD &= ~0x30; //Timer1_16bitAutoReload;
- TH1 = (u8)(brt >> 8);
- TL1 = (u8)brt;
- ET1 = 0; // 禁止 Timer1 中斷
- TR1 = 1; // 運(yùn)行 Timer1
- }
- P_SW1 &= ~0xc0; //默認(rèn)切換到 P3.0 P3.1
- if(io == 1)
- {
- P_SW1 |= 0x40; //切換到 P3.6 P3.7
- P3M1 &= ~0xc0;
- P3M0 &= ~0xc0;
- }
- else if(io == 2)
- {
- P_SW1 |= 0x80; //切換到 P1.6 P1.7
- P1M1 &= ~0xc0;
- P1M0 &= ~0xc0;
- }
- else if(io == 3)
- {
- P_SW1 |= 0xc0; //切換到 P4.3 P4.4
- P4M1 &= ~0x18;
- P4M0 &= ~0x18;
- }
- else
- {
- P3M1 &= ~0x03;
- P3M0 &= ~0x03;
- }
-
- SCON = (SCON & 0x3f) | (1<<6); // 8 位數(shù)據(jù), 1 位起始位, 1 位停止位, 無校驗(yàn)
- // PS = 1; //高優(yōu)先級(jí)中斷
- ES = 1; //允許中斷
- REN = 1; //允許接收
- }
-
-
- //========================================================================
- // 函數(shù): void UART1_ISR (void) interrupt UART1_VECTOR
- // 描述: 串口 1 中斷函數(shù)
- // 參數(shù): none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 備注:
- //========================================================================
- void UART1_ISR (void) interrupt 4
- {
- if(RI)
- {
- RI = 0;
- if(!B_RX1_OK) //接收緩沖空閑
- {
- if(RX1_cnt >= RX1_Length) RX1_cnt = 0;
- RX1_Buffer[RX1_cnt++] = SBUF;
- RX1_TimeOut = 36; //接收超時(shí)計(jì)時(shí)器, 35 個(gè)位時(shí)間
- }
- }
-
- if(TI)
- {
- TI = 0;
- if(TX1_number != 0) //有數(shù)據(jù)要發(fā)
- {
- SBUF = TX1_Buffer[TX1_cnt++];
- TX1_number--;
- }
- else B_TX1_Busy = 0;
- }
- }
復(fù)制代碼
STC32G.H頭文件下載:
STC32-MODBUSRTU.zip
(97.02 KB, 下載次數(shù): 235)
2022-5-4 10:35 上傳
點(diǎn)擊文件名下載附件
STC32單片機(jī)
|
|