这周一直在调STM32F302RBT6( SPI 1 )+5位8段数码管(两片74HC595控制)+5个按键(一片74HC165控制)。
一 程序目的:
在无按键按下时,5个数码管全显(5个8);
当K1按下时,显示5个1;
当K2按下时,显示5个2;
当K3按下时,显示5个3;
当K4按下时,显示5个4;
当K5按下时,显示5个5;
二 其中的重点如下:
1 SPI的配置
2 595的控制时序
3 165的控制时序
三 程序
1 包含的程序如下,其中的dma.c 可以去掉. mykey.c是空的
2 具体内容:
main.c
#include "rs485.h"
#include "myspi.h"
u16 ADC1_ConvertedValue[5];
u16 ADC2_ConvertedValue[20];
void delay(u32 n);
int main(void)
{ //int i;
//GPIO_InitTypeDef GPIO_InitStructure1;
u16 ReceiveData;
RCC_ClocksTypeDef ClockInfo;
RCC_GetClocksFreq(&ClockInfo); //
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //
RS485_Init(9600); //
RCC_GetClocksFreq(&ClockInfo);
myspi_init(); //其他都可以不看,这个是重点。具体内容在下面
MCU_RX_EN; //
//我的SPI采用的是全双工模式,其中NSS采用软件控制方式:即将MCU的内部NSS信号置1,使MCU工作在Master模式;
//因为我要同时控制595和165,如果用硬件控制方式没办法同时满足595和165的控制时序。(也许可以满足,只是我不会配置)
//而芯片的NSS引脚配置为普通的GPIO,这样NSS就可以在我需要的时候为高为低
while(1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4); //
delay(1); //执行完这一步,NSS=0 即165 的SH/LD引脚=0,165载入8个并行口的数据到内部的8个触发器
GPIO_SetBits(GPIOA, GPIO_Pin_4); // SH/LD 置1,8个并行输入被锁住
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET){}
SPI_I2S_SendData16(SPI1,0x0000); // MCU发送0000,数码管显示全显;
// 同时产生SPICLKA脉冲给到165的2脚SCK
// 同时MCU读取595的16输入数据;
// 即MCU作为Master,在全双工模式下,写外设的同时也在读外设
//delay(1); //这句也可以加上
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){}
ReceiveData = SPI_I2S_ReceiveData16(SPI1); //将SPI接收到的数据存在变量ReceiveData中
if (ReceiveData == 0xFBFF)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4); //NSS=0,选中595
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET){}
SPI_I2S_SendData16(SPI1,0x00F9); //K1按下,显示5个1
delay(1);
}
else if(ReceiveData == 0xFEFF)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET){}
SPI_I2S_SendData16(SPI1,0x00A4); //K2按下,显示5个2
delay(1);
}
else if (ReceiveData == 0xFDFF)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET){}
SPI_I2S_SendData16(SPI1,0x00B0); //K3按下,显示5个3
delay(1);
}
else if(ReceiveData == 0xEFFF)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET){}
SPI_I2S_SendData16(SPI1,0x0099); //K4按下,显示5个4
delay(1);
}
else if(ReceiveData == 0xF7FF)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET){}
SPI_I2S_SendData16(SPI1,0x0092); //K5按下,显示5个5
delay(1);
}
}
}
//*********************************************************************************************************************************************//
myspi.c :
#include "myspi.h"
#include "stm32f30x_spi.h"
#include "delay.h"
void delay(u32 n); //这是一个外部函数
void myspi_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure1;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE); //开 SPI1 的时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE); //开GPIOA时钟,因为用到PA4,5,6,7(SPI1)
//GPIO_PinAFConfig(GPIOA,GPIO_PinSource4|GPIO_PinSource5|GPIO_PinSource6|GPIO_PinSource7,GPIO_AF_5);
//配置复用功能时,不要像上面这一行这样配置。这样会配置不成功的,但是我还不知道为什么。
//GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_5); //PA4即NSS脚,因为我把NSS脚当做GPIO来用,故这句不要
GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_5);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_5);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_5);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure1.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure1.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure1.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure1.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure1.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_Init(GPIOA,&GPIO_InitStructure1);
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //SPI 波特率分频系数
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 时钟相位=0,
// 即 在SCK的第一个边沿采样数据位,第二个边沿锁存数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟极性=0,即在没有数据传输时,时钟的空闲状态电平为低
// 若CPOL=0,则在没有数据传输时,时钟的空闲状态电平为高
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC校验,参考手册上说 这个数大于2就行。没详细研究,随便写了个7
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //SPI通信传输的数据位数,也可以选择8位
//因为我这里控制两片级联的595,所以配置为16位
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI采用全双工模式
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //先传输数据的高位
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//MCU工作在Master模式
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS采用软件控制方式。这句会使CR1中的bit9 SSM为置1
SPI_I2S_DeInit(SPI1); //复位SPI1
SPI1->CR1 |= 0x0100; //SSI=1 。因为对于SPI主机来说,需要SSM 和SSI同时为1
//NSS有内部和外部引脚。这时候外部引脚留作驱动595和165使用, 内部NSS引脚电平通过SSI位来驱动
SPI_Init(SPI1,&SPI_InitStructure); //初始化SPI
SPI_RxFIFOThresholdConfig(SPI1,SPI_RxFIFOThreshold_QF); //设置接收FIFO门槛
SPI_Cmd(SPI1,ENABLE); //使能SPI
}
//*******************************************************************************************************************************************//
delay.c :
#include "delay.h"
u32 m;
extern void delay(u32 n) //ÑÓʱn ms
{
m = 70000*n;
while(m)
{
m--;
}
}
delay.h :
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f30x.h"
extern void delay(u32 n);
#endif
四 遗留问题:
1 STM32的SPI 是否有最大时钟限制?18M?
2 为什么像下面这样配置复用功能不成功?
GPIO_PinAFConfig(GPIOA,GPIO_PinSource4|GPIO_PinSource5|GPIO_PinSource6|GPIO_PinSource7,GPIO_AF_5);
3 时钟相位还有些不明白
4请再清楚地解释下595 165的功能表
5 为什么按键按下后,接收到的数据低8位是FF?
6 “因为对于SPI主机来说,需要SSM 和SSI同时为1” 这句话怎么理解?
7 还需再理解一下FIFO?
8 画出595 和165的控制时序图