/*****************************************************************************
** 函数名称: uint crc16(uchar *puchMsg,uchar usDataLen)
** 功能描述: 16位CRC校验函数,查表法*
** 输 入: uchar *puchMsg,uchar usDataLen
** 校验的数组首地址 校验的数据个数
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 14.03.26
******************************************************************************/
uint crc16(uchar *puchMsg,uchar usDataLen)
{
uchar uchCRCHi = 0xFF ;
uchar uchCRCLo = 0xFF ;
uint uIndex ;
while (usDataLen--)
{
uIndex = uchCRCHi ^ *puchMsg++ ;
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
uchCRCLo = auchCRCLo[uIndex] ;
}
return (((uint)(uchCRCLo) << 8) | uchCRCHi) ;
}
/*****************************************************************************
** 函数名称: void delay_us(int n)
** 功能描述: 延时子程序,延迟N us
** 输 入: int n 延时的时间
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
/********延时子程序,延迟N us*************/
void delay_us(int n)
{
int i;
for(i=n;n>0;n--)
_nop_();
}
/*****************************************************************************
** 函数名称: void delay_ms(int n)
** 功能描述: 延时子程序,延迟N ms
** 输 入: int n 延时的时间
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void delay_ms(int n)
{
int i,j;
for(i=n;i>0;i--)
for(j=141;j>0;j--);
}
/*****************************************************************************
** 函数名称: void Led_Display_Fuc(uint Num)
** 功能描述: 数码管显示函数,最多显示五位数,数值仅限于0-9,
该函数根据实际情况选择使用
** 输 入: uint Num 要显示的数字
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
**
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void Led_Display_Fuc(uint Num)
{
uint i;
for (i=0;i<5; i++)
{
P2 = 0; //清除显示,防止数字重影
P0 = DuanMa[Num%10]; //显示最低一位数
delay_us(10);
P2 = 0x80>>i; //显示第一位数时点亮第一个数码管,第二位时点亮第二个数码管,以此类推。
Num = Num/10; //将变量除以10处理将显示下一位数
delay_ms(1);
}
}
复制代码
更多资料可以关注订阅号:单片机精讲吴鉴鹰
随时随地学习在论坛的分享
回复 赏评分 举报
吴鉴鹰
14
主题
258
帖子
807
积分
高级技术员
关注TA 发消息
专家等级:
结帖率:12%
打赏:0.00
受赏:1.00
66楼
楼主| 发表于 2015-4-14 16:44 | 只看该作者 |返回版面
第五讲:基于单片机的Modbus通讯程序设计(二)
/*****************************************************************************
** 函数名称: void init_uart(void)
** 功能描述: 串口初始化函数
** 输 入: int n 延时的时间
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 备注:设定的参数为9600,N,8,1
串口模式为2,即8位数据位,停止位为1,无校验
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void init_uart(void)
{
TH1 = 0xfa;
TL1 = 0xfa; // 波特率设为9600
TMOD = 0x21; // 使用T1定时器,模式2
PCON = PCON | 0x80; // 启用波特率加强位
SCON = 0x50; // 串口模式为2,即8位数据位,停止位为1,无校验
PS = 1; // 设定串口中断优先级为最高
TR1 = 1; // 开始定时
ES = 1; // 打开串口中断
EA = 1; // 打开总中断
}
/*****************************************************************************
** 函数名称: void Send_Data_Fuc(uchar num)
** 功能描述: 发送函数
发送一个字符即一个字节
** 输 入: uchar num 发送的数据或者字符
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 备注:设定的参数为9600,N,8,1
串口模式为2,即8位数据位,停止位为1,无校验
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void Send_Data_Fuc(uchar num)
{
SBUF=num; //把字符放到发送数据缓存区SBUF,num可以是字符也可以是一个字节数据
while(!TI); //当发送完之后,TI自动置位,即TI=1,表示发送完成
TI = 0; //当发送完之后TI不会自动置0,所以需手动将其置0,表示还没发送完数据
}
/*****************************************************************************
** 函数名称: void send_word(uchar *mydata,uchar num)
** 功能描述: 发送函数
发送字符串或者一连串的字节数据
** 输 入: uchar *mydata,uchar num
** mydata表示要发送的字符串数组,num表示数组长度
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 备注:设定的参数为9600,N,8,1
串口模式为2,即8位数据位,停止位为1,无校验
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void send_word(uchar *mydata,uchar num)
{
int i;
for(i=0;i<num;i++)
{
Send_Data_Fuc(*(mydata+i));
}
Data_Count = 0;
}
/*****************************************************************************
** 函数名称: void Com_Data_Receive() interrupt 4 using 1
** 功能描述: 中断接收函数
当数据接收完时,进入串口中断将接收的值放入数组
** 输 入:
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void Com_Data_Receive() interrupt 4 using 1
{
if(RI) //当接收完一个字节的数据后,RI会自动置位,即RI=1
{
uchar a;
RI=0; //接受中断标志软件清零
a=SBUF; //把缓存在单片机的数据给a
Receive_buf[Data_Count]=a; //将接受数据放入预置数组
Data_Count++; //数组自动递增,用于存储下一个数据
if(Data_Count == 8) //当接收完8个MODBUS的功能码之后,自动清0,从新接收(支持的01、05、03、06命令读取或者写入每次都发送8个字节)
{
Data_Count = 0;
Receive_Finish_Flag = 1; //接受完8个MODBUS功能码的标志位
}
}
}
/*****************************************************************************
** 函数名称: void Com_Data_Mng(void)
** 功能描述: MODBUS功能码处理
能识别01,03,05,06功能码,可以实现一对多通讯
** 输 入:
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void Com_Data_Mng(void)
{
//uint begin_address=0,address_leg=0,crc_end;
uint crc_end,legg; //CEC校验值和03功能码发送的位的长度
char hi_type,low_type; //03、06功能码发送字节高低位
if(Receive_buf[0]== 0xa1) //判断机器号码
{
if(Receive_buf[1] == 0x01) //01功能码
{
//begin_address=Receive_buf[2]<<8+Receive_buf[3];
//address_leg=Receive_buf[4]<<8+Receive_buf[5];
crc_end = crc16(Receive_buf,6); //校验
if(crc_end == Receive_buf[6]<<8 | Receive_buf[7])//当校验通过时时
{
hi_type=0; //高位状态
low_type=Pd_Flag_0 | Pd_Flag_1 | Pd_Flag_2 | Pd_Flag_3 | Pd_Flag_4 | Pd_Flag_5 | Pd_Flag_6 | Pd_Flag_7; //低位状态
//low_type=~P0;
Send_buf[0] = Receive_buf[0]; //站号
Send_buf[1] = Receive_buf[1]; //功能码
Send_buf[2] = 0x02; //字节数
Send_buf[3] = low_type; //低位状态,即LED开关情况
Send_buf[4] = hi_type; //高位状态
crc_end = crc16(Send_buf,5); //校验
Send_buf[5] = crc_end%256; //校验低位
Send_buf[6] = crc_end>>8; //校验高位
send_word(Send_buf,7); //发送返回屏
}
}
if(Receive_buf[1] == 0x05) //05功能码
{
begin_address=Receive_buf[3]; //开始地址
crc_end=crc16(Receive_buf,6); //校验
if(crc_end==Receive_buf[6]<<8|Receive_buf[7]) //校验正确时
{
if(Receive_buf[4]==0xff) //当为强制打开时
{
switch(begin_address) //对应的地址
{
case 0x00: LED_OUT_0 = 0; Pd_Flag_0 = 0x01; break; //相应的LED点亮,记录下相应的P0位状态
case 0x01: LED_OUT_1 = 0; Pd_Flag_1 = 0x02; break;
case 0x02: LED_OUT_2 = 0; Pd_Flag_2 = 0x04; break;
case 0x03: LED_OUT_3 = 0; Pd_Flag_3 = 0x08; break;
case 0x04: LED_OUT_4 = 0; Pd_Flag_4 = 0x10; break;
case 0x05: LED_OUT_5 = 0; Pd_Flag_5 = 0x20; break;
case 0x06: LED_OUT_6 = 0; Pd_Flag_6 = 0x40; break;
case 0x07: LED_OUT_7 = 0; Pd_Flag_7 = 0x80; break;
}
}
else //当强制为OFF时
{
//flag_led=0;
switch(begin_address)
{
case 0x00: LED_OUT_0=1; Pd_Flag_0=0; break; //相应的LED熄灭,记录下相应的P0位状态
case 0x01: LED_OUT_1=1; Pd_Flag_1=0; break;
case 0x02: LED_OUT_2=1; Pd_Flag_2=0; break;
case 0x03: LED_OUT_3=1; Pd_Flag_3=0; break;
case 0x04: LED_OUT_4=1; Pd_Flag_4=0; break;
case 0x05: LED_OUT_5=1; Pd_Flag_5=0; break;
case 0x06: LED_OUT_6=1; Pd_Flag_6=0; break;
case 0x07: LED_OUT_7=1; Pd_Flag_7=0; break;
}
}
Send_buf[0]=Receive_buf[0]; //站号
Send_buf[1]=Receive_buf[1]; //功能码
Send_buf[2]=Receive_buf[2]; //起始高位
Send_buf[3]=Receive_buf[3]; //起始低位
Send_buf[4]=Receive_buf[4]; //返回状态
Send_buf[5]=Receive_buf[5]; //返回状态
crc_end=crc16(Send_buf,6); //校验
Send_buf[6]=crc_end%256; //校验低位
Send_buf[7]=crc_end>>8; //校验高位
send_word(Send_buf,8); //发送返回屏
}
}
if(Receive_buf[1]==0x03) //03功能码
{
crc_end=crc16(Receive_buf,6); //校验
if(crc_end == Receive_buf[6]<<8|Receive_buf[7]) //校验一致
{
begin_address=Receive_buf[2]<<8|Receive_buf[3];
address_leg=Receive_buf[4]<<8 | Receive_buf[5]; //总寄存器长度
hi_type=0;
Send_buf[0]=Receive_buf[0]; //站号
Send_buf[1]=Receive_buf[1]; //功能码
legg=(uint)address_leg*2; //字节数
Send_buf[2]=address_leg*2;; //字节数
for(time=0;time<legg;time++) //发送相应字节
{
if(!(time%2)) //字高位为0
Send_buf[time+3] = disp[time/2+begin_address]/256; //字高字节
else
Send_buf[time+3] = disp[time/2+begin_address]%256; //字低字节
}
crc_end=crc16(Send_buf,legg+3); //校验
Send_buf[legg+3]=crc_end%256; //校验低位
Send_buf[legg+4]=crc_end>>8; //校验高位
send_word(Send_buf,legg+5); //返回屏
}
}
if(Receive_buf[1]==0x06) //06功能码
{
begin_address=Receive_buf[2]<<8|Receive_buf[3]; //写入地址
address_num=Receive_buf[4]<<8|Receive_buf[5]; //寄存器值
crc_end=crc16(Receive_buf,6); //校验
if(crc_end==Receive_buf[6]<<8|Receive_buf[7]) //校验正确
{
disp[begin_address]=address_num; //存入数组,给03功能码调用数值
Send_buf[0]=Receive_buf[0]; //站号
Send_buf[1]=Receive_buf[1]; //功能码
Send_buf[2]=Receive_buf[2]; //开始高位
Send_buf[3]=Receive_buf[3]; //开始低位
Send_buf[4]=Receive_buf[4]; //字高位
Send_buf[5]=Receive_buf[5]; //字低位
crc_end=crc16(Send_buf,6); //校验
Send_buf[6]=crc_end%256; //校验低位
Send_buf[7]=crc_end>>8; //校验高位
send_word(Send_buf,8); //返回屏
}
}
}
}
/*****************************************************************************
** 函数名称: void main(void)
** 功能描述: 主函数
** 输 入:
**
**
** 输 出:
**
** 全局变量:
** 调用模块:
** 作 者: 吴鉴鹰
** 日 期: 15.03.26
******************************************************************************/
void main(void)
{
init_uart(); //初始化串口
while(1)
{
Led_Display_Fuc(address_num); //在开发板数码管上显示相应位的数值,发送或者接受数据会暂停显示(数码管闪烁)
//按钮输入
if(!KEY_OUT_3) //判断按钮是否按下
{
delay_ms(2); //延时
while (!KEY_OUT_3);
//delay_ms(2);
LED_OUT_2=!LED_OUT_2;
if(LED_OUT_2)
{
Pd_Flag_2=0x0;
}
else
{
Pd_Flag_2=0x4;
}
}
if(!KEY_OUT_4)
{
delay_ms(2);
while (!KEY_OUT_4);
//delay_ms(2);
LED_OUT_3=!LED_OUT_3;
if(LED_OUT_3)
{
Pd_Flag_3=0x0;
}
else
{
Pd_Flag_3=0x8;
}
}
while(Receive_Finish_Flag) //当接收完成时
{
Com_Data_Mng(); //识别相应的功能码
Receive_Finish_Flag=0; //重新接收
}
}
}
|