基於雙口RAM的單片機通信,軟件程序:
#define _DPRAMCOMM_H
#include < reg52.h> // 引用標準庫的頭文件
#include < absacc.h>
#define uchar unsigned char
#define LP_STT_SEM XBYTE[0x0000] // 左端狀態旗語
#define LP_PRO_SEM XBYTE[0x0001] // 左端配置旗語
#define RP_STT_SEM XBYTE[0x0002] // 右端狀態旗語
#define RP_PRO_SEM XBYTE[0x0003] // 右端配置旗語
#define INTL_SEM XBYTE[0x0004] // 左中斷旗語
#define INTR_SEM XBYTE[0x0005] // 右中斷旗語
#define DPRAM_INTL XBYTE[0x2FFF] // 右端口中斷
#define DPRAM_INTR XBYTE[0x2FFE] // 左端口中斷
#define READY 11 // 0x11表示準備就緒
bit get_sem(uchar *sem_type);
void InitProvRP(void);
void Prov(void);
void FillState(void);
void GetState(void);
uchar int0flag; // 外部中斷0標志
uchar rdyflag; // 另一端準備好標志
uchar ProvTimes; // 表示配置次數
uchar xdata *LpStateRamAddr; // 雙口RAM左端狀態空間起始地址
uchar xdata *LpProvRamAddr; // 雙口RAM左端配置空間起始地址
uchar xdata *RpStateRamAddr; // 雙口RAM右端狀態空間起始地址
uchar xdata *RpProvRamAddr; // 雙口RAM右端配置空間起始地址
uchar xdata ArrayState[254]; // 存放狀態信息的數組
/* 40ms定時中斷服務子程序:
定期更新左端單片機的狀態信息,查詢右端單片機的狀態信息*/
void timer0_int() interrupt 1 using 1
{
TR0 = 0; // 關閉T0
TH0 = 0x70; // 重置40ms定時器的計數初值
TL0 = 0x00;
FillState(); // 定期更新左端單片機狀態讓右端單片機可查詢
GetState(); // 定期查詢右端單片機的狀態信息
}
/* 外部中斷0服務子程序:設置中斷標志位int0flag,讀清中斷*/
void out_int0() interrupt 0 using 1
{
uchar ch;
int0flag = 1; // 表示外部中斷0,實際是雙口RAM產生的中斷
get_sem(&INTL_SEM); // 申請并獲得左中斷旗語
ch = DPRAM_INTR; // 讀清中斷
INTL_SEM = 0x01; // 釋放左中斷旗語
}
/* 主程序 */
void main()
{
int0flag = 0;
rdyflag = 0;
ProvTimes = 0;
LpStateRamAddr = 0x2000;
LpProvRamAddr = 0x2400;
RpStateRamAddr = 0x3000;
RpProvRamAddr = 0x3400;
/* 等待右端單片機準備就緒 */
while(rdyflag!=1)
{
get_sem(&RP_STT_SEM); // 申請并獲得右端狀態旗語
if (*RpStateRamAddr == READY)
rdyflag = 1; // 右端單片機準備就緒標志置1
RP_STT_SEM = 0x01; // 釋放右端狀態旗語
}
/* 對右端單片機進行初始配置 */
InitProvRP();
/* 通過向左端狀態空間的第一地址單元寫READY向右端表示左端準備就緒 */
get_sem(&LP_STT_SEM); // 申請并獲得左端狀態旗語
*LpStateRamAddr = READY; // 左端單片機準備就緒
LP_STT_SEM = 0x01; // 釋放左端狀態旗語
ProvTimes++; // 對右端口的配置次數加1
EA = 1; // 開CPU中斷
EX0 = 1; // 開外部中斷0
ET0 =1; // 開T/C0中斷
PX0 = 0; // 外部中斷低優先級
PT0 = 1; // 計數器高優先級
TMOD = 0x01; // T/C0工作在方式1
TH0 = 0x70; // 預置40ms定時器的計數初值
TL0 = 0x00;
TR0 = 0; // 不啟動T0
/* 右端單片機接收左端對其的初始化配置,運行正常后觸發雙口RAM的
左端中斷,左端單片機受中斷觸發后對右端單片機作第二次配置,并啟
動40ms定時器,開始定期更新本機的狀態信息并監測右端單片機的狀態 */
while(int0flag==1)
{
if (ProvTimes==1)
{
ProvTimes++; // 對右端口的配置次數加1
Prov(); // 對右端口單片機二次配置
/* 通過出發右端中斷,通知右端單片機接受二次配置*/
get_sem(&INTR_SEM); // 申請并獲得右中斷旗語
DPRAM_INTL = 0xFF; // ITNR腳為低,出發右端單片機中斷
INTR_SEM = 0x01; // 釋放右中斷旗語
}
TR0 = 1; // 啟動40ms定時器T0
}
}
/* 申請并獲得旗語函數 */
bit get_sem(uchar *sem_type)
{
*sem_type = 0x00; // 申請旗語
while((*sem_type!=0x00)); // 無限循環直至獲得旗語
return(1);
}
/* 對右端單片機的初始化配置函數:為簡化起見,通過向左端的配置
空間2500H~25FFH全寫0x22,表示對右端單片機的初始配置命令 */
void InitProvRP(void)
{
uchar i;
get_sem(&LP_PRO_SEM); // 申請左端配置旗語
for (i=0;i++;i<=255)
*(LpProvRamAddr+i) = 0x22;
LP_PRO_SEM = 0x01; // 釋放左端配置旗語
}
/* 對右端單片機的二次配置函數:為簡化起見,通過向左端的配置
空間2500H~25FFH全寫0x33,表示對右端單片機的初始配置命令 */
void Prov(void)
{
uchar i;
get_sem(&LP_PRO_SEM); // 申請左端配置旗語
for (i=0;i++;i<=255)
*(LpProvRamAddr+i) = 0x33;
LP_PRO_SEM = 0x01; // 釋放左端配置旗語
}
/* 更新本機狀態函數:為了簡化起見,此函數表示為向左端狀態空
間第一地址單元(存放設備就緒信息)以后的254字節全寫0x44 */
void FillState(void)
{
uchar i;
get_sem(&LP_STT_SEM); // 申請并獲得左端狀態旗語
for (i=0;i++;i<=254)
*(LpStateRamAddr+i+1) = 0x44;
LP_STT_SEM = 0x01; // 釋放左端狀態旗語
}
/* 查詢另一端單片機狀態函數:為簡化起見,此函數表示為用數組
ArrayState存取右端狀態空間第一地址單元(存放設備就緒信息)
以后的254字節(3001H~30FFH)包含的狀態信息 */
void GetState(void)
{
uchar i;
get_sem(&RP_STT_SEM); // 申請并獲得右端狀態旗語
for (i=0;i++;i<=254)
ArrayState[i] = *(RpStateRamAddr+i+1);
RP_STT_SEM = 0x01; // 釋放右端狀態旗語
}