单片机:dspic33FJ128GP206
通信波特率:9600
字符数据格式:1位起始位 8位数据 1位偶校验 1位停止位
帧间最小间隔:3.5个字符
RTU报文帧格式:
<-------------------------------------------------- MODBUS RTU -------------------------------------------------------->
<----------------------------- MODBUS PDU ------------------------>
+-----------+--------------------+----------------------------------------------------+---------------------------------------+
| Address | Function Code | Data(Start Address/Length/Content) | CRC |
+-----------+--------------------+----------------------------------------------------+---------------------------------------+
| | | |
(1byte) (1byte) (0-252bytes,8*N bits) (2bytes,CRC低|CRC高)
编程思路:
1.当单片机接收到来自PC机的数据后,进入串口中断,缓存接收到的数据,同时开启并刷新定时器。当没有接收到数据(或者接收完毕)后,跳出串口中断,同时定时器开始计时:若超过3.5个字符时间单片机仍没有接收到新数据,则认为本次接收完成,将接收完成标志置1;若在3.5个字符时间内又接收到新数据,则再次进入串口中断,重复上述操作。
2.当接收完成标志置1进入接收数据处理:
(1)首先判断接收的第1个数据(1个字节)与本机地址是否相同,如果不相同不发送任何信息;
(2)如果接收的第1个数据与本机地址相同,则对接收缓存中的数据进行crc16校验,如果接收的校验数据与本校验结果不相同,同样不发送任何信息;
(3)如果crc16校验正确则根据数据串中的功能码进行相应的处理。
****************************************************************************************************************************************/
//=========================================头文件引用=================================
#include "modbus.h"
#include "p33FJ128GP206.h"
#include "uart.h"
//==========================================常量定义==================================
#define BAUDRATE 9600 //设置串口1的波特率
#define FCY 8000000 //器件工作速度为8MHz
#define BRGVAL ((FCY/16)/BAUDRATE)-1 //设置波特率发生器
//=========================================初始化振荡器================================
void Init_osc(void)
{
CLKDIVbits.PLLPRE = 0; //预分频因子为2
CLKDIVbits.PLLPOST = 0; //后分频因子为2
PLLFBDbits.PLLDIV = 0x00E; //倍频因子为16
//晶振频率为4MHz,则振荡器频率为4MHz×16/(2*2)=16MHz,
//器件工作速度为16MHz/4=8MHz
}
//=========================================初始化串口1==================================
void Init_uart1(void)
{
U1BRG = BRGVAL; //设置波特率发生器
U1MODEbits.STSEL = 0; //1位停止位
U1MODEbits.PDSEL = 1; //8位数据,偶校验
IFS0bits.U1RXIF = 0; //清除接收中断标志位
IEC0bits.U1RXIE = 1; //使能接收中断
IPC2bits.U1RXIP = 4; //设置接收中断优先级
U1STAbits.URXISEL = 0; //接收到1个字符中断标志位置位
IFS0bits.U1TXIF = 0; //清除发送中断标志位
IEC0bits.U1TXIE = 0; //禁止发送中断
U1MODEbits.UARTEN = 1; //使能串口
U1STAbits.UTXEN = 1; //使能串口发送
}
//========================初始化定时器1===============================================
// 根据波特率和数据位数,可以计算出接收 1 个字符所占用的时间:
// Char_Time_Value = 11 / Baudrate = 1145.833 us
// 根据RTU MODBUS规范,报文帧由时长至少为 3.5 个字符时间的空闲间隔区分,因此
// Frame_Interval_Value_Min = ChTimeValue * 3.5 = 4011us
// 为保证传送数据的可靠性,这里将帧间最小距离设置为 2 倍的规定时长,即
// 4011us * 2 = 8022us
void Init_timer1(void)
{
T1CONbits.TON = 0; //停止定时器
T1CONbits.TCKPS = 1; //设置T1的预分频比是8,即T1加一次需要的时间为1/(8M/8)秒,即1uS
TMR1 = 0; //定时器寄存器清零
PR1 = 8022; //装载定时器周期值,从0开始计数,加到PR1时产生中断
IEC0bits.T1IE = 1; //使能中断
IPC0bits.T1IP = 4; //设置中断优先级
}
//====================================主函数==========================================
void main(void)
{
Init_osc(); //初始化振荡器
Init_uart1(); //初始化串口1
Init_timer1(); //初始化定时器1
while (1) //死循环
{
Com1_Communication(); //串口通信
}
}