上一篇:STM32-(17):SPI与数码管(数码管) | 下一篇:STM32-(19):I2C通信(理论基础) |
---|
SPI是由Motorala公司提出的一种同步串行外围接口。它在速度要求不高、低功耗、需保存少量参数的智能化传感系统中得到了广泛应用。
SPI是一个全双工的同步串行接口。在数据传输过程中,总线上只能是一个主机和一个从机进行通信。
1、MISO(Master In Slave Out)
主机输入、从机输出信号。
2、MOSI(Master Out Slave In)
主机输出、从机输入信号。
3、SCK(Serial Clock)
串行时钟信号。(用来同步使用的)
4、SS(Slave Select)
从机选择信号,低电平有效。
SPI的基本结构相当于两个8位移位寄存器的首位相接,构成16位的环形移位寄存器。从而实现了主机与从机的数据交换。
分析:
①首先看SCK 管脚,通过波特率发生器产生时钟信号,这个信号可以出去给从机使用,也可以进来给自己驱动 COMMUNICATION CONTROL(通信控制),其中波特率发生器由BR0、BR1、BR2控制,由这三个值来决定波特率的速度,通信控制受MSTR、SSM、SSI控制
,如果通信控制出错,比如控制的CRCERR(CRC校验错误)、MODF(模式)、OVR(数据溢出),就会在SPI_SR的相应位置1。通信控制出来的信号连接着 MASTER CONTROL LOGIC(主控逻辑电路),中控逻辑电路控制着MOSI、MISO这一块。
②数据从MISO进来,进入SHIFT REGISTER(移位寄存器),移位寄存器数据一旦8位接收完整之后,会自动放到 RX BUFFER 中,我们可以通过 RX BUFFER可以读取数据。 当我们要发送内容,就是通过总线(地址总线,数据总线)将数据写入 TX BUFFER 中去,然后送到移位寄存器 ,数据一旦8位接收完整之后,可以通过MOSI将数据发送出去。
设置MSTR(主设备选择)和SPE位(使能位)来选择是否工作在主模式还是从模式下。(这两个位都在SPI_CR1寄存器中都可以设置),作为从机,片选要接地,可以硬件实现也可以软件实现,接电源是作主机
SPI接口可由CPOL和CPHA设定4种不同传输格式的时序。(CPOL和CPHA在SPI_CR1寄存器中)
CPOL决定时钟脉冲SCK的有效脉冲方式(正脉冲、负脉冲)。CPHA决定数据线MOSI什么时候输出数据或采集数据。
根据CPOL和CPHA的组合数目,一共有4种设置情况。
分析:
CPOL决定了脉冲的方式,第一行(CPOL=0),是正脉冲,第二行(CPOL=1),是负脉冲(空闲时高电平,来数据下降沿);当CPHA=0,数据是先出来的,即比上方的SCK的电平变化(时钟输出)要快,大概快半拍,我们称之为数据传输相位超前;当CPHA=1,SCK的电平变化之后数据才会出来,相位是同步的,当在SCK的第二个上升沿或者下降沿的时候才开始数据采集
4种时序下的数据传输,其中“第一位数据的输出”和“其他位数据的输出”栏是表示数据在什么时候更新输出。还需注意数据采样是上升沿还是下降沿有效。
根据SPI_CR1寄存器中的LSBFIRST位,输出数据位时可以MSB在先也可以LSB在先。
根据SPI_CR1寄存器的DFF位,每个数据帧可以是8位或是16位。所选择的数据帧格式对发送和/或接收都有效。
在主模式时,串行时钟在SCK脚产生。
配置步骤:
1、 当一字节写进发送缓冲器时,发送过程开始。
2、 在发送第一个数据位时,数据字被并行地(通过内部总线–TX BUFFER)传入移位寄存器,而后串行地移出到 MOSI 脚上; MSB 在先还是 LSB 在先,取决于 SPI_CR1寄存器中的 LSBFIRST 位。数据从发送缓冲器传输到移位寄存器 TXE 标志将被置位,如果设置 SPI_CR1寄存器中的 TXEIE 位,将产生中断。
3、 在试图写发送缓冲器之前,需确认 TXE 标志应该是1
当数据传输完成时:
1、 移位寄存器里的数据传送到接收缓冲器(8位–>RX BUFFER),并且 RXNE 标志被置位(硬件接受满自动置位1)。如果 SPI_CR2寄存器中的 RXEIE 位被设置,则产生中断。
2、 读 SPI_ DR 寄存器时, SPI 设备返回接收到的数据字。读 SPI_DR 寄存器将清除RXNE 位(也是硬件自动清零)。
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_lib.h" //包含了所有的头文件 它是唯一一个用户需要包括在自己应用中的文件,起到应用和库之间界面的作用。
#include "../Module_Function/Module.h"
#include
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
ErrorStatus HSEStartUpStatus;
void Delay_Ms(u16 time);
void RCC_Configuration(void);
void GPIO_Configuration(void);
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name : main
* Description : Main program.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
int main(void)
{
// u16 i=0,j;
#ifdef DEBUG
debug();
#endif
RCC_Configuration();//使能外设时钟
SEG_Init();
//========实现数码管显示==========
while (1)
{
/*for(i=0;i<9999;i++)
{
for(j=0;j<500;j++)
SEG_Display(i,0);
} */
SEG_Display(1234,3);
}
}
/*******************************************************************************
* Function Name : Delay_Ms
* Description : delay 1 ms.
* Input : time (ms)
* Output : None
* Return : None
*******************************************************************************/
void Delay_Ms(u16 time) //延时函数
{
u16 i,j;
for(i=0;i
/****************************************************************************
* 版权: 源享教育(www.yxarm.net)
* 文件: Seg_Module.c
* 版本: 1.0
* 说明: MP3播放器当前播放歌曲序号,播放第1首时4位数码管显示0001,依次排列
* 作者: 刘斌
* 时间: 2011.6.21
* 说明: 数码管段显由HC595控制,位显由IO口SEG_A1/SEG_A2/SEG_A3/SEG_A4控制。
* HC595使用SPI通信方式,Cortex M3的SPI使用步骤如下:
* 1、使能APB2外设SPI1时钟:RCC_APB2PeriphClockCmd();
* 2、将外设SPI寄存器重设为缺省值:SPI_I2S_DeInit();
* 3、初始化外设SPI寄存器:SPI_Init();
* 4、使能APB2外设SPI:SPI_Cmd();
* 5、调用SPI数据发送函数:SPI_I2S_SendData();
------------------------------修改记录--------------------------------------
* 修改功能:
* 修改时间:
* 修改作者:
* 遗留问题:
****************************************************************************/
#include "stm32f10x_lib.h" //包含了所有的头文件 它是唯一一个用户需要包括在自己应用中的文件,起到应用和库之间界面的作用。
#define HC595_nCS GPIO_Pin_0 //HC595_nCS = PA0
#define HC595_RCK GPIO_Pin_1 //HC595_RCK = PA1
#define SEG_A1 GPIO_Pin_8 //SEG_A1 = PC8
#define SEG_A2 GPIO_Pin_15 //SEG_A2 = PB15
#define SEG_A3 GPIO_Pin_9 //SEG_A3 = PC9
#define SEG_A4 GPIO_Pin_8 //SEG_A4 = PE8
u8 const NumberTube_TAB[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//数码管0~9
/*******************************************************************************
* Function Name : SEG_Init
* Description : SEG数码管引脚,SPI1引脚初始化
* Input : None
* Return : None
*******************************************************************************/
void SEG_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 声明一个IO口结构体变量
SPI_InitTypeDef SPI1_InitStructure; //声明一个SPI结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); // 使能APB2外设GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); // 使能APB2外设GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); // 使能APB2外设GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE , ENABLE); // 使能APB2外设GPIOE时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 , ENABLE); // 使能APB2外设SPI1时钟
//==========PA口IO结构体初始化============
GPIO_InitStructure.GPIO_Pin = HC595_nCS|HC595_RCK; //选择PA.0,PA.1
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚频率为50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //模式为推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA寄存器
//==========PB口IO结构体初始化============
GPIO_InitStructure.GPIO_Pin = SEG_A2; //选择PB.15
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚频率为50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //模式为推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB寄存器
//==========PC口IO结构体初始化============
GPIO_InitStructure.GPIO_Pin = SEG_A1|SEG_A3; //选择PC.8,PC.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚频率为50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //模式为推挽输出
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC寄存器
//==========PE口IO结构体初始化============
GPIO_InitStructure.GPIO_Pin = SEG_A4; //选择PE.8
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚频率为50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //模式为推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化GPIOE寄存器
//==========SPI1复用功能初始化============
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //选择PA.4,PA.5, PA.6,PA.7
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚频率为50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //模式为复用推挽输出 (SPI1)
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA寄存器
//==========设置SPI1工作模式==============
SPI1_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线双向全双工
SPI1_InitStructure.SPI_Mode = SPI_Mode_Master; //设置为主SPI
SPI1_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI发送接收8位帧结构
SPI1_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL = 1
SPI1_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //CPHA = 0
SPI1_InitStructure.SPI_NSS = SPI_NSS_Hard; //NSS由外部管脚管理
SPI1_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;//分频值为64
SPI1_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输LSB(低位)开始
SPI1_InitStructure.SPI_CRCPolynomial = 7;
SPI_I2S_DeInit(SPI1); //将外设SPI1寄存器重设为缺省值 ;
SPI_Init(SPI1, &SPI1_InitStructure);//初始化外设SPI1寄存器
//==========使能SPI1========================
SPI_Cmd(SPI1, ENABLE);//使能SPI1外设
GPIO_ResetBits(GPIOA, HC595_nCS);
}
/*******************************************************************************
* Function Name : SEG_BitSelect
* Description : 选择数码管的位选
* Input : data 选择码 ,data的取值为0x01,0x02,0x04,0x08
* Return : None
*******************************************************************************/
void SEG_BitSelect(u8 data)
{
if((data & 0x01) != 0) GPIO_ResetBits(GPIOC, SEG_A1); // 控制smgA1 = PC8
else GPIO_SetBits(GPIOC, SEG_A1);
if((data & 0x02) != 0) GPIO_ResetBits(GPIOB, SEG_A2); // 控制smgA2 = PB15
else GPIO_SetBits(GPIOB, SEG_A2);
if((data & 0x04) != 0) GPIO_ResetBits(GPIOC, SEG_A3); // 控制smgA3 = PC9
else GPIO_SetBits(GPIOC, SEG_A3);
if((data & 0x08) != 0) GPIO_ResetBits(GPIOE, SEG_A4); // 控制smgA4 = PE8
else GPIO_SetBits(GPIOE, SEG_A4);
}
/*******************************************************************************
* Function Name : SEG_Display
* Description : 数码管显示4位数据
* Input : data 需要显示的数据
* radix_point 小数点的位置,取值范围为4、3、2
* Return : None
*******************************************************************************/
void SEG_Display(u16 data,u8 radix_point)
{
u16 j,one,ten,hundred,thousand; //个,十,百,千,的变量声明
thousand = data / 1000; //计算千位
if(thousand != 0) data -= thousand*1000;
hundred = data / 100; //计算百位
if(hundred != 0) data -= hundred*100;
ten = data / 10; //计算十位
if(ten != 0) data -= ten*10;
one = data % 10; //计算个位
//-------------------显示千位数据----------------
GPIO_ResetBits(GPIOA, HC595_RCK);
if(radix_point==4) SPI_I2S_SendData(SPI1, NumberTube_TAB[thousand] & 0x7f);
else SPI_I2S_SendData(SPI1, NumberTube_TAB[thousand] );
GPIO_SetBits(GPIOA, HC595_RCK);
SEG_BitSelect(0x08); //打开数码管位选端
for(j=0;j<500;j++); //小段延时
SEG_BitSelect(0x00); //关闭显示
//-------------------显示百位数据----------------
GPIO_ResetBits(GPIOA, HC595_RCK);
if(radix_point==3) SPI_I2S_SendData(SPI1, NumberTube_TAB[hundred] & 0x7f);
else SPI_I2S_SendData(SPI1, NumberTube_TAB[hundred] );
GPIO_SetBits(GPIOA, HC595_RCK);
SEG_BitSelect(0x01);
for(j=0;j<500;j++);
SEG_BitSelect(0x00); //关闭显示
//-------------------显示十位数据----------------
GPIO_ResetBits(GPIOA, HC595_RCK);
if(radix_point==2) SPI_I2S_SendData(SPI1, NumberTube_TAB[ten] & 0x7f);
else SPI_I2S_SendData(SPI1, NumberTube_TAB[ten] );
GPIO_SetBits(GPIOA, HC595_RCK);
SEG_BitSelect(0x02);
for(j=0;j<500;j++);
SEG_BitSelect(0x00); //关闭显示
//-------------------显示个位数据----------------
GPIO_ResetBits(GPIOA, HC595_RCK);
SPI_I2S_SendData(SPI1, NumberTube_TAB[one] );
GPIO_SetBits(GPIOA, HC595_RCK);
SEG_BitSelect(0x04);
for(j=0;j<500;j++);
SEG_BitSelect(0x00); //关闭显示
}
/*******************************************************************************
* Function Name : SEG_POWEROFF
* Description : SEG数码管关闭
* Input : None
* Return : None
*******************************************************************************/
void SEG_POWEROFF(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 , DISABLE); // 关闭APB2外设SPI1时钟
GPIO_SetBits(GPIOC, SEG_A1);
GPIO_SetBits(GPIOB, SEG_A2);
GPIO_SetBits(GPIOC, SEG_A3);
GPIO_SetBits(GPIOE, SEG_A4);
}
/****************************************************************************
* 版权: 源享教育(www.yxarm.net)
* 文件: Led_Module.c
* 版本: 1.0
* 说明: MP3播放器指示灯,显示音乐播放频率以及音调
* 作者: 刘斌
* 时间: 2011.6.21
* 说明: LED流水灯由LS164芯片控制,LS164输出引脚1~8分别对应LED1~LED8。
* LS164使用IO口模拟通信方式,Cortex M3的IO口使用步骤如下:
* 1、使能APB2外设GPIOx时钟:RCC_APB2PeriphClockCmd();
* 2、初始化GPIOx寄存器:GPIO_Init();
* 3、GPIOx清零:GPIO_ResetBits();
* 4、GPIOx置位:GPIO_SetBits();
------------------------------修改记录--------------------------------------
* 修改功能:
* 修改时间:
* 修改作者:
* 遗留问题:
****************************************************************************/
#include "stm32f10x_lib.h" //包含了所有的头文件 它是唯一一个用户需要包括在自己应用中的文件,起到应用和库之间界面的作用。
#define LS164_DATA GPIO_Pin_10 //LS164_DATA = PE10
#define LS164_CLK GPIO_Pin_5 //LS164_CLK = PB5
#define LS164_CLR GPIO_Pin_11 //LS164_CLR = PE11
/*******************************************************************************
* Function Name : LED_Init
* Description : LED引脚初始化
* Input : None
* Return : None
*******************************************************************************/
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 声明一个IO口结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE , ENABLE); // 使能APB2外设GPIOE时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); // 使能APB2外设GPIOB时钟
//==========PE口IO结构体初始化============
GPIO_InitStructure.GPIO_Pin = LS164_DATA|LS164_CLR; //选择PE.10,PE.11
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚频率为50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //模式为推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化GPIOE寄存器
//==========PB口IO结构体初始化============
GPIO_InitStructure.GPIO_Pin = LS164_CLK; //选择PB.5
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚频率为50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //模式为推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB寄存器
}
/*******************************************************************************
* Function Name : LED_Display
* Description : 向74LS164发送一个8位数据。
* Input : data 需要发送的数据
* Return : None
*******************************************************************************/
void LED_Display(u8 data)
{
u8 j; //定义一个8位无符号整型变量j
GPIO_ResetBits(GPIOE, LS164_CLR); //74LS164输出清零
GPIO_SetBits(GPIOE, LS164_CLR);
//----------------模拟时钟信号,循环8次完成数据传送---------------
for(j=0; j<8; j++)
{
GPIO_ResetBits(GPIOB, LS164_CLK); //向74LS164发送一个低电平时钟信号
if ( (data & 0x01) != 0 ) //低位先传送。判断最低位是否为1
GPIO_SetBits(GPIOE, LS164_DATA);
else
GPIO_ResetBits(GPIOE, LS164_DATA);
data >>=1; //data右移一位
GPIO_SetBits(GPIOB, LS164_CLK); //向74LS164发送一个高电平时钟信号
}
GPIO_ResetBits(GPIOB, LS164_CLK);
}
上一篇:STM32-(17):SPI与数码管(数码管) | 下一篇:STM32-(19):I2C通信(理论基础) |
---|