STC15系列单片机SPI使用教程(一)

发布者:Wanderlust123最新更新时间:2022-08-02 来源: csdn关键字:STC15系列  单片机  SPI 手机看文章 扫描二维码
随时随地手机看文章

一、硬件接线

1、普通SPI设备接线

如NRF24L01,可以直接连接IO

2、FLASH设备接线

如GD25Q80BSIG,需要加上拉电阻

二、程序编写

1、和SPI相关的寄存器


① SPCTL寄存器

② SPSTAT寄存器

③ SPDAT寄存器

④ AUXR1/P_SW1寄存器

2、寄存器,数据类型重定义

sfr P_SW1  = 0xA2; //外设功能切换寄存器1

sfr SPSTAT = 0xCD; //SPI状态寄存器

sfr SPCTL  = 0xCE; //SPI控制寄存器

sfr SPDAT  = 0xCF; //SPI数据寄存器


#ifndef uchar

#define uchar unsigned char

#endif

#ifndef uint

#define uint  unsigned int

#endif


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


sbit SS_1 = P1^2; //SPI_1的CS脚

sbit SS_2 = P2^4; //SPI_2的CS脚


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;                                //切换到第二组SPI

// 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;        //主机模式

}


void InitSPI_2(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;                                //切换到第二组SPI

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数据交换代码

uchar SPISwap(uchar dat) 

{

    SPDAT = dat;                //触发SPI发送数据

    while (!(SPSTAT & SPIF));   //等待发送完成

    SPSTAT = SPIF | WCOL;       //清除SPI状态位

    return SPDAT;               //返回SPI数据

}


6、NRF24L01读写例程

//NRF24L01相关宏定义

#define NOP 0xFF //空操作

#define READ_REG    0x00 

#define WRITE_REG   0x20

#define TX_ADDR     0x10


sbit CE  = P2^5;

sbit IRQ = P3^2; //INT0


//SPI写寄存器

//reg:指定寄存器地址

//value:写入的值

uchar SPI_RW_Reg(uchar reg, uchar value)

{

uchar status;

SS_2 = 0;              // 使能SPI传输

status = SPISwap(reg); //返回从MISO读出的数据,status应为上次向该寄存器内写的value

SPISwap(value);        //写入寄存器的值

SS_2 = 1; // 禁止SPI传输

return status;       // 返回状态值

}

//读取SPI寄存器值

//reg:要读的寄存器

uchar SPI_Read(uchar reg)

{

uchar reg_val;

SS_2 = 0;      // 使能SPI传输

SPISwap(reg);         // 发送寄存器号

reg_val = SPISwap(NOP); // 读取寄存器内容

SS_2 = 1;             // 禁止SPI传输

return reg_val;     // 返回状态值

}

//在指定位置写指定长度的数据

//reg:寄存器(位置)

//*pBuf:数据指针

//bytes:数据长度

//返回值,此次读到的状态寄存器值

uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar bytes)

{

uchar status, byte_ctr;

SS_2 = 0; // 使能SPI传输

status = SPISwap(reg);// 发送寄存器值(位置),并读取状态值

for(byte_ctr = 0; byte_ctr < bytes; byte_ctr++){ // 写入数据  

SPISwap(*pBuf++);

}

SS_2 = 1;//关闭SPI传输

return status; // 返回读到的状态值

}


//在指定位置读出指定长度的数据

//reg:寄存器(位置)

//*pBuf:数据指针

//bytes:数据长度

//返回值,此次读到的状态寄存器值 

uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes)

{

uchar status, byte_ctr;

SS_2 = 0; // 使能SPI传输

status = SPISwap(reg); // 发送寄存器值(位置),并读取状态值       

for(byte_ctr = 0; byte_ctr < bytes; byte_ctr++){

pBuf[byte_ctr] = SPISwap(NOP); // 读出数据

}

SS_2 = 1; // 关闭SPI传输

return status; // 返回读到的状态值

}


//检测24L01是否存在

//返回值:0,成功;1,失败

uchar NRF24L01_Check(void)

{

uchar buf[5] = {0xA5, 0xA5, 0xA5, 0xA5, 0xA5};

uchar buf1[5];

uchar i;

CE = 0;

SPI_Write_Buf(WRITE_REG + TX_ADDR, buf, 5);

SPI_Read_Buf(TX_ADDR, buf1, 5); //读出写入的地址

CE = 1;

for(i = 0; i < 5; i++)

if(buf1[i] != 0xA5)

break;    

if(i != 5)

return 1;//检测24L01错误

return 0; //检测到24L01

}

//主函数

void main(void)

{

Init_Uart();

EA = 1; //开总中断


InitSPI_2();

NRF24L01_Check();    //切换SPI后需要读多几次,等待SPI稳定

NRF24L01_Check();

if(!NRF24L01_Check()){

SendString("NRF24L01 Checked OK!rn");

}

else{

SendString("NRF24L01 Checked Fail!rn");

}

while(1);

}


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;

SPISwap(Write_Enable);

SS_1 = 1;

}

//写禁能

void Write_Disable_Cmd(void)

{

SS_1 = 0;

SPISwap(Write_Disable);

SS_1 = 1;

}

//读状态寄存器前八位

uchar Read_Status_Register_Sta(void)

{

uchar sta;

SS_1 = 0;

SPISwap(Read_Status_Register);

sta = SPISwap(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++){

SPISwap(cmd[i]);

}

for(i = 0; i < len; i++){

*dat++ = SPISwap(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;

cmd[0] = Page_Program;

cmd[1] = ad1;

cmd[2] = ad2;

cmd[3] = ad3;

temp = 256 - ad3; //一次最多写256字节,超过的写进下一页

Write_Enable_Cmd(); //写使能

SS_1 = 0;

for(i = 0; i < 4; i++){

SPISwap(cmd[i]);

}

for(i = 0; i < temp; i++){

SPISwap(*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++){

SPISwap(cmd[i]);

}

for(i = 0; i < temp; i++){

SPISwap(*dat++);

}

SS_1 = 1;

while(Read_Status_Register_Sta() & 0x01);

}

}

//芯片擦除

void Chip_Erase_1_Cmd(void)

{

Write_Enable_Cmd();

SS_1 = 0;

SPISwap(Chip_Erase_2);

SS_1 = 1;

while(Read_Status_Register_Sta() & 0x01);

}

//读ID

void Read_Identification_Sta(uchar *rdid)

{

uchar i;

SS_1 = 0;

SPISwap(Read_Identification);

for(i = 0; i < 3; i++){

*rdid++ = SPISwap(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++ = '';

}

//主函数

void main(void)

[1] [2]
关键字:STC15系列  单片机  SPI 引用地址:STC15系列单片机SPI使用教程(一)

上一篇:STC8单片机IO口简单模拟串行通信
下一篇:STC15F104W 使用 315/433 MHz 超再生模块发送/接收数据

推荐阅读最新更新时间:2024-11-13 14:22

动图演示UART、SPI、 I2C等串行通信的底层原理
UART、SPI、 I2C等串行通信是嵌入式开发中非常常见的通信方式,这些通信的最底层通信原理其实不难,但很多初学者却学不会。 本文分享一些常见通信的底层数据传输原理。 UART串口 UART:Universal Asynchronous Receiver/Transmitter,通用异步收发传输器。 UART串口是最常见的一种串口通信,下面是串口连接上位机电脑,以及RS232应用的动画。 如上图,PC 上通过UART来调试MCU。 上图,RS-232通过电平转换芯片与MCU通讯。 SPI串行通信 SPI:Serial Peripheral Interface,串行外设接口。 SPI是一种常见的串行同步通信
[单片机]
动图演示UART、<font color='red'>SPI</font>、 I2C等串行通信的底层原理
51单片机入门之按键检测
按键是单片机系统中最常用的交互方式,大学课程里介绍按键的时候,分为了两种:独立式按键和矩阵式按键。在单片机初期,各种扩展芯片的匮乏,导致了IO口资源的宝贵。而今各种按键管理芯片层出不穷,可以实现用尽可能少的IO口检测较多的按键,所以本人认为,矩阵式键盘如今存在的意义只是用在教学上了。下面介绍按键检测的方法。 功能描述: 图中有8个按键,8个发光二极管,1个数码管。当按键1按下时,数码管显示数字1,同时第一个发光二极管点亮。依次类推,当第八个按钮按下时,数码管显示8,并且第八个发光二极管点亮。 硬件分析:按键的一端接单片机,另一端接地,按键按下后单片机侧为低电平,所以当单片机检测到相应的端口为低电平时即可判断按键被按下
[单片机]
51<font color='red'>单片机</font>入门之按键检测
51单片机驱动1602液晶的C程序
# include AT89x51.h /***************************************** 电路连接 P1------DB0~DB7 P2.0------RS P2.1------RW P2.2------E *****************************************/ # define LCD_DB P1 sbit LCD_RS=P2^0; sbit LCD_RW=P2^1; sbit LCD_E=P2^2; /******定义函数****************/ # define ucharunsign
[单片机]
兆易创新GD32 MCU加速物联网升级: 尽享创意 无处不在
物联网风头正夯,亚太区更是引领全球IoT技术的核心引擎。2017年7月,兆易创新GigaDevice携手EET Taiwan和日本CQ出版机构, 在台北富邦国际会议中心及日本横滨太平洋会展中心举办智慧互联与嵌入式应用研讨会。聚焦物联网模块、低成本物联网组件以及物联网安全问题,深入探讨物联网的发展前景和必备条件。作为业界领先的微控制器供应商,兆易创新为夏季系列研讨会带来了精彩的行业趋势分享和产品应用介绍,并展出了GD32系列多款Cortex-M系列内核通用MCU所打造的智能创新平台及最新的系统级应用方案Demo,引起与会工程师及合作伙伴的热情参与和广泛好评。 微控制器更满足市场所需 让物联网概念变为实际的是物联网装置的「大脑
[半导体设计/制造]
利用51单片机制作一个秒表的详细过程
前面的话: 和很多朋友一样,在学51单片机的过程中我们肯定会涉及到制作一个秒表,牵涉到把单片机的多个部分组合起来使用,这对于我们初学者来说可能显得有些困难,我同大家一样,百思不得其解,最后头都弄大了才把这个秒表制作出来,为了给以后的朋友们一些思路,一些参考,所以在这里我把自己制作的整个详细过程整理出来供大家参考。我调试出来是没有问题的,各方面都稳定运行,由于我水平有限,中间可能会有不对的地方,欢迎大家指正,我们一起学习,一起进步! 我将分为三个部分来介绍:1.整体思路,2.硬件电路方面,3.软件编程方面。 1.整体思路 利用51单片机制作秒表时,我介绍精确到十分位(即0.1s)的制作,并让其拥有启动,暂停,复位三个功能。
[单片机]
利用51<font color='red'>单片机</font>制作一个秒表的详细过程
74LS164(74HC164)单片机C51驱动
#include reg51.h #define uchar unsigned char #define uint unsigned int sbit AB =P1^0; sbit CLK=P1^1; sbit RET=P1^2; //延时函数 void mDelay(uchar Delay) { // 延时子程序 uchar i; while(Delay--) { for(i = 0; i 123; i++); } } void uDelay(uchar Delay) { // 延时子程序 for(;Delay 0;Delay--); } void
[单片机]
STM32——简述USART与SPI、IIC之间的区别与联系
简述USART与SPI、IIC之间的区别与联系 第一个区别当然是名字: SPI(Serial Peripheral Interface) 串行外设接口 I2C(INTER IC BUS) 集成电路总线 USART(Universal Asynchronous Receiver Transmitter) 通用异步收发器 第二个区别在电气信号线上: SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI)。 SPI总线可以实现多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。主从设备间可以实现全双工通信
[单片机]
于ATmega128 单片机的自动投切开关电源设计
电源技术的发展方向之一是并联运行分布电源系统,以便通过N+1 冗余获得故障容错及冗余功率,并且建立模块式分布电源系统,以增大总负载电流。采用双端驱动集成芯片TL494 输出PWM 脉冲控制主开关的导通来控制电压输出,以ATmega128 单片机为核心,实现大电流时自动由单电源供电投切到双电源并联均流供电,增强了开关电源的带负载能力和提高电源的供电效率。 模块化是开关电源发展的总体趋势,可以采用模块化电源组成分布式电源系统,可以设计成N+1冗余电源系统,并实现并联方式的容量扩展,使整个电源体积重量下降,模块中半导体器件的电流应力小,提高了系统的可靠性。本研究的开关电源在带小负载时为单电源供电,带大负载时(电流超过1.7A)自动投切
[单片机]
于ATmega128 <font color='red'>单片机</font>的自动投切开关电源设计
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved