4. 串行设备接口SPI

4.  串行设备接口SPI

  4.1 SPI结构及寄存器说明

  4.2 SPI设计实例

    4.2.1 SPI基本功能程序设计

    4.2.2 SPI环回程序设计

    4.2.3 用SPI控制LCD

4.1 SPI结构及寄存器说明

  • 串行设备接口(Serial peripheral interface:SPI)是工业标准串行协议,通常用于嵌入式系统,将微处理器连接到各种片外传感器、转换器、存储器和控制设备
  • SPI可以实现主设备或从设备协议,当配置为主设备时,SPI可以连接多达16个独立的从设备,发送数据和接收数据寄存器的宽度可配置为8位或16位
  • SPI使用2根数据线、1根控制线和1根时钟线实现串行通信

        主入从出(MISO) 主出从入(MOSI)

        串行时钟(SCK) 从设备选择(NSS)

  • 时钟极性和时钟相位组合

  • 时钟极性为0时初始电平为低,为1时初始电平为高时钟相位为0时第1个边沿采样,为1时第2个边沿采样
  • SPI由收发数据和收发控制两部分组成
  • 收发数据部分包括发送缓冲区、接收缓存区和移位寄存器
  • 收发控制部分包括控制状态寄存器、通信电路、主控制电路和波特率发生器

  • NSS是一个可选的引脚功能是用作“片选引脚”用来选择从设备,通常配成通用I/O引脚
  • 当SPI连接多个从设备时MOSI、MISO和SCK连接所有的从设备,但每个从设备的NSS引脚必须连接到主设备的一个通用I/O引脚
  • SPI使用的GPIO引脚

            注:(1)括号中的引脚为复用功能重映射引脚

  • SPI通过7个寄存器进行操作

  • SPI控制寄存器1(CR1)

  • SPI控制寄存器2(CR2)

  • SPI状态寄存器(SR)

4.2 SPI设计实例

    4.2.1 SPI基本功能程序设计

  • SPI初始化子程序
//	SPI1初始化子程序(主模式)
void Spi1_Init(void)
{
    RCC->APB2ENR	|=	1<<2;					//开启GPIOA时钟
    RCC->APB2ENR	|=	1<<12;					//开启SPI1时钟
    GPIOA->CRL	&=	0x0000ffff;
    GPIOA->CRL	&=	0xb4bb0000;	    //PA.07(MOSI)复用推挽输出、PA.06(MISO)浮空输入
                                    //PA.05(SCK)和PA.04(NSS)复用推挽输出
    SPI1->CR1	|=	1<<2;						//主模式
    SPI1->CR1	|=	1<<6;						//SPI允许
}
  • SPI发送/接收子程序
//SPI1发送子程序
//入口参数:data-发送数据
//出口参数:返回发送数据
short Spi1_Txd(short	data)
{
    SPI1->DR	=data;							//发送数据
    while(!(SPI1->SR&2));						//等待TxE=1(发送数据寄存器空)
    return	data;								//返回数据
}

//SPI1接收子程序
//出口参数:接收数据(接收成功)/0(接收不成功)
short Spi1_Rxd(void)
{
    if(SPI1->SR&1)								//RXNE=1(接收数据寄存器不空)
        return SPI1->DR;						//返回接收数据
    else
        return 0;								//返回0
}

    4.2.2 SPI环回程序设计

int mian(void)
{
    SysTick_Init();								//系统定时器初始化
    Key_Init();									//按键接口初始化
    Led_Init();									//LED接口初始化
    Lcd_Init();									//LCD初始化
    USART1_Init();								//USART1初始化
    Spi_Init();									//SPI1初始化
    while(1)
    {
        SysTick_Proc();							//定时处理
        Key_Proc();								//按键处理
        Led_Proc();								//LED处理
        Lcd_Proc(min,sec,(sec&1)<<3);			//LCD显示分秒
        if(sec	!=	sec2)						//1s时间到
        {
            sec2	=	sec;
            printf("%02x:",min);				//分值直接通过USART发送
            Spi1_Txd(sec);						//秒值通过SPI发送
        }
        Rxd_Time();								//设置分秒值
        if(SPI1->SR&1)							//RXNE=1(接收数据寄存器不空)
            printf("%02x \r\n",SPI->DR);
    }
}

    4.2.3 用SPI控制LCD

  • 对比LCM046写操作时序和SPI时钟极性和时钟相位组合图可以看出:LCM046写操作时序和SPI时钟极性=1、时钟相位=1的信号波形相同,因此可以用SPI控制LCM046

  • 实验证明:SPI时钟极性=0、时钟相位=0时也可以控制LCM046正常工作,这是liyongSPI控制外设的典型方法
  • SPI初始化子程序
//SPI1初始化子程序(主模式)
void Spi1_Init(void)
{
    RCC->APB2ENR	|=	1<<2;							//开启GPIOA时钟
    RCC->APB2ENR	|=	1<<12;							//开启SPI1时钟
    GPIOA->CRL	&=	0x0000ffff;
    GPIOA->CRL	|=	0xb4b70000;		//PA.07(MOSI)和PA.05(SCK)复用推挽输出
                                    //PA.04(NSS)通用开漏输出
    GPIOA->ODR	|=	1<<4;								//PA.04(NSS)输出高电平
    SPI1->CR1	|=	0x0b6f;								//16位、SPI允许、PCLK/64
                                                        //主模式、CPOL=1、CPHA=1
}
  • SPI发送子程序
//SSPI1发送子程序
//入口参数:data-发送数据
//出口参数:返回发送数据
short Spi1_Txd(short	data)
{
    GPIOA->BRR	=	1<<4;							//PA.04(NSS)=0
    SPI1->DR	=	data;							//发送数据
    while(!(SPI1->SR&2));							//等待
    while(SPI1->SR&1<<7);							//等待BSY=0(忙)
    GPIOA->BSRR	=	1<<4;							//PA.04(NSS)=1
    return	data;									//返回发送数据
}
  • LCM046初始化子程序
//LCM046初始化子程序
void Lcd_Init(void)
{
    Spi1_Init();								//初始化SPI1

    Lcd_Code_Write(0x29);						//初始化模块
    Lcd_Code_Write(0x18);						//内部振荡器
    Lcd_code_Write(0x01);                       //开振荡器
    Lcd_Code_Write(0x03);                		//开显示器
}
  • LCM046写命令和写数据子程序
//LCM046写命令子程序
//入口参数:code-8位命令代码
void Lcd_Code_Write(char code)
{
    short data	=	(4<<9)+(code<<1);
    Spi1_Txd(data<<4);										//数据左对齐
}
//LCM046写数据子程序
//入口参数:addr-6位地址,data-4位数据
void Lcd_Data_Write(char addr,char data)
{
    short data1	=	(5<<10)+(addr<<4)+data;
    Spi1_Txd(data1<<3);									    //数据左对齐
}

猜你喜欢

转载自blog.csdn.net/SherlockHolmess/article/details/87857122