之前一直對SPI通信一知半解邮旷,所以想抽空把它搞得明白一些。考慮到之前是結(jié)合Flash芯片來學的傲霸,十分不直觀,而且主要把時間和精力都花在Flash芯片的datasheet和驅(qū)動上了眉反,SPI通信也沒學好昙啄。所以這次就考慮用4位數(shù)碼管顯示模塊,模塊是直接買的現(xiàn)成的寸五,如下圖所示梳凛,這樣可以簡化操作,把精力聚焦到學習的核心–SPI通信本身上來梳杏。
該模塊是用2片74HC595串聯(lián)驅(qū)動的韧拒,一片用來控制數(shù)碼管的位選(U1)淹接,一片用來控制數(shù)碼管的段選(U2)。接口比較簡單叛溢,總共5個引腳蹈集,2個引腳分別接VCC和GND,DIO用來接收串行數(shù)據(jù)的輸入雇初,SCLK用來接收同步時鐘拢肆,每個SCLK上升沿74HC595內(nèi)部的移位寄存器會移一位,RCLK用來控制數(shù)據(jù)的輸出靖诗,每個RCLK上升沿74HC595內(nèi)部的移位寄存器的數(shù)據(jù)會被放進存儲寄存器并輸出到外部引腳QA~QH上郭怪。而QH’是串行輸出引腳,該引腳會接收最高位的溢出刊橘,從而實現(xiàn)多片74HC595的級聯(lián)鄙才。
當兩片74HC595串聯(lián)時,先發(fā)八位數(shù)據(jù)用于段選促绵,再發(fā)八位數(shù)據(jù)用于位選攒庵,然后RCLK上升沿,就可以驅(qū)動某位數(shù)碼管顯示某個字符败晴,通過動態(tài)掃描數(shù)碼管浓冒,由于人眼的視覺暫停效果,就可以實現(xiàn)4位數(shù)碼管的同時顯示尖坤。先用通用I/O來實現(xiàn)該數(shù)碼管的驅(qū)動稳懒,程序如下:
頭文件74HC595.h
#ifndef __74HC595_H__
#define __74HC595_H__
#include"stm32f10x_lib.h" //包含所有的頭文件
#include
// 4-Bit LED Digital Tube Module
#define HC595_SCLK_PIN GPIO_Pin_5 // SPI1_SCK PA5
#define HC595_RCLK_PIN GPIO_Pin_12 // SPI1_NSS PA4
#define HC595_DIO_PIN GPIO_Pin_7 // SPI1_MOSI PA7
#define HC595_GPIO GPIOA
#define HC595_RCLK_GPIO GPIOB
#define HC595_RCC RCC_APB2Periph_GPIOA
#define HC595_RCLK_RCC RCC_APB2Periph_GPIOB
void HC595_Init(void);
void HC595_SendByte(u8 data);
u8 HC595_Display(u16 num, u8 dp);
#endif
源文件74HC595.c
// 用于HC595實現(xiàn)的4Bit-LED Digit Tube Module
// 注意:該4位數(shù)碼管是共陽的!
#include "74HC595.h"
// 碼表
const u8 digitTable[] =
{
// 0 1 2 3 4 5 6 7 8 9
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90,
// A b C d E F -
0x8C, 0xBF, 0xC6, 0xA1, 0x86, 0xFF, 0xbf
};
/*******************************************************************************
* Function Name : HC595_Init
* Description : 初始化HC595
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void HC595_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //聲明一個結(jié)構(gòu)體變量
RCC_APB2PeriphClockCmd(HC595_RCC | HC595_RCLK_RCC, ENABLE);
//使能HC595的時鐘
//74HC595, SCLK RCLK DIO 推挽輸出
GPIO_InitStructure.GPIO_Pin = HC595_SCLK_PIN| HC595_DIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管腳頻率為50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //輸出模式為推挽輸出
GPIO_Init(HC595_GPIO, &GPIO_InitStructure); //初始化寄存器
GPIO_InitStructure.GPIO_Pin = HC595_RCLK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管腳頻率為50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //輸出模式為推挽輸出
GPIO_Init(HC595_RCLK_GPIO, &GPIO_InitStructure); //初始化寄存器
}
/*******************************************************************************
* Function Name : HC595_SendByte
* Description : 發(fā)送一個字節(jié)
* Input : data
* Output : None
* Return : None
*******************************************************************************/
void HC595_SendByte(u8 data)
{
u8 i;
for (i=8; i>=1; i--)
{
// 高位在前
if (data&0x80)
GPIO_SetBits(HC595_GPIO, HC595_DIO_PIN);
else
GPIO_ResetBits(HC595_GPIO, HC595_DIO_PIN);
data <<= 1;
// SCLK上升沿
GPIO_ResetBits(HC595_GPIO, HC595_SCLK_PIN);
GPIO_SetBits(HC595_GPIO, HC595_SCLK_PIN);
}
}
/*******************************************************************************
* Function Name : HC595_Display
* Description : 顯示4位數(shù)字(包括小數(shù)點)
* Input : num: 0000 - 9999
* dp: 小數(shù)點的位置1-4
* Output : None
* Return : 正常返回0,錯誤返回1
*******************************************************************************/
u8 HC595_Display(u16 num, u8 dp)
{
u8 qian = 0, bai = 0, shi = 0, ge = 0;
// 對顯示的參數(shù)范圍進行檢查
if (num > 9999 || dp > 4)
//報錯
return 1;
// 對num進行分解
qian = num / 1000;
bai = num % 1000 / 100;
shi = num % 100 / 10;
ge = num % 10;
// 千位
if(dp == 1)
HC595_SendByte(digitTable[qian] & 0x7F);
else
HC595_SendByte(digitTable[qian]);
HC595_SendByte(0x08);
GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
// 百位
if(dp == 2)
HC595_SendByte(digitTable[bai] & 0x7F);
else
HC595_SendByte(digitTable[bai]);
HC595_SendByte(0x04);
GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
// 十位
if(dp == 3)
HC595_SendByte(digitTable[shi] & 0x7F);
else
HC595_SendByte(digitTable[shi]);
HC595_SendByte(0x02);
GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
// 個位
if(dp == 4)
HC595_SendByte(digitTable[ge] & 0x7F);
else
HC595_SendByte(digitTable[ge]);
HC595_SendByte(0x01);
GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);
return 0;
}
接下來就可以把重心都放在STM32的SPI外設(shè)上了慢味,首先需要讀一下STM32F10x的參考手冊的SPI(串行外設(shè)接口)部分,這樣對SPI就可以有較好的理解纯路,比較重要的是要看懂SPI的結(jié)構(gòu)框圖和主從機通信的示意圖驰唬,如下:
這個理解以后蜕琴,我們就可以參考《STM32F103XX固件庫用戶手冊》的SPI部分來實現(xiàn)STM32的SPI外設(shè)配置和收發(fā)數(shù)據(jù)了,具體代碼如下:
頭文件74HC595_SPI.h
#ifndef __74HC595_SPI_H__
#define __74HC595_SPI_H__
#include"stm32f10x_lib.h" //包含所有的頭文件
#include
// 4-Bit LED Digital Tube Module
// 引腳 // SPI1 4位數(shù)碼管
#define HC595_NSS_PIN GPIO_Pin_4 // SPI1_NSS 未用
#define HC595_SCK_PIN GPIO_Pin_5 // SPI1_SCK SCLK
#define HC595_MISO_PIN GPIO_Pin_6 // SPI1_MISO 未用
#define HC595_MOSI_PIN GPIO_Pin_7 // SPI1_MOSI DIO
#define HC595_RCLK_PIN GPIO_Pin_12 // RCLK
// 端口
#define HC595_SPI1_GPIO GPIOA
#define HC595_RCLK_GPIO GPIOB
// 時鐘
#define HC595_SPI1_RCC RCC_APB2Periph_GPIOA
#define HC595_RCLK_RCC RCC_APB2Periph_GPIOB
void HC595_Init(void);
void HC595_SendByte(u8 data);
u8 HC595_Display(u16 num, u8 dp);
#endif
源文件74HC595_SPI.c
/************************省略部分代碼見(74HC595.c)************************/
/*******************************************************************************
* Function Name : HC595_Init
* Description : 初始化HC595
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void HC595_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure; // 聲明一個結(jié)構(gòu)體變量
// 不需要開啟AFIO時鐘
RCC_APB2PeriphClockCmd(HC595_SPI1_RCC | HC595_RCLK_RCC |
RCC_APB2Periph_SPI1, ENABLE); // 使能HC595及SPI1的時鐘
//74HC595, SPI1_NSS雏搂、SPI1_SCK凸郑、SPI1_MOSI
GPIO_InitStructure.GPIO_Pin = HC595_NSS_PIN | HC595_SCK_PIN
|HC595_MOSI_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 管腳頻率為50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 輸出模式為復用推挽輸出
GPIO_Init(HC595_SPI1_GPIO, &GPIO_InitStructure); // 初始化寄存器
//74HC595, SPI1_MISO
GPIO_InitStructure.GPIO_Pin = HC595_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 輸入模式為浮空輸入
GPIO_Init(HC595_SPI1_GPIO, &GPIO_InitStructure); // 初始化寄存器
//74HC595, RCLK
GPIO_InitStructure.GPIO_Pin = HC595_RCLK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 管腳頻率為50MHZ
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 輸出模式為復用推挽輸出
GPIO_Init(HC595_RCLK_GPIO, &GPIO_InitStructure); // 初始化寄存器
/* Initialize the SPI1 according to the SPI_InitStructure members */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
// 第一步:設(shè)置主從模式和通信速率
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
// SPI_NSS_Hard時需要外部電路把NSS接VCC, SPI_NSS_Soft時SPI外設(shè)會將SSM和SSI置位
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
// 實測波特率最低為SPI_BaudRatePrescaler_8,否則出錯
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
// 第二步:設(shè)置數(shù)據(jù)格式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
// MSB在前還是LSB在前要根據(jù)碼表和數(shù)碼管與74HC595的接法來定
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
// 第三步:設(shè)置時鐘和極性
// 當SPI_CPOL_Low且SPI_CPHA_2Edge出錯
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
//第四步:其它而昨,CRC校驗歌憨,可靠通信务嫡,這步可以不設(shè)置
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
/* Enable SPI1 */
SPI_Cmd(SPI1, ENABLE);
}
/*******************************************************************************
* Function Name : HC595_SendByte
* Description : 發(fā)送一個字節(jié)
* Input : data
* Output : None
* Return : None
*******************************************************************************/
void HC595_SendByte(u8 data)
{
SPI_I2S_SendData(SPI1, data);
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));
}
/************************省略部分代碼(見74HC595.c)************************/
這樣就大工告成啦心铃,STM32的SPI外設(shè)還是比較簡單的于个,尤其是通過庫函數(shù)來調(diào)用厅篓。用數(shù)碼管模塊這種簡單的可視化工具羽氮,我們就可以更好的研究通信協(xié)議本身的特性啦档押,后續(xù)我還會用這種方式來學習其它的通信協(xié)議令宿。
————————————————
spi相應的資料供大家后續(xù)的學習中用來參考