stm32之SPI通信之學習分析附源碼

  之前一直對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ù)的學習中用來參考

stm32之SPI通信

通信協(xié)議 - UART串口協(xié)議

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市癞松,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌硕勿,老刑警劉巖源武,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異查排,居然都是意外死亡跋核,警方通過查閱死者的電腦和手機叛买,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門刻伊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捶箱,“玉大人丁屎,你說我怎么就攤上這事晨川」猜牵” “怎么了妈拌?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵供炎,是天一觀的道長音诫。 經(jīng)常有香客問我竭钝,道長香罐,這世上最難降的妖魔是什么庇茫? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮宁炫,結(jié)果婚禮上羔巢,老公的妹妹穿的比我還像新娘竿秆。我一直安慰自己幽钢,他們只是感情好搅吁,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般享甸。 火紅的嫁衣襯著肌膚如雪蛉威。 梳的紋絲不亂的頭發(fā)上蚯嫌,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天束凑,我揣著相機與錄音汪诉,去河邊找鬼扒寄。 笑死孝常,一個胖子當著我的面吹牛上渴,可吹牛的內(nèi)容都是我干的稠氮。 我是一名探鬼主播隔披,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼纠永,長吁一口氣:“原來是場噩夢啊……” “哼尝江!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤相恃,失蹤者是張志新(化名)和其女友劉穎侨歉,沒想到半個月后幽邓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柒啤,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年送火,在試婚紗的時候發(fā)現(xiàn)自己被綠了种吸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镜盯。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡辙浑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出送滞,到底是詐尸還是另有隱情犁嗅,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏项栏。R本人自食惡果不足惜案腺,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一访递、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧同辣,春花似錦拷姿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棒妨,卻和暖如春踪古,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工伏穆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拘泞,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓枕扫,卻偏偏與公主長得像陪腌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子烟瞧,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 姓名:周崇杰 學號:16140120059 專業(yè):機械設(shè)計制造及其自動化 轉(zhuǎn)載自:http://blog.csd...
    CJbaby閱讀 4,699評論 0 5
  • 有一次做一個東西,為了盡量不占用CPU的處理數(shù)據(jù)時間,所以就使用DMA接收串口的數(shù)據(jù),但是呢問題來了.,,,,,怎...
    楊奉武閱讀 3,142評論 0 1
  • //#include "../user/main.h" include "stm32f4xx.h" include...
    fee87be72e84閱讀 2,031評論 0 0
  • 重點是說SPI通信協(xié)議,,,, 不要害怕協(xié)議因為協(xié)議是人規(guī)定的,,剛好我也是人......規(guī)定的協(xié)議既然能成為規(guī)范...
    楊奉武閱讀 994評論 0 1
  • 昨天在朋友的菜地里幫忙收菜参滴,干了下午的半天强岸,雖然肩膀酸痛,渾身乏軟卵洗,但是那久違了的田野里勞作的感覺讓人愉快请唱。 不曾...
    花開向暖閱讀 526評論 0 0