📄 can_self.c
字号:
/**************************************************************************************
项 目:基于CAN总线的收发通信(节点1)
说 明:主程序部分
功 能:CAN总线上挂接两个节点,当其中一个节点(节点1)的按键按下时,除了自身的数码管1、2
显示加一,还向节点2发送一个远程帧;节点2接收到远程帧后想总线发送一个数据帧,如果
节点2的按键被按下时,除了自身的数码管1、2显示加一,还会向总线发送数据帧,使得节点
2的数码管3、4显示与其保持一致。
// CAN主要参数: PeliCAN模式,扩展帧EFF模式
// 29位标示码结构:发送一个远程帧
// 本节点的接收代码寄存器值: 0x55,0x22,0x33,0x44
// 本节点的屏蔽代码寄存器值:0x00,0xff,0x00,0xff;可以接收节点2的数据
// 目的节点地址:0x11,0x22,0x33,0x44;可以被节点1接收
模 块:can_self.c
作 者:PIAE GROUP
注释修改者:特 权
修改 时间:08.6.17.
**************************************************************************************/
/***感谢PIAE工作组提供的源码,这里特权根据自己的编程习惯做了一些修改并添加详细注释***/
#include <reg52.h>
#include <intrins.h>
#include "define.h"
///////////////////////////////////////////////
//函数:inter0_key (外部中断INT0)
//说明:INT0按键为计数按键
// 每按下一次键,计数值加一
//入口:按键中断
//返回:按键加一
///////////////////////////////////////////////
void inter0_key(void) interrupt 0
{
EA = 0; //关闭中断
Txd_data++; //计数结果增1,即待发送的数据增1
TXD_flag = 1; //发送数据标志位置位,即重新发送数据以更新数码管的显示数值
EA = 1; //重新开启中断
}
///////////////////////////////////////////////
//函数:inter1_can_rxd (外部中断INT1)
//说明:接收数据函数,在中断服务程序中调用
//入口:无
//返回:无
///////////////////////////////////////////////
void inter1_can_rxd( void ) interrupt 2
{
uchar state;
EA = 0; //关CPU中断
IE1 = 0; //由于是中断INT1是电平触发方式,所以需要软件将INT1的中断请求标志IE0清零
state = IR; //IR为SJA1000的中断寄存器
if( state & 0x01) //若IR.0=1--接收中断
{ //接收数据帧
RX_buffer[0] = RBSR0;
RX_buffer[1] = RBSR1;
RX_buffer[2] = RBSR2;
RX_buffer[3] = RBSR3;
RX_buffer[4] = RBSR4;
RX_buffer[5] = RBSR5;
RX_buffer[6] = RBSR6;
RX_buffer[7] = RBSR7;
RX_buffer[8] = RBSR8;
RX_buffer[9] = RBSR9;
RX_buffer[10] = RBSR10;
RX_buffer[11] = RBSR11;
RX_buffer[12] = RBSR12;
RXD_flag = 1; //接收标志置位,以便进入接收处理程序
CMR = 0x04; //CMR.2=1--接收完毕,释放接收缓冲器
state = ALC; //释放仲裁随时捕捉寄存器(读该寄存器即可)
state = ECC; //释放错误代码捕捉寄存器(读该寄存器即可)
}
IER = 0x01; // IER.0=1--接收中断使能
EA = 1; //重新开启CPU中断
}
///////////////////////////////////////////////
//函数:main
//说明:主函数
//入口:无
//返回:无
///////////////////////////////////////////////
void main(void)
{
init_mcu();
init_sja1000();
while(1)
{
rxd_deal(); //接收处理程序
txd_deal(); //发送处理程序
led_show(0,Txd_data); //数码管1-2显示发送数据子程序
led_show(1,Rxd_data); //数码管3-4显示接收数据子程序
}
}
///////////////////////////////////////////////
//函数:init_mcu
//说明:单片机I/O口初始化
// 主要是各中断寄存器的初始化
//入口:无
//返回:无
///////////////////////////////////////////////
void init_mcu(void)
{
SJA_RST = 1; //CAN总线复位管脚复位无效
SJA_CS = 0; //CAN总线片选有效
EX1 = 1; //开MCU外部中断INT1
IT1 = 0; //MCU外部中断INT1设置为电平触发,该中断口连接CAN总线接收中断口
IT0 = 1; //MCU外部中断INT0设置为下降沿触发
EX0 = 1; //开MCU外部中断INT0
EA = 1; //开MCU总中断
SJA_CS = 1; //CAN总线片选无效,使得对数据总线的操作不会影响SJA1000。
}
///////////////////////////////////////////////
//函数:init_sja1000
//说明:独立CAN控制器SJA1000的初始化
//入口:无
//返回:无
///////////////////////////////////////////////
void init_sja1000(void)
{
uchar state;
uchar ACRR[4];
uchar AMRR[4];
// 接收代码寄存器
ACRR[0] = 0x55;
ACRR[1] = 0x22;
ACRR[2] = 0x33;
ACRR[3] = 0x44;
// 接收屏蔽寄存器
AMRR[0] = 0x00;
AMRR[1] = 0Xff;
AMRR[2] = 0x00;
AMRR[3] = 0xff;
// 使用do--while语句确保进入复位模式
do
{
MODR = 0x09; // 设置MOD.0=1--进入复位模式,以便设置相应的寄存器
state = MODR;
}
while( !(state & 0x01) );
// 对SJA1000部分寄存器进行初始化设置
CDR = 0x88; // CDR为时钟分频器,CDR.3=1--时钟关闭, CDR.7=0---basic CAN, CDR.7=1---Peli CAN
BTR0 = 0x31; // 总线定时寄存器0 ;总线波特率设定
BTR1 = 0x1c; // 总线定时寄存器1 ;总线波特率设定
IER = 0x01; // IER.0=1--接收中断使能; IER.1=0--关闭发送中断使能
OCR = 0xaa; // 配置输出控制寄存器
CMR = 0x04; // 释放接收缓冲器
// 初始化接收代码寄存器
ACR0 = ACRR[0];
ACR1 = ACRR[1];
ACR2 = ACRR[2];
ACR3 = ACRR[3];
// 初始化接收屏蔽寄存器
AMR0 = AMRR[0];
AMR1 = AMRR[1];
AMR2 = AMRR[2];
AMR3 = AMRR[3];
// 使用do--while语句确保退出复位模式
do
{
MODR = 0x08; //MOD.3=0--双滤波器模式
state = MODR;
}
while( state & 0x01 );
}
///////////////////////////////////////////////
//函数:rxd_deal
//说明:接收处理程序;检测接收标志状态位,
// 如果置位则进行接收处理
//入口:无
//返回:无
///////////////////////////////////////////////
void rxd_deal(void)
{
if( RXD_flag ) //RXD_flag=0说明无数据可以接收,RXD_flag=1说明有数据可以接收
{
EA = 0; //关闭CPU中断
RXD_flag = 0; //RXD_flag清零,以便下次查询
Rxd_data = RX_buffer[5]; //CAN总线要接收的数据,也是要在数码管3-4位置显示的数据
EA = 1; //重新开启CPU中断
}
}
///////////////////////////////////////////////
//函数:txd_deal
//说明:发送处理程序;检测发送标志状态位,
// 如果置位则进行发送数据处理
//入口:无
//返回:无
///////////////////////////////////////////////
void txd_deal(void)
{
if( TXD_flag ) //若TXD_flag=1,要求进行数据的发送处理
{
_nop_();
TXD_flag = 0; //RXD_flag清零,以便下次查询
can_txd(); //发送数据帧
_nop_();
_nop_();
} //发送数据帧后,SJA1000将产生接收中断
}
///////////////////////////////////////////////
//函数:can_txd
//说明:发送扩展数据帧
//入口:无
//返回:无
///////////////////////////////////////////////
void can_txd(void)
{
uchar state;
uchar TX_buffer[ N_can ] ; //N_can=13,TX_buffer数组为待传送的数据帧
//初始化标识码头信息
TX_buffer[0] = 0xc8; //.7=0--扩展帧;.6=1--远程传送请求;
TX_buffer[1] = 0x11; //本帧信息的ID
TX_buffer[2] = 0x22;
TX_buffer[3] = 0x33;
TX_buffer[4] = 0x44;
//初始化发送数据单元
TX_buffer[5] = Txd_data; //发送的第1个字节数据,也是数码管要显示的数据(计数结果)
TX_buffer[6] = 0x22; //2
TX_buffer[7] = 0x33; //3
TX_buffer[8] = 0x44; //4
TX_buffer[9] = 0x55; //5
TX_buffer[10] = 0x66; //6
TX_buffer[11] = 0x77; //7
TX_buffer[12] = 0x88; //8
//初始化数据信息
EA = 0; //关中断
//查询SJA1000是否处于接收状态,当SJA1000不处于接收状态时才可继续执行
do
{
state = SR; //SR为SJA1000的状态寄存器
LED_RED = 0; //点亮LED1
}
while( state & 0x10 ); //SR.4=1 正在接收,等待
//查询SJA1000是否处于发送完毕状态
do
{
state = SR;
LED_RED = 0; //点亮LED1
}
while(!(state & 0x08)); //SR.3=0,发送请求未处理完,等待直到SR.3=1
//查询发送缓冲器状态
do
{
state = SR;
LED_RED = 0; //点亮LED1
}
while(!(state & 0x04)); //SR.2=0,发送缓冲器被锁。等待直到SR.2=1
LED_RED = !LED_RED; //熄灭LED1
LED_GRE = !LED_GRE; //点亮LED2
//将待发送的一帧数据信息存入SJA1000的相应寄存器中
TBSR0 = TX_buffer[0];
TBSR1 = TX_buffer[1];
TBSR2 = TX_buffer[2];
TBSR3 = TX_buffer[3];
TBSR4 = TX_buffer[4];
TBSR5 = TX_buffer[5];
TBSR6 = TX_buffer[6];
TBSR7 = TX_buffer[7];
TBSR8 = TX_buffer[8];
TBSR9 = TX_buffer[9];
TBSR10 = TX_buffer[10];
TBSR11 = TX_buffer[11];
TBSR12 = TX_buffer[12];
CMR = 0x01; //置位发送请求
EA = 1; //重新开启中断
}
///////////////////////////////////////////////
//函数:delay
//说明:延时子函数
//入口:uchar time:延时时间time us
//返回:无
///////////////////////////////////////////////
void delay(uchar time)
{
while(time--);
}
///////////////////////////////////////////////
//函数:led_show
//说明:数码管显示函数
// 注意控制P2口的高4位时,不要影响P2口的低4位值
//入口:uchar wela: wela=1--高两位显示发送数据
// wela=0--低两位显示接收数据
// uchar number: 要显示的数据
//返回:无
///////////////////////////////////////////////
void led_show(uchar wela,uchar number)
{
uchar digit;
uchar temp;
if(wela) //高位显示,即数码管1-2
{
temp = 0x7b; //temp=01111011B
}
else //低位显示,即数码管3-4
{
temp = 0xde; //temp=11011110B
}
//数码管个位显示
digit = number%10;
P0 = led[digit];
P2 = (temp | 0x0f) & P2; //开个位
delay(5);
P2 = 0xff;
P0 = 0xff; //关闭个位
//数码管十位显示
number /= 10;
digit = number%10;
P2 = ((temp << 4) | 0x0f) & P2; //开十位
P0 = led[digit];
delay(5);
P2 = 0xff; //关闭十位
P0 = 0xff;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -