Ⅰ 求:51單片機模擬i2c匯流排程序
//給你一個簡單的可斷電保存的計時程序,用的是24C08
#include <reg52.h> // 包含51單片機寄存器定義的頭文件
#include <intrins.h> //包含_nop_()函數定義的頭文件
#define OP_READ 0xa1 // 器件地址以及讀取操作,0xa1即為1010 0001B
#define OP_WRITE 0xa0 // 器件地址以及寫入操作,0xa1即為1010 0000B
sbit SCL=P3^4; //將串列時鍾匯流排SCL位定義在為P3.4引腳
sbit SDA=P3^5; //將串列數據匯流排SDA位定義在為P3.5引腳
unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//定義共陽數碼管顯示字型碼
unsigned char sec=0; //定義計數值,每過1秒,sec加1
unsigned int count; //定時中斷次數
bit write=0; //寫24C08的標志;
sbit shiwei=P2^6; //十位選通定義
sbit gewei=P2^7; //個位選通定義
sbit K5=P3^2; //清0按鍵
/*****************************************************
函數功能:延時1ms
***************************************************/
void delay1ms()
{
unsigned char i,j;
for(i=0;i<10;i++)
for(j=0;j<33;j++)
;
}
/*****************************************************
函數功能:延時若干毫秒
入口參數:n
***************************************************/
void delaynms(unsigned char n)
{
unsigned char i;
for(i=0;i<n;i++)
delay1ms();
}
/***************************************************
函數功能:開始數據傳送
***************************************************/
void start()
// 開始位
{
SDA = 1; //SDA初始化為高電平「1」
SCL = 1; //開始數據傳送時,要求SCL為高電平「1」
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
SDA = 0; //SDA的下降沿被認為是開始信號
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
SCL = 0; //SCL為低電平時,SDA上數據才允許變化(即允許以後的數據傳遞)
}
/***************************************************
函數功能:結束數據傳送
***************************************************/
void stop()// 停止位
{
SDA = 0; //SDA初始化為低電平「0」 _n
SCL = 1; //結束數據傳送時,要求SCL為高電平「1」
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
SDA = 1; //SDA的上升沿被認為是結束信號
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
SDA=0;
SCL=0;
}
/***************************************************
函數功能:檢測應答位
***************************************************/
bit Ask() //檢測應答
{
bit ack_bit; //儲存應答位
SDA = 1; // 發送設備(主機)應在時鍾脈沖的高電平期間(SCL=1)釋放SDA線,
//以讓SDA線轉由接收設備(AT24Cxx)控制
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
SCL = 1; //根據上述規定,SCL應為高電平
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
ack_bit = SDA; //接受設備(AT24Cxx)向SDA送低電平,表示已經接收到一個位元組
//若送高電平,表示沒有接收到,傳送異常 結束發送
SCL = 0; //SCL為低電平時,SDA上數據才允許變化(即允許以後的數據傳遞)
return ack_bit; // 返回AT24Cxx應答位
}
/***************************************************
函數功能:從AT24Cxx讀取數據
出口參數:x
***************************************************/
unsigned char ReadData()
// 從AT24Cxx移入數據到MCU
{
unsigned char i;
unsigned char x; //儲存從AT24Cxx中讀出的數據
for(i = 0; i < 8; i++)
{
SCL = 1; //SCL置為高電平
x<<=1; //將x中的各二進位向左移一位
x|=(unsigned char)SDA; //將SDA上的數據通過按位「或「運算存入x中
SCL = 0; //在SCL的下降沿讀出數據
}
return(x); //將讀取的數據返回
}
/***************************************************
函數功能:向AT24Cxx的當前地址寫入數據
入口參數:y (儲存待寫入的數據)
***************************************************/
//在調用此數據寫入函數前需首先調用開始函數start(),所以SCL=0
void WriteCurrent(unsigned char y)
{
unsigned char i;
for(i = 0; i < 8; i++) // 循環移入8個位
{
SDA = (bit)(y&0x80); //通過按位「與」運算將最高位數據送到S
//因為傳送時高位在前,低位在後
_nop_(); //等待一個機器周期
SCL = 1; //在SCL的上升沿將數據寫入AT24Cxx
_nop_(); //等待一個機器周期
_nop_(); //等待一個機器周期
SCL = 0; //將SCL重新置為低電平,以在SCL線形成傳送數據所需的8個脈沖
y <<= 1; //將y中的各二進位向左移一位
}
}
/***************************************************
函數功能:向AT24Cxx中的指定地址寫入數據
入口參數:add (儲存指定的地址);dat(儲存待寫入的數據)
***************************************************/
void WriteSet(unsigned char add, unsigned char dat)
// 在指定地址addr處寫入數據WriteCurrent
{
start(); //開始數據傳遞
WriteCurrent(OP_WRITE); //選擇要操作的AT24Cxx晶元,並告知要對其寫入數據
Ask();
WriteCurrent(add); //寫入指定地址
Ask();
WriteCurrent(dat); //向當前地址(上面指定的地址)寫入數據
Ask();
stop(); //停止數據傳遞
delaynms(4); //1個位元組的寫入周期為1ms, 最好延時1ms以上
}
/***************************************************
函數功能:從AT24Cxx中的當前地址讀取數據
出口參數:x (儲存讀出的數據)
***************************************************/
unsigned char ReadCurrent()
{
unsigned char x;
start(); //開始數據傳遞
WriteCurrent(OP_READ); //選擇要操作的AT24Cxx晶元,並告知要讀其數據
Ask();
x=ReadData(); //將讀取的數據存入x
stop(); //停止數據傳遞
return x; //返回讀取的數據
}
/***************************************************
函數功能:從AT24Cxx中的指定地址讀取數據
入口參數:set_addr
出口參數:x
***************************************************/
unsigned char ReadSet(unsigned char set_addr)
// 在指定地址讀取
{
start(); //開始數據傳遞
WriteCurrent(OP_WRITE); //選擇要操作的AT24Cxx晶元,並告知要對其寫入數據
Ask();
WriteCurrent(set_addr); //寫入指定地址
Ask();
return(ReadCurrent()); //從指定地址讀出數據並返回
}
/***********************************************************/
void LEDshow() //LED顯示函數
{
P0=table[sec/10];
shiwei=0;
delaynms(2);
shiwei=1;
P0=table[sec%10];
gewei=0;
delaynms(2);
gewei=1;
}
/***********************************************************/
/***************************************************
函數功能:主函數
***************************************************/
void main(void)
{
TMOD=0x01; //定時器0工作在方式1
ET0=1;
EA=1;
TH0=(65536-50000)/256; //對TH0 TL0賦值
TL0=(65536-50000)%256; //使定時器0.05秒中斷一次
SDA = 1; // SDA=1,SCL=1,使主從設備處於空閑狀態
SCL = 1;
sec=ReadSet(2);//讀出保存的數據賦於sec
TR0=1; //開始計時
while(1)
{
LEDshow();
if(write==1) //判斷計時器是否計時一秒
{
write=0; //清零
WriteSet(2,sec); //在24c08的地址2中寫入數據sec
}
if(K5==0){
delaynms(10);
if(K5==0){
sec=0;
}
}
}
}
/**************************************************************/
void t0(void) interrupt 1 using 0 //定時中斷服務函數
{
TH0=(65536-50000)/256; //對TH0 TL0賦值
TL0=(65536-50000)%256; //重裝計數初值
count++; //每過50ms tcnt加一
if(count==20) //計滿20次(1秒)時
{
count=0; //重新再計
sec++;
write=1; //1秒寫一次24C08
if(sec==100) //定時100秒,在從零開始計時
{sec=0;}
}
}
Ⅱ 單片機LCD編程
/*************************************************
名 稱:LCD12864_ST7920並行測試程序
試驗功能:測試基於ST7920控制晶元的LCD12864的顯示
並行方式連接;晶振11.0592M
編 寫:JiangX.net
*************************************************/
#include <reg52.h>
#include <ziku.h>
#include <intrins.h>
#include <string.h>
#define uchar unsigned char
#define uint unsigned int //宏定義
#define ulong unsigned long
#define Channal_A 1 //通道A
#define Channal_B 2 //通道B
#define Channal_AB 3 //通道A&B
//**************************一下為函數聲明修**************************************
static void DA_conver(uint Dignum);
extern void Write_A_B(uint Data_A,uint Data_B,uchar Channal,bit Model);
//**************************修改硬體時要修改的部分********************************
sbit DIN = P0^0; //數據輸入端
sbit SCLK = P0^1; //時鍾信號
sbit CS = P0^2; //片選輸入端,低電平有效
/*MAX187介面定義*/
sbit MAX187_CS= P2^6;
sbit MAX187_DO= P2^5;
sbit MAX187_SK= P2^7;
/*****13864LCD介面定義*****/
sbit LCD_RS = P3^7; //1:輸入數據 ;0:輸入命令
sbit LCD_RW = P2^7; //1:讀數據 0:寫數據
sbit LCD_EN = P2^6; //LCM使能端
sbit LCD_PSB = P2^3; //串並口選擇 1:並口模式; 0:串口模式;
#define LCD_DATA P1 //LCD匯流排埠
/*****LCD功能初始化指令*****/
#define CLEAR_SCREEN 0x01 //清屏指令:清屏且AC值為00H
#define AC_INIT 0x02 //將AC設置為00H。且游標移到原點位置
#define CURSE_ADD 0x06 //設定游標移到方向及圖像整體移動方向(默認游標右移,圖像整體不動)
#define FUN_MODE 0x30 //工作模式:8位基本指令集
#define DISPLAY_ON 0x0c //顯示開,顯示游標,且游標位置反白
#define DISPLAY_OFF 0x08 //顯示關
#define CURSE_DIR 0x14 //游標向右移動:AC=AC+1
#define SET_CG_AC 0x40 //設置AC,范圍為:00H~3FH
#define SET_DD_AC 0x80
/*****漢字地址表*****/
uchar code addr_tab[]={ //便於根據漢字坐標求出地址
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,//第一行漢字位置
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,//第二行漢字位置
0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,//第三行漢字位置
0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,//第四行漢字位置
};
//------------全局變數定義-------------
uchar first[]="MAX187 ";
uchar show187[]="12345";
//uchar code shw187[]="123456";
uchar error[]="ERROR!";
ulong vv;
/*****n(ms)延時子程序*****/
void delayms(uint t) //約延時n(ms)
{
uint i;
while(t--)
{
for(i=0;i<125;i++);
}
}
/*****等待LCD忙*****/
void WaitBusy(void) //延時一小段時間,等待LCD空閑
{
uchar i=5;
while(i--);
}
/*****寫指令代碼*****/
void Lcd_WriteCmd(uchar cmdcode)
{
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 1;
WaitBusy();
LCD_DATA = cmdcode;
LCD_EN = 0;
}
/*****寫數據*****/
void Lcd_WriteData(uchar dispdata)
{
LCD_RS = 1;
LCD_RW = 0;
LCD_EN = 1;
WaitBusy();
LCD_DATA = dispdata;
LCD_EN = 0;
}
/*****初始化LCD*****/
void Lcd_Init()
{
LCD_PSB = 1;
delayms(50);
Lcd_WriteCmd(0x30); //選擇基本指令集
delayms(1);
Lcd_WriteCmd(0x30); //選擇8bit數據流
delayms(1);
Lcd_WriteCmd(0x0c); //開顯示(無游標、不反白)
delayms(1);
Lcd_WriteCmd(0x01); //清除顯示,並且設定地址指針為00H
delayms(20);
}
/*****顯示漢字*****/
void hanzi_Disp(uchar x,uchar y,uchar *s)
{ //x、y為漢字坐標
Lcd_WriteCmd(addr_tab[8*x+y]); //寫地址
while(*s>0)
{
Lcd_WriteData(*s); //寫數據
s++;
}
}
/*****顯示圖片(顯示Gdram內容)*****/
/*繪圖顯示的步驟
1 關閉繪圖顯示功能
2 先將垂直的坐標(Y)寫入繪圖RAM地址
3 再將水平的位元組坐標(X)寫入繪圖RAM地址
4 將D15-D8寫入RAM中
5 將D7-D0寫入到RAM中
6 打開繪圖顯示功能*/
//圖片取模方式:橫向取模,位元組正序
void pic_Disp(uchar *pic) //顯示Gdram內容(顯示圖片)
{
uchar x,y,i;
for(i=0;i<9;i=i+8)
for(y=0;y<32;y++)
{
for(x=0;x<8;x++)
{
Lcd_WriteCmd(0x36); //擴充指令,開繪圖顯示
Lcd_WriteCmd(0x80+y); //行地址
Lcd_WriteCmd(0x80+x+i); //列地址
Lcd_WriteData(*pic++); //寫數據 D15-D8
Lcd_WriteData(*pic++); //寫數據 D7-D0
Lcd_WriteCmd(0x30);
}
}
}
/*max187延時程序*/
void delay_MS(uint ms)//普通延時MS級
{ uint i,j;
for(i=0;i<ms;i++)
{for(j=0;j<1141;j++); }
}
void delay_uS(uint us)//普通延時US級
{ uint i,j;
for(i=0;i<us;i++)
{ for(j=0;j<10;j++); }
}
//MAX187讀程序;
unsigned int MAX187read(void)
{
uchar H,L,i;
uint count; //16位變數
MAX187_CS=0; //低電平有效,開始轉換
delay_uS(2); //延時8.5μ等待轉換結束
MAX187_SK=1;
MAX187_SK=0;
H=0; //高位變數清零
for(i=0;i<4;i++) //高4位
{
MAX187_SK=1;
if(MAX187_DO) H++; //位標量,如為1則H加1,如為0則跳過
MAX187_SK=0;
if(i!=3)
H<<=1;
}
L=0;
for(i=0;i<8;i++) //低8位
{
MAX187_SK=1;
if(MAX187_DO) L++; //位標量,如為1則L加1,如為0則跳過
MAX187_SK=0;
if(i!=7)
L<<=1;
}
delay_uS(1);
MAX187_CS=1;
count=0;
count=L;
return count;
}
void MAX187_data()
{
vv=MAX187read();
vv=vv*2*4096/4093;
show187[0]=vv/1000+'0';
show187[1]='.';
show187[2]=vv/100%10+'0';
show187[3]=vv/10%10+'0';
show187[4]=vv%10+'0';
show187[5]='\0';
}
/*****主函數*****/
void main(void)
{
uchar volt,number;
number=2;
volt=0x30+number;
Lcd_Init();
while(1)
{
Lcd_Init();
hanzi_Disp(0,1,"山東天工機電");
hanzi_Disp(1,2,"智能充電機");
hanzi_Disp(2,0,"www.sdtgjd.com");
hanzi_Disp(3,3,"");
delayms(1000);
Lcd_Init();
hanzi_Disp(1,2,"充電電流:");
//delayms(500);
//Lcd_Init();
MAX187_data();
hanzi_Disp(2,3,show187);
delayms(1500);
Write_A_B(0x0fff,0x0000,Channal_A,0); //測量A通道
//Write_A_B(0x0000,0x0600,Channal_B,1); //測量B通道
// Write_A_B(0x0504,0x0a47,Channal_AB,1); //測量AB通道,測量時需屏蔽上面兩句
//while(1);
}
}
//=================================================================
// 函數名稱 :void DA_conver(uint Dignum)
// 函數功能 :進行DA轉換
// 入口參數 :Dignum:根據說明設置轉化數據.頭四位為特殊位用於選擇轉化方式,
// 以及用於通道選擇.請自行設置.後12位為需要轉換的值
// 出口參數 :無
//=================================================================
void DA_conver(uint Dignum)
{
uint Dig=0;
uchar i=0;
SCLK=1;
CS=0; //片選有效
for(i=0;i<16;i++) //寫入16為Bit的控制位和數據
{
Dig=Dignum&0x8000;
if(Dig)
{
DIN=1;
}
else
{
DIN=0;
}
SCLK=0;
_nop_();
Dignum<<=1;
SCLK=1;
_nop_();
}
SCLK=1;
CS=1; //片選無效
}
//=================================================================
// 函數名稱 :void Write_A_B(uint Data_A,uint Data_B,uchar Channal,bit Model)
// 函數功能 :模式、通道選擇並進行DA轉換
// 入口參數 :Data_A:A通道轉換的電壓值
// Data_B:B通道轉換的電壓值
// Channal:通道選擇,其值為Channal_A,Channal_B,或Channal_AB
// Model:速度控制位 0:slow mode 1:fast mode
// 出口參數 :無
// 說明: Data_A,Data_B的范圍為:0-0x0fff
// 本程序如果只需要一個通道時,另外一個通道的值可任意,但是不能預設
//=================================================================
void Write_A_B(uint Data_A,uint Data_B,uchar Channal,bit Model)
{
uint Temp;
if(Model)
{
Temp=0x4000;
}
else
{
Temp=0x0000;
}
switch(Channal)
{
case Channal_A: //A通道
DA_conver(Temp|0x8000|(0x0fff&Data_A));
break;
case Channal_B: //B通道
DA_conver(Temp|0x0000|(0x0fff&Data_B));
break;
case Channal_AB:
DA_conver(Temp|0x1000|(0x0fff&Data_B)); //A&B通道
DA_conver(Temp|0x8000|(0x0fff&Data_A));
break;
default:
break;
}
}
Ⅲ 單片機89c51的電子時鍾課程設計
#include <reg52.h>
#include<stddef.h>
#define uchar unsigned char
#define uint unsigned int
#define LCD1602_FLAG
#define LCD1602_PORT P0
sbit lcd1602_rs=P2^0;
sbit lcd1602_e=P2^2;
sbit lcd1602_rw=P2^1;
sbit lcd1602_busy=P0^7;
sbit key_ch=P3^5;
sbit key_add=P3^6;
sbit key_minus=P3^7;
uchar i,sec,min,h,date,month,flag;
uint year;
uchar *chgstr[7]={" ","sec","min","hour","date","min","year"};
uchar j,k,m,n,o,p;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
uchar timestr[10],datestr[10];
void init();
void delay(uint);
void time_display();
void date_display();
void control();
void time();
/*
************************************
* 函數名稱:lcd1602_CheckBusy()
* 函數功能:狀態查詢
************************************
*/
void lcd1602_CheckBusy()
{
do
{
lcd1602_busy=1;
lcd1602_rs=0;
lcd1602_rw=1;
lcd1602_e=0;
lcd1602_e=1;
}
while(lcd1602_busy);
}
/*
***************************************
* 函數名稱: lcd1602_WriteCmd()
* 函數功能:寫命令
* 入口參數:命令字
* 出口參數:無
***************************************
*/
void lcd1602_WriteCmd(const uchar cmd)
{
lcd1602_CheckBusy();
lcd1602_rs=0;
lcd1602_rw=0;
lcd1602_e=1;
LCD1602_PORT=cmd;
lcd1602_e=0;
}
/*
*******************************************
* 函數名稱:lcd1602_WriteData()
* 函數功能:寫數據
* 入口參數:c--待寫數據
* 出口參數:無
*********************************************
*/
void lcd1602_WriteData(const uchar c)
{
lcd1602_CheckBusy();
lcd1602_rs=1;
lcd1602_rw=0;
lcd1602_e=1;
LCD1602_PORT=c;
lcd1602_e=0;
}
/*
***********************************************
* 函數名稱:lcd1602_Init()
* 函數功能:初始化LCD
* 入口參數:無
* 出口參數:無
***********************************************
*/
void lcd1602_Init()
{
lcd1602_WriteCmd(0x38); //顯示模式為8位2行5*7點陣
lcd1602_WriteCmd(0x0c); //display enable,flag enable,flash enable,
lcd1602_WriteCmd(0x06); //flag move to right,screen don't move
lcd1602_WriteCmd(0x01); //clear screen
}
/*
************************************************
* 函數名稱:lcd1602_Display()
* 函數功能: 字元顯示
* 入口參數:ptr--字元或字元串指針
* 出口參數:無
* 說 明:用戶可通過以下方式來調用:
* 1)lcd1602_Display("Hello,world!");
* 2) INT8U 存儲類型 txt[]="要顯示的字元串";
* 或者 INT8U 存儲類型 txt[]={'t','x','t',..,'