|
楼主 |
发表于 2019-1-5 14:28:33
|
显示全部楼层
全部程序,不含串口初始化,不涉及其他程序部分
单片机是M8,编译平台CV
void rw1(void); // 任务声明
void rw2(void);
void rw3(void);
#define MBBMAX 3 /* 任务数 */
void (* flash mmbrwb[MBBMAX])(void)={rw1,rw2,rw3}; // 任务表在flash中
uchar mmbdsb[MBBMAX]; // 任务定时表,在RAM中
uchar mmbbh; // 当前任务号
#define MBZJG 1 /* 帧间隔时间(按波特率调整) */
#define MBSIZE 14 /* 缓存大小(12B数据+2Bcrc) */
uchar mmbbuf[MBSIZE]; // mod缓存
uchar mmbgs; // mod数据个数,从1开始
uchar mmbjs; // mod帧间计时(用于判断帧尾)/mod发送指针,从0开始
uchar mmbcs; // 应答超时
uchar mmbzt=50; // mod状态,初始为任务空闲状态
#define DEkz PORTD.2 /* PD2为485发送控制 */
#define Dreg UDR /* 收发数据寄存器 */
/***/
interrupt [USART_RXC] void mb_rx(void) // 接收缓存非空中断
{
uchar nrxd=Dreg; // 读接收缓存
if(mmbzt<=2)
{
mmbjs=0; // 复位帧间计时
switch (mmbzt)
{
case 0:
mmbgs=1; // 作为第一个数据
if(nrxd==mmbbuf[0]) mmbzt=1; else mmbzt=2; // 第一个数据是站点地址,与发送地址一致,转后续字节,否则等待帧结束
break;
case 1:
if(mmbgs<(MBSIZE-1)) mmbbuf[mmbgs++]=nrxd; else mmbzt=2; // 装入后续字节,超量则等待帧结束
break;
}
}
}
/***/
interrupt [USART_TXC] void mb_tx(void) // TXC发送完成中断 DRE→Dreg空中断
{
if(mmbzt==6) // 发送中
{
if(mmbjs<mmbgs)
{
Dreg=mmbbuf[mmbjs++]; // 没发完,接着发下一个
}
else
{
DEkz=0; // 发完后485芯片DE低电平
mmbzt=0; // 等待接收状态
}
}
}
/***/
void mb_zjjs(void) // 帧间计时判断,定时(2mS)轮询
{
if(mmbzt==1) {if(++mmbjs>MBZJG) mmbzt=8;} // 收到一帧,准备处理
if(mmbzt==2) {if(++mmbjs>MBZJG) mmbzt=0;} // 等到帧结束,转等待接收
}
/***/
uint mb_crc16(uchar *np,uchar nlen) // 计算crc16校验: 数据、字节数
{
uchar nxh;
uint ncrc =0xFFFF;
while(nlen--)
{
ncrc^=*np++;
for(nxh=0;nxh<8;nxh++)
{
if (ncrc & 0x0001)
{ ncrc>>=1; ncrc^=0xA001;}
else { ncrc>>=1; }
}
}
return (ncrc);
}
/******/
void mb_master(void) // modbus主机模式主程序,定时调用,调用周期即为任务定时及超时计时的时基
{
uchar nxh;
uint ncrc;
for(nxh=0; nxh<MBBMAX; nxh++)
{
if(mmbdsb[nxh]) mmbdsb[nxh]--; // 任务定时倒计时
if(mmbzt==50)
{
mmbbh++; if(mmbbh>=MBBMAX) mmbbh=0; // 调度空闲时找下一个可运行的任务
if(mmbdsb[mmbbh]==0) {mmbzt=5;}
}
}
if((mmbzt<5)&&(--mmbcs==0)) mmbzt=21; // 应答超时倒计时,减到0算超时
switch (mmbzt)
{
case 5: // 发送数据准备
(*mmbrwb[mmbbh])();
if(mmbzt==5)
{
ncrc=mb_crc16(mmbbuf,mmbgs); // 计算crc
mmbbuf[mmbgs++]=ncrc; // crc低字节
mmbbuf[mmbgs++]=ncrc>>8; // crc高字节
mmbjs=0; // 发送指针
DEkz=1; Dreg=mmbbuf[mmbjs++]; // 485芯片DE高电平,启动发送
mmbzt++; //zt=6正在发送
}
break;
case 8: // 收到数据整理
mmbzt=22; // 字节数不够或crc错误
if(mmbgs>=5) // 至少有5个字节
{
if(mb_crc16(mmbbuf,mmbgs)==0) // crc正确
{
mmbzt=(mmbbuf[1]&0x80)?23:10; // 功能码最高位=1,从机报错
}
}
case 21:
(*mmbrwb[mmbbh])(); // 收到数据、超时及出错处理
break;
}
}
/******/
/* 任务定时, 应答超时, 从机站号, 功能码, 地址, 数据 */
void mb_rwzb(uchar nds, uchar ncs, uchar nzh, uchar ngn, uint ndz, uint nsj)
{
mmbdsb[mmbbh]=nds; // 任务定时
mmbcs=ncs; // 应答超时
mmbgs=0; // 有效字节数
mmbbuf[mmbgs++]=nzh; // 0从机站号
mmbbuf[mmbgs++]=ngn; // 1功能码
mmbbuf[mmbgs++]=ndz>>8; // 2 首地址高字节
mmbbuf[mmbgs++]=ndz; // 3 首地址低字节
mmbbuf[mmbgs++]=nsj>>8; // 4 个数或其他高字节
mmbbuf[mmbgs++]=nsj; // 5 个数或其他低字节
}
/*** 任务模块 ***/
void rw1(void) // 读模拟量
{
switch(mmbzt)
{
case 5: // 任务准备
mb_rwzb(100,5, 1,4,1,1);
break;
case 10: // 收到的数据
mwd=((uint)mmbbuf[3]<<8)|mmbbuf[4]; // 读取到的温度值
case 21: case 22: case 23: // 出错
mmbzt=50; // 任务完成,忽略出错
break;
}
}
/***/
void rw2(void) // 写一个线圈
{
static uchar nxhh;
switch(mmbzt)
{
case 5:
if(++nxhh>20) nxhh=0;
if(nxhh>15) mmbzt=50; // 8个线圈轮流开停,中间停顿一下,通过读线圈任务可看到线圈的通断情况
else
{ mb_rwzb(10,5, 1,5,nxhh>>1,(nxhh&0x01)?0x0000:0xFF00); }
break;
case 10:
mmbzt=50; // 任务完成
break;
case 21: case 22: case 23: // 出错
mmbzt=5; // 出错重发,不可恢复错误,要防止任务阻塞
break;
}
}
/***/
void rw3(void) // 读线圈
{
switch(mmbzt)
{
case 5:
mb_rwzb(5,5, 1,1,0,8);
break;
case 10: // 收到的数据
mjdq=mmbbuf[3]; // 收到的线圈状态
case 21: case 22: case 23: // 出错
mmbzt=50; // 任务完成,忽略出错
break;
}
}
|
|