超简单的用状态机实现的modbus从机模式来啦
本帖最后由 zhanan 于 2019-1-9 13:14 编辑上次写了个主机模式:https://www.amobbs.com/forum.php?mod=viewthread&tid=5705140&page=1&_dsign=c924640c
从机模式和主机模式不一样,从机模式是被动的,收到相关的信息后,处理然后回传,接着再等待。
用状态机的写法是一样的,主要是功能处理要啰嗦一些。我把常用的功能收到什么、回传什么整理好了,照着套吧。
不多说了,上图,然后上程序
void mb_gncl(void);// 功能处理
#define MBID 2 /* 从机站号 */
#define MBZJG 1 /* 帧间隔时间(按波特率调整) */
#define MBSIZE 14/* 缓存大小 */
uchar mmbbuf; // mod缓存
uchar mmbgs;// mod数据个数,从1开始
uchar mmbjs;// mod帧间计时(用于判断帧尾)/出错码/mod发送指针(从0开始)
uchar mmbzt;// mod状态
#define DEkz PORTD.2 /* PD2为485发送控制 */
#define Dreg UDR /* 收发数据寄存器 */
/***/
interrupt void mb_rx(void)// 接收缓存非空中断
{
uchar nrxd=Dreg; // 读接受缓存
if(mmbzt<=2)
{
mmbjs=0;// 复位帧间计时
switch (mmbzt)
{
case 0:
mmbgs=1;
mmbbuf=nrxd; // 站点ID
if(nrxd==MBID) mmbzt=1; else mmbzt=2; // 是本机站号转后续字节,否则等待帧结束
break;
case 1:
if(mmbgs<(MBSIZE-1)) mmbbuf=nrxd; else mmbzt=2; // 装入后续字节,超量则等待帧结束
break;
}
}
}
/***/
interrupt void mb_tx(void)// TXC发送完成中断 DRE→Dreg空中断
{
同主机模式
}
/***/
void mb_zjjs(void)// 帧间计时判断
{
同主机模式
}
/***/
void mb_slave(void)
{
uint ncrc;
if(mmbzt==8) // 收到一帧数据
{
if(mmbgs>=5) // 至少5字节
{
if(mb_crc16(mmbbuf,mmbgs)==0) // crc校验正确
{
mmbjs=0;// 出错码清0
mb_gncl(); // 功能处理
if(mmbjs) // 如果有错误
{
mmbbuf|=0x80; // 功能码最高位置1
mmbbuf=mmbjs; // 错误码
mmbgs=3;
}
ncrc=mb_crc16(mmbbuf,mmbgs); // 计算crc
mmbbuf=ncrc; // crc低字节
mmbbuf=ncrc>>8; // crc高字节
mmbjs=0; // 发送指针
DEkz=1; Dreg=mmbbuf; // 485芯片DE高电平,启动发送
mmbzt=6; // 置为发送状态
return;
}
}
mmbzt=0; // 回空闲状态
}
}
/***/ /*** 从机模式功能处理 ***/
void mb_gncl(void)// 功能处理,若出错则mmbjs为错误码
{
switch (mmbbuf)// 按功能码转
{
case 1: case 2:// 读n个线圈、读n个位输入
// 处理
// buf 线圈/位首地址
// buf 线圈/位个数
// ......
// 回传
mmbbuf=1;// 回传字节数=n/8+1
mmbbuf=mxsd; // 显示灯线圈/位7-0 ,0对应线圈/位首地址
mmbgs=4;
break;
case5:// 写1个线圈
// 处理
// buf 线圈地址
// buf: 0xFF 置1,0x00 置0
// buf: 0x00
// ......
// 回传:原样回传
mmbgs=6;//
break;
case 15:// 写n个线圈
// 处理
// buf 线圈首地址
// buf 线圈个数
// buf 数据字节数
// buf=0x00; // 线圈位7-0 ,位0对应线圈首地址
// buf=0x00; // 15-8
// ......
// 回传:前6个字节(buf-)原样回传
mmbgs=6;//
break;
case3: case 4:// 读n个、读1个寄存器
// 处理
// buf 寄存器首地址
// buf 寄存器个数,读一个=1
// ......
// 回传
mmbbuf=2;// 回传字节数,读一个寄存器字=2
mmbbuf=mwd>>8; // 温度值高字节
mmbbuf=mwd; // 温度值低字节
mmbgs=5;
break;
case6:// 写1个寄存器
// 处理
// buf 寄存器地址
// buf 寄存器数据
// ......
mwd=((uint)mmbbuf<<8)|mmbbuf;// 改写温度值
// 回传:原样回传
mmbgs=6;
break;
case 16:// 写n个寄存器
// 处理
// buf 寄存器首地址
// buf 寄存器个数
// buf 数据字节数
// buf寄存器1数据
// buf 寄存器2数据
// buf寄存器3数据
// ......
mwd=((uint)mmbbuf<<8)|mmbbuf;// 改写温度值
// 回传:前6个字节(buf-)原样回传
mmbgs=6;
break;
default:// 无效功能
mmbjs=1; // 功能码错误
break;
} // endcase
}
/***/ 这种处理方式 比较好我喜欢 非常不错, mmbzt = 8 时,是在哪里处理的。如果状态为8时,UART接收中断是否需要中止接收数据
页:
[1]