1.主节点
主节点资源:4个按键,采用9600波特率的UART,在发送同步间隔场时用4800!收发芯片:MCP201
2.从节点
从节点资源:UART,CCP捕捉 首先用接收引脚状态判断是否同步间隔场,用定时器1进行计时判断同步间隔场是否合格,判断合格后,用CCP模块捕捉同步头55H,同时计算波特率,之后打开UART进行数据接收!
程序如下:
1.MASTER
// LIN 总线设计
//---------------------------------------------
// 文件名: MASTER.c
// 版本: V1.0
// 日期: 25/12/06
// 功能: LIN总线主节点
// 编译环境: HiTech PIC C compiler v.8.05
// 使用MCU: PIC16F877
// 通信速率: 9600
//---------------协议说明---------------------
//数据发送按照USART方式发送
//Lin协议说明:
//1.电平定义:
// 显性电平:逻辑0
// 隐性电平:逻辑1
//2.数据组成
// 同步间隔场:至少13位的显性电平,以及1个位的界定符(隐性电平)
// 同步场:0x55主机发送,从机通过定时器捕捉方式来计时计算波特率
// 数据场:
// a.标识符场:定义了报文的内容和长度,最高两位是奇偶校验位
// 位5和6是数据长度标识,低四位是地址标识符
// b.命令场:由2.4.8个数据字节组成,外加一个数据校验场
// 保留标识符:(广播标识符)
// "0x3c"主机请求,用于主机广播命令,所有从机都接收
// "0x3d"从机响应,触发所有从机节点顺序向主机传送数据
// 第一个数据场的00-7F保留,其余自由分配(程序里没有遵循,实验嘛呵呵)
//---------------------------------------------
#include
//---------------------------------------------
//--------------常量定义----------------------
#define Key1 RA0
#define Key2 RA1
#define Key3 RA2
#define Key4 RA3
#define CS RB1
#define STATUSIT(avr,s) ((unsigned)(&avr)*8+(s)) //绝对寻址定义
static bit C @ STATUSIT(STATUS,0); //对进位位进行定义
//----------------内存定义--------------------
unsigned char send[10]; //定义数据数组
unsigned char Counter; //发送个数计数
//--------------函数定义----------------------
void Picint(void); //初始化
void KeyScan(void); //键盘扫描
void Work1(void); //事件处理1
void Work2(void); //事件处理2
void Work3(void); //事件处理3
void Work4(void); //事件处理4
void interrupt SDI(void); //中断函数
void Delay(unsigned int m); //延时函数
void Send(void); //数据发送
void SendSync(void); //同步间隔场和同步头发送
void SendData(unsigned char asd); //发送数据场
unsigned char CheckSum(unsigned char as); //校验和计算
void qushu();
//*********************************************
//主函数
//*********************************************
void main()
{
Picint();
while(1)
{
KeyScan(); //键盘扫描等待处理
}
}
//*********************************************
//初始化函数
//*********************************************
void Picint()
{
INTCON=0; //中断定义
ADCON1=0x07; //定义A口为数字ioput模式
TRISA0=1; //键盘接口定义
TRISA1=1;
TRISA2=1;
TRISA3=1;
TRISB0=1; //中断定义端口
TRISB1=0; //片选引脚
TXSTA=0x04; //USART模块设置,使用高速波特率设置
RCSTA=0x80; //使能串口,并没有开启接收和发送
TRISC7=1; //数据输入
TRISC6=0; //数据发送
CS=1; //片选
RC6=1;
}
//*********************************************
//数据发送 USART
//*********************************************
void Send()
{
//TXEN=1; //发送使能
SendSync(); //发送同步间隔场和同步头
SendData(Counter); //发送的字节的数量
TXEN=0; //发送禁止
}
//*********************************************
//同步间隔场和同步头发送
//*********************************************
void SendSync()
{
SPBRG=0x33; //同步间隔场按照4800发送
Delay(20);
TXEN=1;
Delay(20);
TXREG=0x80; //同步间隔场
while(1) //等待发送完毕
{
if(TXIF==1) break;
}
Delay(150);
SPBRG=0x19; //修改波特率为9600
Delay(50);
TXREG=0x55; //发送同步头,用于从机进行波特率计算
while(1)
{
if(TXIF==1) break;
}
Delay(300); //延时准备发送数据
}
//*********************************************
//发送数据场
//*********************************************
void SendData(unsigned char asd)
{
unsigned char i;
TXREG=send[0]; //发送地址字节
while(1)
{
if(TXIF==1) break;
}
Delay(250); //延时,供从节点判断是否是自己的地址
for(i=0;i
TXREG=send[i+1]; //发送数据字节,以及校验字节
while(1)
{
if(TXIF==1) break;
}
Delay(100);
}
}
//*********************************************
//校验和计算
//*********************************************
unsigned char CheckSum(unsigned char as)
{
//校验和说明:
//是所有命令字节的和,但是如果相加的话产生进位位怎进位位加到和的最低位,最后结果取反
//从节点接收后同样计算命令字节和,而后与校验字节相加,其和必须等于0xFF;
unsigned char z,y;
y=0;
asm("bcf _STATUS,0"); //清进位位
for(z=0;z
y=y+send[z+1];
if(C)
{
C=0; //清除进位位供下次使用
y=y+1; //如果进位位为1,则加到结果的最低位
}
}
y=~y; //按位取反
return y; //返回校验和
}
//*********************************************
//键盘扫描函数
//*********************************************
void KeyScan()
{
if(Key1)
{
Delay(8000); //延时消抖
if(Key1) //确认键盘按下
{
while(Key1) //等待键盘释放
{}
Work1(); //调用处理事件
}
}
//----------------------------------------------
if(Key2)
{
Delay(8000); //延时消抖
if(Key2) //确认键盘按下
{
while(Key2) //等待键盘释放
{}
Work2(); //调用处理事件
}
}
//----------------------------------------------
if(Key3)
{
Delay(8000); //延时消抖
if(Key3) //确认键盘按下
{
while(Key3) //等待键盘释放
{}
Work3(); //调用处理事件
}
}
//----------------------------------------------
if(Key4)
{
Delay(8000); //延时消抖
if(Key4) //确认键盘按下
{
while(Key4) //等待键盘释放
{}
Work4(); //调用处理事件
}
}
}
//*********************************************
//事件处理
//*********************************************
void Work1()
{
send[0]=0xc1; //地址字节01,加上奇偶校验为c1
send[1]=0x12; //命令字节1
send[2]=0x13; //命令字节2
Counter=3;
send[3]=CheckSum(Counter-1); //校验和计算
Send();
}
//---------------------------------------------
void Work2()
{
send[0]=0xc1; //地址字节01,加上奇偶校验为c1
send[1]=0x22; //命令字节1
send[2]=0x23;
Counter=3;
send[3]=CheckSum(Counter-1); //校验和计算
Send();
}
//---------------------------------------------
void Work3()
{
send[0]=0xc1; //地址字节01,加上奇偶校验为c1
send[1]=0x32; //命令字节1
send[2]=0x33;
Counter=3;
send[3]=CheckSum(Counter-1); //校验和计算
Send();
}
//---------------------------------------------
void Work4()
{
send[0]=0xc1; //地址字节01,加上奇偶校验为c1
send[1]=0x42; //命令字节1
send[2]=0x43;
Counter=3;
send[3]=CheckSum(Counter-1); //校验和计算
Send();
}
//*********************************************
//延时函数
//*********************************************
void Delay( unsigned int m)
{
unsigned int i;
for(i=0;i<=m;i++)
{}
}
2.SLAVE
// Lin总线
//---------------------------------------------
// 文件名: slave(mew).c
// 版本: V1.0
// 日期: 13/12/06
// 功能: Lin 总线从节点
// 编译环境: HiTech PIC C compiler v.8.05
// 使用MCU: PIC16F876(主频4M)
// 接收说明:
// 首先判断同步间隔场:符合要求进行下面操作,否则不予理睬并记录错误
// 同步间隔采集合格:采集同步头,主节点发送0x55,接收5个下降沿用于计算波特率
// 波特率计算成功:串口接收打开,进行ID接收,判断是否自己ID不是放弃接收后面数据
// ID校验成功:继续接收后面数据场,并进行校验,合格则进行相应操作,不合格记录错误
// 初始化,进行下一个同步头接收
//
//--------------------------------------------
#include
//--------------------------------------------
//---------------常量定义----------------------
#define TSync 1000 //同步间隔时间定义
#define FRest 0 //状态复位0
#define FSync 1 //同步间隔场检测
#define FSync1 2 //同步间隔场检测状态1
#define FTow 3 //同步头接收
#define FId 4 //地址字节接收
#define FData 5 //数据接收
#define CS RC1 //片选引脚,高有效
#define RFIN RC2 //同步间隔场和同步场输入端
#define Jiance RB7 //检测引脚,测试时候用
//--------------数据定义-----------------------
unsigned char Data[9]; //数据接收数组
bank1 unsigned int TimeData[5]; //CCP1捕捉时间记录函数
unsigned char RFdata; //状态判断
unsigned char FallCount; //同步头的下降沿计数
unsigned int Sytime; //同步头总时间保存
unsigned char RFstate; //接收状态指示
unsigned char Towallow; //波特率计算允许标志
unsigned char DataCount; //接收数据字节个数寄存器
unsigned char y;
volatile bit IDcheck; //数据校验合格标志
volatile bit Operate; //操作允许标志
volatile bit DatCheck; //数据和校验成功标志
//bank1 unsigned char Stat @ 0x8f; //
unsigned char IDmoment ; //ID号暂时保存
//#define AVR(adr,biti) ((unsigned)(&adr)*8+(biti))
//#define ID0 AVR(Stat,0) //位定义
//#define ID1 AVR(Stat,1)
//#define ID2 AVR(Stat,2)
//#define ID3 AVR(Stat,3)
//#define ID4 AVR(Stat,4)
//#define ID5 AVR(Stat,5)
//#define ID6 AVR(Stat,6)
//#define ID7 AVR(Stat,7)
union
{
struct
{
unsigned b0:1; //0位,冒号后面的是占的位数
unsigned b1:1;
unsigned b2:1;
unsigned b3:1;
unsigned b4:1;
unsigned b5:1;
unsigned b6:1;
unsigned b7:1;
}onebit;
unsigned char allbit;
}all;
#define ID0 all.onebit.b0
#define ID1 all.onebit.b1
#define ID2 all.onebit.b2
#define ID3 all.onebit.b3
#define ID4 all.onebit.b4
#define ID5 all.onebit.b5
#define ID6 all.onebit.b6
#define ID7 all.onebit.b7
#define Stat all.allbit
/*
上面位定义也可以用绝对寻址位操作(但不建议)
或者结构和联合体
应用方法:
all.onebit.b0=1; //给位0置1
all.allbit=0; //字节清0
*/
#define STATUSIT(avr,s) ((unsigned)(&avr)*8+(s)) //绝对寻址定义
static bit C @ STATUSIT(STATUS,0); //对进位位进行定义
union //16位时间计数器
{
unsigned int Counter;
unsigned char Time[2];
}ASD;
#define TimeL ASD.Time[0]
#define TimeH ASD.Time[1]
//--------------函数定义-----------------------
void Picint(void); //初始化
void Delay(void); //延时
void DataIn(void); //数据输入判断
void interrupt SDI(void); //中断函数
void DataClear(void); //数组清0
void Dispose(void); //数据处理函数
void DataCdc(unsigned char asd); //数据校验
//---------------------------------------------
//********************************************
//主函数
//********************************************
void main()
{
Picint();
while(1)
{
DataIn(); //数据查询
if(Operate) //是否允许操作,进行相应操作
{
DataCdc(DataCount); //数据校验是否合格
if(DatCheck)
{
Dispose();
}
}
}
}
//********************************************
//数据处理函数
//********************************************
void Dispose() //实验,并没具体做什么操作
{
if((Data[0]==0x12)&(Data[1]==0x13))
{
RB7=1;
}
if(Data[0]==0x22)
{
RB7=0;
}
}
//********************************************
//数据校验
//********************************************
void DataCdc(unsigned char asd) //计算数据校验和
{
unsigned char x,y;
DatCheck=0;
y=0;
asm("bcf _STATUS,0"); //清进位位
for(x=0;x<(asd-1);x++)
{
y=y+Data[x];
if(C)
{
C=0;
y=y+1;
}
}
y=y+Data[asd-1];
if(y==0xff) //校验成功
{
DatCheck=1;
}
}
//********************************************
//中断函数
//********************************************
void interrupt SDI() //用捕捉方式进行下降沿计数,计算同步头
{
if(CCP1IE&CCP1IF) //确认中断
{
CCP1IF=0; //清中断标志
TimeL=CCPR1L; //取数
TimeH=CCPR1H; //
TimeData[FallCount]=ASD.Counter; //写入数组
FallCount++;
if(FallCount==5)
{
CCP1CON=0x00; //禁止CCP1捕捉
CCP1IE=0; //禁止CCP1中断
CCP1IF=0; //清中断标志
FallCount=0;
Towallow=1; //波特率计算允许
TMR1ON=0;
TMR1L=0;
TMR1H=0;
}
}
}
//********************************************
//数据输入
//********************************************
void DataIn()
{
switch(RFstate)
{
case FSync: //同步间隔场检测
RFdata=RFIN; //采样值
if(RFdata==0) //下降沿检测
{
TMR1L=0;
TMR1H=0;
TMR1ON=1; //开启定时器1
RFstate=FSync1; //转向状态1
}
break;
case FSync1:
RFdata=RFIN;
if(RFdata==0)
{
if(TMR1IF) //在很长时间内如果主机的同步间隔场没有完成产生错误
{
TMR1IF=0;
TMR1ON=0;
TMR1L=0;
TMR1H=0;
RFstate=FRest; //状态机复位
}
}
else
{
TMR1ON=0; //关闭定时器
TimeL=TMR1L; //取出计数值
TimeH=TMR1H;
TMR1L=0; //计数器清0
TMR1H=0;
if(ASD.Counter>=TSync)
{
RFstate=FTow; //间隔场接收成功,下面接收同步头
TMR1ON=1;
CCP1IE=1; //使能CCP1中断
CCP1IF=0; //清中断标志
CCP1CON=0x04; //配置捕捉方式,捕捉下降沿
}
else
{
RFstate=FRest; //否则指向复位
}
}
break;
case FTow: //同步头接收
if(Towallow)
{
unsigned char i;
// SPBRG=0x19; //波特率设置
// RFstate=FId; //指向ID场接收
// CREN=1; //使能接收
Sytime=0;
for(i=0;i<4;i++) //循环计算总数据
{
Sytime=Sytime+(TimeData[i+1]-TimeData[i]);
}
Sytime=Sytime>>3; //除8计算
Sytime=(Sytime>>2)-1;
i=(unsigned char)Sytime;//强制转换为字节型数据,设定波特率
if((i<=30)&(i>=20)) //给定计算范围
{
SPBRG=0x19; //波特率设置
RFstate=FId; //指向ID场接收
CREN=1; //使能接收
}
else
{
RFstate=FRest; //状态复位
}
}
break;
case FId:
while(1) //等待数据接收
{
if(RCIF==1) break;
}
IDmoment=RCREG; //数据读取,清中断标
Stat=IDmoment;
if((Stat&0x0f)==0x01) //判断是否是自己的ID地址,如果是自己的ID地址,校验暂时没加
{
Stat=IDmoment;
if((ID4==0)&(ID5==0))
{
DataCount=3; //两个数据接收,同时还有1个校验字节
}
if((ID4==1)&(ID5==0))
{
DataCount=3; //两个数据接收,同时还有1个校验字节
}
if((ID4==0)&(ID5==1))
{
DataCount=5; //四个数据接收,同时还有1个校验字节
}
if((ID4==1)&(ID5==1))
{
DataCount=9; //八个数据接收,同时还有1个校验字节
}
RFstate=FData; //指向数据接收
}
else
{
RFstate=FRest; //复位
}
break;
case FData: //数据接收
for(y=0;y
while(1)
{
if(RCIF==1) break;
}
Data[y]=RCREG; //读取一个数据
}
RFstate=FRest; //复位,数据接收完毕
Operate=1; //允许操作
break;
default:
RFstate=FSync; //指向同步头接收
Towallow=0; //波特率计算禁止
FallCount=0;
CCP1CON=0x00; //CCP禁止
TXEN=0; //禁止发送
CREN=0; //异步模式禁止接收
DataCount=0; //数据长度清0
Operate=0;
}
}
//********************************************
//初始化
//********************************************
void Picint()
{
INTCON=0; //清中断源
GIE=1; //开总中断
PEIE=1; //开外部中断
RCIE=0; //禁止串口接收中断
ADCON1=0x07; //定义A口为数字ioput模
//TRISA= 0xFF; //定义端口输入输出模式
TRISC1=0; //片选输出端口
TRISC2=1; //捕捉模块1输入端
TRISB7=0; //测试使用的输出引脚
RB7=0;
SPBRG=0x19; //定义波特率
TXSTA=0x04; //使能高速波特率,异步模式
RCSTA=0x80; //使能串口,禁止连续接收
TRISC6=1; //串口使能后这两位必须设置为输入高阻抗状态
TRISC7=1;
T1CON=0x00; //定时器1初始化,用于捕捉功能
TMR1L=0x00;
TMR1H=0x00;
TMR1IE=0; //中断标志以及中断清除
TMR1IF=0;
CS=1; //片选置高
RFstate=FRest; //状态机复位
Operate;
DataClear(); //数组清0
}
//********************************************
//数组清0 函数
//********************************************
void DataClear()
{
for(y=0;y<9;y++)
{
Data[y]=0;
}
}
上一篇:PIC18F2680的CAN总线设计
下一篇:用Pic单片机控制8路MG995舵机(servo motor)的实现方法
推荐阅读最新更新时间:2024-03-16 15:09