硬件SPI(中断方式)
以STC15W408AS单片机为例
一、硬件接线
二、程序编写
1、和SPI中断相关的寄存器
① IE寄存器
② IE2寄存器
③ IP2寄存器
2、自定义寄存器,数据类型重定义
sfr P_SW1 = 0xA2; //外设功能切换寄存器1
sfr SPSTAT = 0xCD; //SPI状态寄存器
sfr SPCTL = 0xCE; //SPI控制寄存器
sfr SPDAT = 0xCF; //SPI数据寄存器
sfr IE2 = 0xAF; //中断控制寄存器2
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
typedef bit BOOL;
#define NULL 0
#define FALSE 0
#define TRUE 1
3、寄存器相关位宏定义, CS引脚定义
#define SPI_S0 0x04
#define SPI_S1 0x08
#define SPIF 0x80 //SPSTAT.7
#define WCOL 0x40 //SPSTAT.6
#define SSIG 0x80 //SPCTL.7
#define SPEN 0x40 //SPCTL.6
#define DORD 0x20 //SPCTL.5
#define MSTR 0x10 //SPCTL.4
#define CPOL 0x08 //SPCTL.3
#define CPHA 0x04 //SPCTL.2
#define SPDHH 0x00 //CPU_CLK/4
#define SPDH 0x01 //CPU_CLK/16
#define SPDL 0x02 //CPU_CLK/64
#define SPDLL 0x03 //CPU_CLK/128
#define ESPI 0x02 //IE2.1
sbit SS_1 = P1^2; //SPI_1的SS脚
BOOL g_fSpiBusy; //SPI的工作状态
4、SPI初始化代码
void InitSPI_1(void)
{
uchar temp;
temp = P_SW1; //切换到第一组SPI
temp &= ~(SPI_S0 | SPI_S1); //SPI_S0=0 SPI_S1=0
P_SW1 = temp; //(P1.2/SS, P1.3/MOSI, P1.4/MISO, P1.5/SCLK)
// temp = P_SW1; //可用于测试U7,U7使用的是第二组SPI控制Flash
// temp &= ~(SPI_S0 | SPI_S1); //SPI_S0=1 SPI_S1=0
// temp |= SPI_S0; //(P2.4/SS_2, P2.3/MOSI_2, P2.2/MISO_2, P2.1/SCLK_2)
// P_SW1 = temp;
// temp = P_SW1; //切换到第三组SPI
// temp &= ~(SPI_S0 | SPI_S1); //SPI_S0=0 SPI_S1=1
// temp |= SPI_S1; //(P5.4/SS_3, P4.0/MOSI_3, P4.1/MISO_3, P4.3/SCLK_3)
// P_SW1 = temp;
SPDAT = 0; //初始化SPI数据
SPSTAT = SPIF | WCOL; //清除SPI状态位
SPCTL = SPEN | MSTR | SSIG | SPDLL; //主机模式
}
5、SPI中断服务程序
//SPI中断服务程序
void spi_isr() interrupt 9 using 1
{
SPSTAT = SPIF | WCOL; //清除SPI状态位
g_fSpiBusy = FALSE;
}
6、SPI数据交换代码
/************************************************
使用SPI方式进行数据交换
入口参数:
dat : 准备写入的数据
出口参数:
从Flash中读出的数据
************************************************/
uchar SPIShift(uchar dat)
{
g_fSpiBusy = TRUE;
SPDAT = dat; //触发SPI发送
while(g_fSpiBusy); //等待SPI数据传输完成
return SPDAT;
}
7、GD25Q80BSIG读写例程
//GD25Q80BSIG相关宏定义
#define NOP 0xFF //空操作
#define Write_Enable 0x06 //写使能
#define Write_Disable 0x04 //写禁能
#define Read_Status_Register 0x05 //读前八位状态寄存(S7-S0)
#define Read_Status_Register_1 0x35 //读后八位状态寄存(S15-S8)
#define Read_Data 0x03 //读数据
#define Page_Program 0x02 //页面编程,256字节
#define Chip_Erase_1 0xC7 //芯片擦除命令1
#define Chip_Erase_2 0x60 //芯片擦除命令2
#define Read_Identification 0x9F //读取标识命令允许读取8位制造商标识,然后是两个字节的设备标识。
sbit WP = P1^6; //写保护,低电平有效
//写使能
void Write_Enable_Cmd(void)
{
SS_1 = 0;
SPIShift(Write_Enable);
SS_1 = 1;
}
//写禁能
void Write_Disable_Cmd(void)
{
SS_1 = 0;
SPIShift(Write_Disable);
SS_1 = 1;
}
//读状态寄存器前八位
uchar Read_Status_Register_Sta(void)
{
uchar sta;
SS_1 = 0;
SPIShift(Read_Status_Register);
sta = SPIShift(NOP);
SS_1 = 1;
return sta;
}
//读数据
void Read_Data_Cmd(uchar ad1, uchar ad2, uchar ad3, uchar *dat, uint len)
{
uchar i, cmd[4];
cmd[0] = Read_Data;
cmd[1] = ad1;
cmd[2] = ad2;
cmd[3] = ad3;
SS_1 = 0;
for(i = 0; i < 4; i++){
SPIShift(cmd[i]);
}
for(i = 0; i < len; i++){
*dat++ = SPIShift(NOP);
}
SS_1 = 1;
}
//页编程,输入24位起始地址
void Page_Program_Cmd(uchar ad1, uchar ad2, uchar ad3, uchar *dat, uint len)
{
uchar i, cmd[4];
uint count = 0, temp = 0;
long address = 0; //24位地址
address = (ad1 << 16) | (ad2 << 8) | ad3;
cmd[0] = Page_Program;
cmd[1] = ad1;
cmd[2] = ad2;
cmd[3] = ad3;
temp = 256 - ad3;
Write_Enable_Cmd();
SS_1 = 0;
for(i = 0; i < 4; i++){
SPIShift(cmd[i]);
}
for(i = 0; i < temp; i++){
SPIShift(*dat++);
}
SS_1 = 1;
while(Read_Status_Register_Sta() & 0x01);
if(len > temp){
cmd[0] = Page_Program;
cmd[1] = ad1;
cmd[2] = ad2 + 1;
cmd[3] = 0;
temp = len - temp;
Write_Enable_Cmd();
SS_1 = 0;
for(i = 0; i < 4; i++){
SPIShift(cmd[i]);
}
for(i = 0; i < temp; i++){
SPIShift(*dat++);
}
SS_1 = 1;
while(Read_Status_Register_Sta() & 0x01);
}
}
//擦除芯片
void Chip_Erase_1_Cmd(void)
{
Write_Enable_Cmd();
SS_1 = 0;
SPIShift(Chip_Erase_2);
SS_1 = 1;
while(Read_Status_Register_Sta() & 0x01);
}
//读id
void Read_Identification_Sta(uchar *rdid)
{
uchar i;
SS_1 = 0;
SPIShift(Read_Identification);
for(i = 0; i < 3; i++){
*rdid++ = SPIShift(NOP);
}
SS_1 = 1;
}
//16进制转字符串输出
void HexToAscii(uchar *pHex, uchar *pAscii, uchar nLen)
{
uchar Nibble[2];
uint i,j;
for (i = 0; i < nLen; i++){
Nibble[0] = (pHex[i] & 0xF0) >> 4;
Nibble[1] = pHex[i] & 0x0F;
for (j = 0; j < 2; j++){
if (Nibble[j] < 10){
Nibble[j] += 0x30;
}
else{
if (Nibble[j] < 16)
Nibble[j] = Nibble[j] - 10 + 'A';
}
*pAscii++ = Nibble[j];
} // for (int j = ...)
} // for (int i = ...)
*pAscii++ = '\0';
}
//主函数
void main(void)
{
uchar sta, dis[2], rdid[3];
uchar write[10] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 11}, read[10] = {
0x00};
uchar play[20] = {
0x00};
Init_Uart();
InitSPI_1();
IE2 |= ESPI; //使能SPI传输中断
EA = 1;
g_fSpiBusy = FALSE;
Read_Identification_Sta(rdid); //切换SPI后,需要多读几次,等待SPI稳定
Read_Identification_Sta(rdid);
Read_Identification_Sta(rdid);
HexToAscii(&rdid[0], dis, 1);
SendString("Manufacturer ID: 0x");
SendString(dis);
SendString("\r\n");
HexToAscii(&rdid[1], dis, 1);
SendString("Memory Type: 0x");
SendString(dis);
SendString("\r\n");
HexToAscii(&rdid[2], dis, 1);
SendString("Capacity: 0x");
SendString(dis);
SendString("\r\n");
sta = Read_Status_Register_Sta();
HexToAscii(&sta, dis, 1);
SendString("GD25Q80BSIG Status Register: 0x");
SendString(dis);
SendString("\r\n");
Chip_Erase_1_Cmd(); //写数据之前要先擦除数据
Page_Program_Cmd(0x00, 0x01, 0xFA, write, 10);//写数据
Read_Data_Cmd(0x00, 0x01, 0xFA, read, 10);//读数据
HexToAscii(read, play, 10);
SendString("Read Address 0x0001FA: ");
SendString(play);
SendString("\r\n");
while(1);
}