接线
电源模块输出3.3v电源 W25Q32接P0口上拉3.3V 用4个1k电阻限流
/*模式0*/
sbit CS=P0^0;//SS
sbit MISO=P0^1;//从机DO
sbit CLK=P0^2;
sbit MOSI=P0^3;//..DI
LCD1602用i2c转接板接P1^6(SCL) P1^7(SDA)
注意:电源需要首先接到最小系统板给51和1602供电再接到电源模块5v插针给电源模块供电(没错 这样也行。。) 开始先接电源模块再通过插针给最小系统和1602供电不知道为啥1602不显示
乱糟糟的图:
代码备忘
main.c
#include "regx52.h"
#include "intrins.h"
#include "delayms.h"
//#include "stdio.h"
#include "lcd1602i2c.h"
#include "W25Q32.h"
#define uchar unsigned char
#define uint unsigned int
void main()
{
uchar Writes[]="rexsam111",Read[10];
lcd_init();
W25Q_init();
W25Q_SectorErase(0,0xf0); //注释掉这两行测试掉电不丢失
W25Q_PageProgram(0,0xf0,Writes,9);
W25Q_Read(0,0xf0,Read,9);
lcdstr(1,1,Read);
while(1)
{
}
}
SPI.c
#include <REGX52.H>
#define uchar unsigned char
/*模式0*/
sbit CS=P0^0;//SS
sbit MISO=P0^1;//从机DO
sbit CLK=P0^2;
sbit MOSI=P0^3;//..DI
void spi_init()
{
CS=1;
CLK=0;
}
void spi_start(){ CS=0; }
void spi_stop(){ CS=1; }
uchar spi_(uchar Send)
{
uchar Data=0x00,i;
for(i=0;i<8;i++)
{
// MOSI=( Send & (0x80>>i) );
// MOSI=Send&0x80;//上升沿要给从机的数据要提前放好
Send<<=1;
MOSI=CY;
CLK=1;//上升沿主从机读入数据
// if(MISO==1)
// Data|=(0x80>>i);
Data<<=1;
Data|=MISO;
// if(i<7) Data<<=1;
CLK=0;//下降沿从机把数据放上线 主机下个循环头放数据上线
}
return Data;
}
W25Q32.c 草率了 因为不知道可以定义long变量就是32位 就把24位地址分成了高8位和低16位来发
#include <REGX52.H>
#include "SPI.h"
#define uchar unsigned char
#define uint unsigned int
#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3
#define W25Q64_DUMMY_BYTE 0xFF
void W25Q_init()
{
spi_init();
}
void W25Q_readID(uchar *MID,uint *DID)
{
spi_start();
spi_(W25Q64_JEDEC_ID);//发完指令 下一次!!!才能收到结果
*MID=spi_(W25Q64_DUMMY_BYTE);
*DID=spi_(W25Q64_DUMMY_BYTE);
*DID<<=8;
*DID|=spi_(W25Q64_DUMMY_BYTE);
spi_stop();
}
void W25Q_WriteEnable()
{
spi_start();
spi_(W25Q64_WRITE_ENABLE);
spi_stop();
}
void W25Q_WaitBusy()
{
uchar R1,busy;
uchar timeout=100;//不要太长 否则写入失败
spi_start();
do
{
spi_(W25Q64_READ_STATUS_REGISTER_1);
R1=spi_(W25Q64_DUMMY_BYTE);
busy=R1&0x01;
timeout--;
if(!timeout)
{
break;
}
}
while(busy);
spi_stop();
}
//i为数据数组角标
void W25Q_PageProgram(uchar addr23_16,uint addr15_0,uchar *DataArray,uchar i)
{
uchar j;
W25Q_WriteEnable();//写使能
spi_start();
spi_(W25Q64_PAGE_PROGRAM);
spi_(addr23_16);
spi_(addr15_0&0xf0);
spi_(addr15_0&0x0f);
for(j=0;j<=i;++j)
{
spi_(DataArray[j]);
}
spi_stop();
W25Q_WaitBusy();//只有写和擦除造成忙 所以只在写和擦除后加busy就够了
}
void W25Q_Read(uchar addr23_16,uint addr15_0,uchar *DataArray,uint i)
{
uchar j;
spi_start();
spi_(W25Q64_READ_DATA);
spi_(addr23_16);
spi_(addr15_0&0xf0);
spi_(addr15_0&0x0f);
for(j=0;j<=i;j++)
{
DataArray[j]=spi_(0xff);
}
spi_stop();
}
void W25Q_SectorErase(uchar addr23_16,uint addr15_0)
{
W25Q_WriteEnable();
spi_start();
spi_(W25Q64_SECTOR_ERASE_4KB);
spi_(addr23_16);
spi_(addr15_0&0xf0);
spi_(addr15_0&0x0f);
spi_stop();
W25Q_WaitBusy();
}
lcd1602i2c.c
#include <REGX52.H>
sbit sda=P1^7;
sbit scl=P1^6;
#define uchar unsigned char
#define ADDR 0x4e // 0x27<<! | 0x00(i2c write)
void delay()
{ }
void i2c_init() //scl sda都拉高初始化总线
{
scl=1;
delay();
sda=1;
delay();
}
void i2c_start()
{
sda=1;
delay();
scl=1;
delay();
sda=0;
delay();
}
void i2c_stop()
{
sda=0;
delay();
scl=1;
delay();
sda=1;
delay();
}
void i2c_re() //等待接收应答的程序
{
unsigned char i=0;
scl=1;
delay();
while((sda==1)&&(i<250))
i++;
scl=0;
delay();
}
void i2c_writebyte(uchar d )
{
uchar i,temp;
temp=d;
for(i=0;i<8;i++)
{
temp=temp<<1;
scl=0;
delay();
sda=CY;
delay();
scl=1;
delay();
}
scl=0;//释放总线?
delay();
sda=1;
delay();
}
uchar i2c_readbyte()
{
uchar i,temp;
scl=0;
delay();
sda=1;//释放数据总线
delay();
for(i=0;i<8;i++)
{
scl=1;
delay();
temp=(temp<<1)|sda;
scl=0;
delay();
}
return temp;
}
void write_com(uchar aa)
{
uchar H=0x00,L=0x00;
H=aa&0xf0;
L=(aa&0x0f)<<4;
i2c_writebyte(0x08+H);//1(BACKLIGHT),0(EN),0(RW),0(RS)
i2c_re();
i2c_writebyte(0x0c+H);//1(BACKLIGHT),1(EN),0(RW),0(RS)
i2c_re();
i2c_writebyte(0x08+H);//1(BACKLIGHT),0(EN),0(RW),0(RS)
i2c_re();
delay();
i2c_writebyte(0x08+L);
i2c_re();
i2c_writebyte(0x0c+L);
i2c_re();
i2c_writebyte(0x08+L);
i2c_re();
delay();
}
void write_data(uchar aa)
{
uchar H=0x00,L=0x00;
H=aa&0xf0;
L=(aa&0x0f)<<4;
i2c_writebyte(0x09+H);//1(BACKLIGHT),0(EN),0(RW),1(RS)
i2c_re();
i2c_writebyte(0x0d+H);//1(BACKLIGHT),1(EN),0(RW),1(RS)
i2c_re();
i2c_writebyte(0x09+H);//1(BACKLIGHT),0(EN),0(RW),1(RS)
i2c_re();
delay();
i2c_writebyte(0x09+L);
i2c_re();
i2c_writebyte(0x0d+L);
i2c_re();
i2c_writebyte(0x09+L);
i2c_re();
delay();
}
void lcd_init()
{
i2c_init();
i2c_start();
i2c_writebyte(ADDR);
i2c_re();
write_com(0x02);//设置四线发送数据
write_com(0x08);//因为writcom只发四位 0X28需要分两次发才行
write_com(0x0c);
write_com(0x06);
write_com(0x01);
}
void lcdgoto(unsigned char x,unsigned char y)//x行 y列
{
if(x==1)
write_com(0x80+y-1);
else
write_com(0x80+0x40+y-1);
}
void lcdstring(uchar *str)
{
uchar i=0;
while(str[i]) write_data(str[i++]);
}
void lcdstr(uchar x,uchar y,uchar *str)
{
lcdgoto(x,y);
lcdstring(str);
}
void lcdchar(uchar x,uchar y,uchar c)
{
lcdgoto(x,y);
write_data(c);
}
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
void lcdnum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
lcdgoto(Line,Column);
for(i=Length;i>0;i--)
{
write_data(Number/LCD_Pow(10,i-1)%10+'0');
}
}
void lcdhex(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
lcdgoto(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
write_data(SingleNumber+'0');
}
else
{
write_data(SingleNumber-10+'A');
}
}
}
void lcdsignednum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
lcdgoto(Line,Column);
if(Number>=0)
{
write_data('+');
Number1=Number;
}
else
{
write_data('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
write_data(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
无关紧要碎碎念
第一天因为用的89c52 应该是0口的Vcc断了导致P0口输出不稳影响通讯搞得我困惑了一晚上(我就不明白我自己的写法有什么问题得不出正确ID 事实上也没问题),那个单片机也弹翻新提示框了,应该是到寿了也不稳定。不合理设计的最小系统板上的DIP40插座真是断脚神器(前面没空间翘单片机只能一边翘),本来没事一插一把断脚了我还傻傻的没当回事万用表一量有输出继续用了,换了个新的一插一拔好几个脚差点断了,最后虚虚的搭着用了不敢插进去了。
看着32的教程用51驱动成还是挺开心的,本废物也能做点简单的事。