/****************************************************************************************
Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
File name: usart.c
Author: Harvard
Version: 0.0
Date: 20070620
Description: 完成串行口的初始化,设置,提供单字节的发送接收
History:
----------------------------------------------------------------------------------------
1. Date: 20070622
Author: XXJ
Modification:
t1.5与t3.5采用固定间隔代替,分别为3
2. date: 20070707
author: xxj
abs: 顺利完成modbus的读参数功能,且调试时可以通过在代码中
用串口发送不同状态码的方式来判定各种通信错误:如0位crc
校验出错;1为通信地址错误.
针对16位变量和8位变量在位地址处理上的异同.把所有的
变量均统一成8位一个字节进行同一处理.只接收字地址寻址
收到字地址后.立即乘以2,变成字节,对于16位变量,读两个
字节恰好全部读取,而对于8位变量,可能将其他变量的8个位
也读取了,组成一个字节,这样会造成每次读取时,由于其他
变量的变化而每次的值都会不一样,解决办法时,尽可能将
两个8位变量组合成一个16位的状态字,由用户去统一协调
提取.还有一个办法是在message ,map 中,8位变量前面加入
*****************************************************************************************/
#include "config.h"
/*
* 快读系统状态
* 返回:系统的软件.硬件版本可用于系统的485硬件通断测试
*/
/* 函数名称:void mod_process(void)
* 输入: 接收结束标志
* 输出:
*/
///////////////////////////////////////////////
u16 SYS_485_adr = 1 ;
u16 SYS_485_baud ;/*Meaasga maps */
static u08 Space = 0;
static u16 Space16 = 0;
/* Modbus地址与实际内部变量的映射表 */
u08 * code modbus_tab[] =
{
//只读
&HIGH_(SYS_485_adr), &LOW_(SYS_485_adr), //0: 本机地址
&HIGH_(SYS_x_data), &LOW_(SYS_x_data), //1: 本机地址
&HIGH_(SYS_y_data), &LOW_(SYS_y_data), //2
&HIGH_(SYS_z_data), &LOW_(SYS_z_data), //3
&HIGH_(SYS_485_adr), &LOW_(SYS_485_adr), //4: 本机地址
&HIGH_(SYS_baudrate), &LOW_(SYS_baudrate), //5: 本机地址
&HIGH_(Space16), &LOW_(Space16), //3
&HIGH_(Space16), &LOW_(Space16), //4
&HIGH_(Space16), &LOW_(Space16), //5
&Space, &LOW_(Space16), //6
&Space, &LOW_(Space16), //7
&Space, &LOW_(Space16 ), //8
&Space, &LOW_(Space16 ), //9
};
/* 针对HLM_SOKO的需求, 构造特殊的传输帧,
* 数据经过0x10指令 ,类似于广播地址般的直接写入
* 从机
*/
////////////////////////单路默认:左通道
void modbus_wr( void )
{
u08 i;
//////////////////////////1-帧头
////4 计算CRC ,后放在RxBuf[20..21]中
CRC = 0xffff;
for( i=0; i<20 ; i++ )
{
CRC = _crc16_update( CRC ,TxBuf );
}
TxBuf[20] = CRC%256;
TxBuf[21] = CRC/256;
TxBufPtr = 21;
send_frame ();
}
////////////////////////单路默认:左通道
void modbus_wr_R( void )
{
u08 i;
//////////////////////////1-帧头
////4 计算CRC ,后放在RxBuf[20..21]中
CRC = 0xffff;
for( i=0; i<20 ; i++ )
{
CRC = _crc16_update( CRC ,TxBuf );
}
TxBuf[20] = CRC%256;
TxBuf[21] = CRC/256;
TxBufPtr = 21;
send_frame ();
}
void mod_process(void)
{
if( fFrameDone == OK ) //接收已经完成
{
fFrameDone = NO; //清除标志
usart_dis_recv(); //禁止接收中断
if ( fFrameBad ==TRUE ) //数据帧被破换,不予处理
{
fFrameBad = FALSE; //清除错误标志
}
else //数据帧正常则解析协议
{
cmd_proc();
}
RxBufPtr = RX_BUF_BGN_ADR;//准备下次接收
usart_en_recv(); //重新接收
}
if ( fRxBufFull == YES ) //数据帧错误
{
fFrameBad = FALSE; //清除错误标志
RxBufPtr = RX_BUF_BGN_ADR; //准备下次接收
}
fRxBufFull = NO ; //清空缓冲区
usart_en_recv(); //重新接收
}
/*命令处理*/
void cmd_proc(void)
{
u16 reg_adr =0; //临时 存放要读写的寄存器的地址
u08 reg_errcode =0;//出错时的错误编码
////////////////////////////////////////////=======================CRC 校验
if ( CRC != 0 )
{
//usart_send_byte(0); //错误代码0: crc 校验错误
return ;
}
CRC = 0xffff; //准备计算crc
///////////////////////////////////////////========================判断地址是否正确
if( SYS_485_adr != RxBuf[0] && 0 != RxBuf[0] )
{
return ;
}
TxBuf[0] = SYS_485_adr ;//respond frame's header
TxBuf[1] = RxBuf[1];//respond frame's function code
CRC = _crc16_update( CRC ,TxBuf[0] );
CRC = _crc16_update( CRC ,TxBuf[1] );
////////////////////////////////////////// ========================识别功能码
if( RxBuf[1]==0x07 ) //----------------fast read version
{
TxBuf[2]= (HVERSION<<4)+SVERSION;
CRC = _crc16_update( CRC ,TxBuf[2] );
TxBuf[3] = CRC%256;
TxBuf[4] = CRC/256;
TxBufPtr = 4;
send_frame ();
}
else
if( RxBuf[1]==0x03 || RxBuf[1]== 0x04 ) //-------------------------读寄存器
{
u08 cnt;
TxBuf[2] = RxBuf[5]*2; //字节数
CRC = _crc16_update( CRC ,TxBuf[2] );
TxBufPtr = 3; //调整发送指针,
for( cnt =0 ; cnt< TxBuf[2] ; )//RxBuf[5]--word counts
{
TxBuf[TxBufPtr] = *((u08 *) modbus_tab [cnt +RxBuf[3]*2]);//RxBuf[3]中为起始地址低8位
CRC = _crc16_update( CRC ,TxBuf[TxBufPtr] );
cnt = cnt + 1;
TxBufPtr = TxBufPtr + 1 ;
}
TxBuf[TxBufPtr] = CRC%256;
TxBufPtr = TxBufPtr + 1; //调整发送指针
TxBuf[TxBufPtr] = CRC/256;
send_frame();//发送
}
else
if( RxBuf[1]==0x10 ) //--------------------写寄存器
{
u08 cnt;
TxBuf[2] = RxBuf[2]; //写起始地址高8位,一般为0
TxBuf[3] = RxBuf[3]; //写起始地址低8位
TxBuf[4] = RxBuf[4]; //欲写寄存器数目高8位,一般为0
TxBuf[5] = RxBuf[5]; //欲写寄存器数目低8位
//不允许写长度为0
if(RxBuf[6] < 2) goto err_ret;
//通信协议的规范性检测,从数据帧长度判断
if( (6 + 2 + RxBuf[6] ) != RxBufPtr ) goto err_ret;//指令格式不正确
//是否超出通信协议的地址表范围,所有参数共26字节
if( ( RxBuf[5] + RxBuf[3] ) >20 ) goto err_ret;//字地址及数目超范围
// 20070713 :考虑安全性,将11 12号地址的写功能关闭
//
// if( (RxBuf[3] <= 9 )) goto err_ret;//10号子地址以下为只读
for( cnt =0 ; cnt< RxBuf[6] ;cnt++ ) //RxBuf[6]--byte counts
{
*((u08 *) modbus_tab[ cnt +RxBuf[3]*2] )
=
RxBuf[ 7 + cnt ]; //RxBuf[3]中为起始地址低8位,乘以2,是将之转换为内部的字节地址
}
for ( cnt = 0 ; cnt < 4 ; cnt ++ ) //发送字节指针为0-7,其中:0 .1 为固定,其crc已经计算好
{
CRC = _crc16_update( CRC ,TxBuf[2+cnt] );//2-5
}
TxBuf[6] = CRC%256;
TxBuf[7] = CRC/256;
TxBufPtr = 7; //调整发送指针
send_frame(); //发送
//SynE2promFromRam(); //保存更改
usart_init (SYS_485_baud);//重新初始化串行口
//通信参数的写操作功能已经被关闭
err_ret: TxBufPtr = TX_BUF_BGN_ADR; //after sending ,reset ptr
}
else
if( RxBuf[1]==0x06 ) //----------写寄存器(单个寄存器)
{
/* 先获取寄存器地址 */
reg_adr =((u16) RxBuf[2]<<8) + (u16) RxBuf[3] ; // reg_adr = (u16) RxBuf[3] ;
/* 最大写入地址限定 */
if(reg_adr > MODBUS_ADDR_MAX )
{
reg_errcode = 2;//非法的数据地址
goto err_ret_06;
}
/* 预写一次寄存器值
*/
*((u08 *)(modbus_tab [reg_adr*2]))= RxBuf[4] ;
*((u08 *)(modbus_tab [reg_adr*2+1]))=RxBuf[5] ;
///////发送会送数据帧
TxBuf[0] = RxBuf[0];
TxBuf[1] = RxBuf[1];
TxBuf[2] = RxBuf[2];
TxBuf[3] = RxBuf[3];
TxBuf[4] = RxBuf[4];
TxBuf[5] = RxBuf[5];
TxBuf[6] = RxBuf[6];
TxBuf[7] = RxBuf[7];
TxBufPtr = 7; //调整发送指针
send_frame(); //发送
///////保存参数
//save_user_data_to_flash(); //保存更改
/////////////错误处理:
err_ret_06:
/* 如果是非法的数据地址 */
if( reg_errcode == 2 )
{
TxBuf[0] = RxBuf[0];
TxBuf[1] = 0x86;
CRC =0xffff;
CRC = _crc16_update( CRC ,TxBuf[0] );
CRC = _crc16_update( CRC ,TxBuf[1] );
TxBuf[2] = reg_errcode;
CRC = _crc16_update( CRC ,TxBuf[2] );
TxBuf[3] = CRC%256;
TxBuf[4] = CRC/256;
TxBufPtr = 4;
send_frame();
}
else
if( reg_errcode == 3 )
{
TxBuf[0] = RxBuf[0];
TxBuf[1] = 0x86;
CRC =0xffff;
CRC = _crc16_update( CRC ,TxBuf[0] );
CRC = _crc16_update( CRC ,TxBuf[1] );
TxBuf[2] = reg_errcode;
CRC = _crc16_update( CRC ,TxBuf[2] );
TxBuf[3] = CRC%256;
TxBuf[4] = CRC/256;
TxBufPtr = 4;
send_frame();
}
TxBufPtr = TX_BUF_BGN_ADR; //after sending ,reset ptr
}
}
/*发送一一帧数据*/
void send_frame(void)
{
u08 i;
for( i = 0 ; i <= TxBufPtr; i++ ) //TxBufPtr为发送缓冲区指示器
{
usart_send_byte(TxBuf);
}
TxBufPtr = TX_BUF_BGN_ADR; //after sending ,reset ptr
}
/****************************************************************************************
Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
File name: usart.c
Author: Harvard
Version: 0.0
Date: 20070620
Description: 完成串行口的初始化,设置,提供单字节的发送接收
History:
----------------------------------------------------------------------------------------
1. Date: 20070622
Author: XXJ
Modification:
t1.5与t3.5采用固定间隔代替,分别为3
2. ...
----------------------------------------------------------------------------------------
1. Date: 2016-03-03
Author: Shine
Abstract: - 升级串口初始化函数,改串口晶振为外部12MHZ,波特率范围从4800-115200bps
- 串口初始化i函数的参数从16bit升级到32bit
*****************************************************************************************/
#include "config.h"
u16 _crc16_update(u16 crc, u08 a)
{
int i;
crc ^= a;
for (i = 0; i < 8; ++i)
{
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc = (crc >> 1);
}
return crc;
}
/*
** 串行口初始化
** baudrate取值为: 4800 9600 19200
*/
void usart_init(u32 baudrate)
{
/* 根据实际需要,选择不同的定时器作为波特率发生器 */
InitialUART0_Timer3(baudrate);
/* 将485引脚置为接收状态 */
TXD_EN = 0;
/* 使能串口中断0*/
set_ES;
}
void usart_dis_recv(void)
{
/* 禁止接收 */
TXD_EN =1;
/* 关闭串口0中断 ;*/
clr_ES;
}
void usart_en_recv (void)
{
/* 使能接收引脚*/
TXD_EN = 0;
/* 打开串口中断*/
set_ES;
}
/*超时定时器初始化及其开关控制*/
#define T2_TICK_MIN 1000000/(F_CPU/1024)
#define T2_TICK 1000 //1ms
#define TIMER2_BGN_VAL (255-T2_TICK/T2_TICK_MIN)
//static u08 CNT_t15=0; //1.5字符时间
//static u08 CNT_t35=0; //3.5字符时间
static u08 SYS_cnt_frame =0; //时间间隔计数器,用于1.5 和3.5字符时间判断
u08 SYS_t2_on = OFF ; //用于1.5 3.5字符判断的软定时器 开关.
void timer2_on(void)
{
SYS_cnt_frame = 0 ;
SYS_t2_on = ON;
}
void timer2_off(void)
{
SYS_t2_on = OFF;
/** 帧间隔控制 */
SYS_cnt_frame = 0;
// CNT_t15= 0;
// CNT_t35= 0;
}
/**
* @brief Timer-2 IRQ
*
* @param None
*
* @return None
*
* @Details The TIMER2 default IRQ, declared in startup_M051Series.s.
*/
void Timer2_ISR (void) interrupt 3
{
//////////////////////////////////////////////--重新加载定时器初值
/* 给TIMER 赋初值
** 定时器时钟为:16MHZ/12= 4/3MHZ
** 定时器tick为: 1/ (4/3MHZ) = 3/4us = 0.75uS
** 定时器要定时1ms:
** 定时器初值为: 1000us/0.75uS = 1333
*/
TH1 = (65535-1333)/256;
TL1 = (65535-1333)%256;
/* 如果定时器打开 */
if( SYS_t2_on == ON )
{
SYS_cnt_frame++;
if ( SYS_cnt_frame > T35) //a frame was finished
{
usart_dis_recv(); //禁止接收中断
timer2_off () ; //tunr off timer;
SYS_cnt_frame = 0 ; //reset timer
RxBufPtr--; //调整指针
fFrameDone = OK; //frame is completed
}
}
/* 定时器关,还未进行帧间隔判断 */
else
{
SYS_cnt_frame = 0;
}
}
u08 RxBuf[MAX_BUF_LEN]={0,1,2,3,4};
u08 TxBuf[MAX_BUF_LEN];
u08 RxBufPtr = RX_BUF_BGN_ADR;
u08 TxBufPtr = TX_BUF_BGN_ADR;
BOOL fIsRXING = NO; //is receiving now
BOOL fIsTXING = NO; //is transsending now
BOOL fRxBufFull = NO;
BOOL fTxBufFull = NO;
BOOL fFrameDone = NO; //one frame finished
BOOL fFrameBad = NO; //frame was broken
u16 CRC = 0xFFFF; //CRC初始值
/*接收中断*/
void UART0_IRQHandler( void ) interrupt 4
{
/* 1 - 接收中断处理 */
if ( RI==1 )
{ /* if reception occur */
clr_RI; /* clear reception flag for next reception */
/* Rx trigger level is 1 byte*/
RxBuf[RxBufPtr] = SBUF;
/* reset timer */
SYS_cnt_frame = 0 ;
/*new frame coming*/
if( RxBufPtr == RX_BUF_BGN_ADR )//新的一帧开始
{
CRC = 0xffff ; //准备crc计算,
timer2_on(); //开启定时器计时
}
/*with a frame and RxBuf is not full*/
/*when we detect a frame was borken according to t15 ,
* we keep on receiving in order to synchronize the frame
*/
else if
( RxBufPtr < MAX_BUF_LEN -1)
{
if ( SYS_cnt_frame > T15 && SYS_cnt_frame < T35 )
{
SYS_cnt_frame = 0 ; //reset timer
fFrameBad = TRUE; //frame is broken
}
}
/*RxBuf full */
/*
**
*/
else if( RxBufPtr >= MAX_BUF_LEN -1)
{
usart_dis_recv() ; //stop receving
timer2_off () ; //tunr off timer;
fRxBufFull = YES ; //缓冲区已满
fFrameBad = TRUE; //frame is broken
RxBufPtr --; //指针保持不变,防止破坏其他数据区
}
CRC = _crc16_update( CRC,RxBuf[RxBufPtr] );
RxBufPtr++; //adjust pointer
}//end while
/* 2 - 发送标记处理 */
if(TI==1)
{
clr_TI; /* if emission occur */
}
}
/*查询发送*/
void usart_send_byte( u08 txbyte )
{
TXD_EN =1;
WORK_LED =0;
/* 向串口写入1字节 */
Send_Data_To_UART0( txbyte );
/* 等待1字节传输完毕 */
TXD_EN =0;
WORK_LED =1;
}
|