色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

GD32開發實戰指南(基礎篇) 第22章 SPI

嵌入式大雜燴 ? 來源:嵌入式大雜燴 ? 作者:嵌入式大雜燴 ? 2023-05-24 09:04 ? 次閱讀

開發環境:

MDK:Keil 5.30

開發板:GD32F207I-EVAL

MCU:GD32F207IK

1 SPI簡介

SPI,是Serial Peripheral interface的縮寫,顧名思義就是串行外圍設備接口。是Motorola首先在其MC68HCXX系列處理器上定義的。是一種高速全雙工的通信總線,它由摩托羅拉公司提出,當前最新的為 V04.01—2004 版。它被廣泛地使用在ADC、LCD 等設備與 MCU 間通信的場合。SPI接口主要應用在 EEPROM,FLASH,實時時鐘AD轉換器,還有數字信號處理器和數字信號解碼器之間。SPI,是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線,節約了芯片的管腳,同時為PCB的布局上節省空間,提供方便,正是出于這種簡單易用的特性,現在越來越多的芯片集成了這種通信協議。

1.1 SPI 信號

SPI 包含 4 條總線,SPI 總線包含 4 條總線,分別為SS、SCK、MOSI、MISO。它們的作用介紹如下 :

1)SS ( Slave Select):片選信號線,當有多個 SPI 設備與 MCU 相連時,每個設備的這個片選信號線是與 MCU 單獨的引腳相連的,而其他的 SCK、MOSI、MISO 線則為多個設備并聯到相同的 SPI 總線上,見下圖。當 SS 信號線為低電平時,片選有效,開始SPI 通信。

1684842210943wtxtixill8

2)SCK (Serial Clock):時鐘信號線,由主通信設備產生,不同的設備支持的時鐘頻率不一樣,如 GD32 的 SPI 時鐘頻率最大為 f PCLK /2。

3)MOSI (Master Output, Slave Input):主設備輸出 / 從設備輸入引腳。主機的數據從這條信號線輸出,從機由這條信號線讀入數據,即這條線上數據的方向為主機到從機。

4)MISO(Master Input, Slave Output):主設備輸入 / 從設備輸出引腳。主機從這條信號線讀入數據,從機的數據則由這條信號線輸出,即在這條線上數據的方向為從機到主機。

1.2 SPI模式

SPI通信中可作為從機也可以作為主機,這取決于硬件設計和軟件設置。

當器件作為主機時,使用一個IO引腳拉低相應從機的選擇引腳(NSS),傳輸的起始由主機發送數據來啟動,時鐘(SCK)信號由主機產生。通過MOSI發送數據,同時通過MISO引腳接收從機發出的數據。

當器件作為從機時,傳輸在從機選擇引腳(NSS)被主機拉低后開始,接收主機輸出的時鐘信號,在讀取主機數據的同時通過MISO引腳輸出數據。

根據 SPI 時鐘極性(CKPL)和時鐘相位(CKPH) 配置的不同,分為 4 種 SPI 模式。

時鐘極性是指 SPI 通信設備處于空閑狀態時(也可以認為這是 SPI 通信開始時,即SS 為低電平時),SCK 信號線的電平信號。CKPL=0 時, SCK 在空閑狀態時為低電平,CKPL=1 時則相反。

時鐘相位是指數據采樣的時刻,當 CKPH =0 時,MOSI 或 MISO 數據線上的信號將會在 SCK 時鐘線的奇數邊沿被采樣。當 CKPH=1 時,數據線在 SCK 的偶數邊沿采樣。

C:\\Users\\BruceOu\\Desktop\\111.png

我們來分析這個 CKPH =0 的時序圖。首先,由主機把片選信號線SS 拉低,即為圖中的SS (O)時序,意為主機輸出,SS (I)時序實際上也是SS 線信號,SS (I)時序表示從機接收到SS 片選被拉低的信號。

在SS 被拉低的時刻,SCK 分為兩種情況,若我們設置為 CKPL=0,則 SCK 時序在這個時刻為低電平,若設置為 CKPL=1,則 SCK 在這個時刻為高電平。

無論 CKPL=0 還是=1,因為我們配置的時鐘相位 CKPH =0,在采樣時刻的時序中我們可以看到,采樣時刻都是在 SCK 的奇數邊沿(注意奇數邊沿有時為下降沿,有時為上升沿)。因此,MOSI 和 MISO 數據線的有效信號在 SCK 的奇數邊沿保持不變,這個信號將在SCK 奇數邊沿時被采集,在非采樣時刻,MOSI 和 MISO 的有效信號才發生切換。

對于 CKPH =1 的情況也很類似,但數據信號的采樣時刻為偶數邊沿。使用 SPI 協議通信時,主機和從機的時序要保持一致,即兩者都選擇相同的 SPI 模式。

1.3 SPI特性

GD32的小容量有一個SPI接口,中容量有2個,大容量有3個接口,其特性如下所示。

  • 具有全雙工和單工模式的主從操作;
  • 16位寬度,獨立的發送和接收緩沖區;
  • 8位或16位數據幀格式;
  • 低位在前或高位在前的數據位順序;
  • 軟件和硬件NSS管理;
  • 硬件CRC計算、發送和校驗;
  • 發送和接收支持DMA模式;
  • 支持SPI四線功能的主機模式(只有SPI0)。

2 SPI架構

下圖所示為GD32的 SPI 架構圖,可以看到 MISO 數據線接收到的信號經移位寄存器處理后把數據轉移到接收緩沖區,然后這個數據就可以由我們的軟件從接收緩沖區讀出了。

1684842212106p6eaqgquvn

當要發送數據時,我們把數據寫入發送緩沖區,硬件將會把它用移位寄存器處理后輸出到 MOSI 數據線。

SCK 的時鐘信號則由波特率發生器產生,我們可以通過波特率控制位(PSC)來控制它輸出的波特率。

控制寄存器 CTL0掌管著主控制電路,GD32的 SPI 模塊的協議設置(時鐘極性、相位等)就是由它來制定的。而控制寄存器 CTL1則用于設置各種中斷使能。

最后為 NSS 引腳,這個引腳扮演著 SPI 協議中的SS 片選信號線的角色,如果我們把 NSS 引腳配置為硬件自動控制,SPI 模塊能夠自動判別它能否成為 SPI 的主機,或自動進入 SPI 從機模式。但實際上我們用得更多的是由軟件控制某些 GPIO 引腳單獨作為SS信號,這個 GPIO 引腳可以隨便選擇。

通常SPI通過4個引腳與外部器件相連:

● MISO:主設備輸入/從設備輸出引腳。該引腳在從模式下發送數據,在主模式下接收數據。

● MOSI:主設備輸出/從設備輸入引腳。該引腳在主模式下發送數據,在從模式下接收數據。

● SCK: 串口時鐘,作為主設備的輸出,從設備的輸入。

● NSS: 從設備選擇。這是一個可選的引腳,用來選擇主/從設備。它的功能是用來作為“片選引腳”,讓主設備可以單獨地與特定從設備通訊,避免數據線上的沖突。從設備的NSS引腳可以由主設備的一個標準I/O引腳來驅動。

3 SPI工作原理

3.1 (NSS)輸入輸出管理

  • (NSS)輸出管理

對于每個SPI的NSS可以輸入,也可以輸出。所謂輸入,就是NSS的電平信號給自己,所謂輸出,就是將NSS的電平信號發送出去,給從機。配置為輸出,還是不輸出,我們可以通過SPI_CTL1寄存器的NSSDRV位。當NSSDRV=1時,并且SPI處于主模式控制時(MSTMOD=1),NSS就輸出低電平,也就是拉低,因此當其他SPI設備的NSS引腳與它相連,必然接收到低電平,則片選成功,都成為從設備了。

  • (NSS)輸入管理

NSS軟件模式:

  • SPI主機:

需要設置SPI_CTL0寄存器的SWNSSEN=1和SWNSS=1,SWNSSEN=1是為了使能軟件管理,NSS有內部和外部引腳。這時候外部引腳留作他用(可以用來作為GPIO驅動從設備的片選信號)。內部NSS引腳電平則通過SPI_CTL0寄存器的SWNSS位來驅動。SWNSS=1是為了使NSS內電平為高電平。為什么主設備的內部NSS電平要為1呢?

GD32手冊上說,要保持MSTMOD=1和SPIEN=1,也就是說要保持主機模式,只有NSS接到高電平信號時,這兩位才能保持置‘1’。

  • SPI從機:

NSS引腳在完成字節傳輸之前必須連接到一個低電平信號。在軟件模式下,則需要設置SPI_CR1寄存器的SWNSSEN=1(軟件管理使能)和SWNSS=0.

NSS硬件模式:

對于主機,我們的NSS可以直接接到高電平.對于從機,NSS接低就可以。

3.2 單主和單從應用

1684842212488ey8d9bek5o

從上圖可以看出,主機和從機都有一個串行移位寄存器,主機通過向它的SPI串行寄存器寫入一個字節發起一次傳輸。寄存器通過MOSI信號將字節傳給從機,從機也將自己的移位寄存器中的內容通過MISO信號返還給主機。這樣,兩個移位寄存器中下的內容就被交換,外設的寫操作是同步完成的。如果只進行寫操作,主機只需忽略接收到的字節;反之,若主機要讀取從機的一個,就必須發送一個空字節來引發從機的傳輸。

3.3 時鐘信號的相位和極性

SPI_CTL0寄存器的CKPL和CKPH位,能夠組合成四種可能的時序關系。CKPL (時鐘極性)位控制在沒有數據傳輸時時鐘的空閑狀態電平,此位對主模式和從模式下的設備都有效。如果CKPL被清’0’,SCK引腳在空閑狀態保持低電平;如果CKPL被置’1’,SCK引腳在空閑狀態保持高電平。如果CKPH (時鐘相位)位被置’1’,SCK時鐘的第二個邊沿(CPOL位為0時就是下降沿,CKPL位為’1’時就是上升沿)進行數據位的采樣,數據在第二個時鐘邊沿被鎖存。如果CKPH位被清’0’,SCK時鐘的第一邊沿(CPOL位為’0’時就是下降沿,CKPL位為’1’時就是上升沿)進行數據位采樣,數據在第一個時鐘邊沿被鎖存。

CKPL時鐘極性和CKPH時鐘相位的組合選擇數據捕捉的時鐘邊沿。

3.4 數據幀格式

根據SPI_CTL0寄存器中的LF位,輸出數據位時可以MSB在先也可以LSB在先。根據SPI_CTL0寄存器的FF16位,每個數據幀可以是8位或是16位。所選擇的數據幀格式對發送和/或接收都有效。

3.5 SPI主從模式工作原理

配置SPI主模式的步驟如下:

設置SPI_CTL0寄存器的PSC [2:0]位,來定義串行時鐘波特率。

選擇CKPL和CKPH位,定義數據傳輸和串行時鐘間的相位關系。

設置FF16位來定義8或16位數據幀格式。

配置SPI_CTL0寄存器的LF位定義幀格式。

如果NSS引腳需要工作在輸入模式,硬件模式中在整個數據幀傳輸期間應把NSS引腳連接到高電平;在軟件模式中,需設置SPI_CTL0寄存器的SWNSSEN=1和SWNSS=1。如果NSS引腳工作在輸出模式,則只需設置SSOE=1位。

設置MSTMOD=1和SPIEN=1,只當NSS引腳被連到高電平,這些位才能保持置位。

配置SPI從模式的步驟如下:

設置FF16位以定義數據幀格式為8位或16位。

定義數據傳輸和串行時鐘之間的相位關系。

幀格式必須和主設備相同,MSB在前還是LSB在前取決于SPI_CTL0寄存器中的LF位。

硬件模式下,在完整的數據幀(8位或16位)發送過程中,NSS引腳必須為低電平。軟件模式下,設置SPI_CTL0寄存器中的SWNSSEN=1,SWNSS=0。

MSTMOD=0位,設置SPIEN=1,使相應引腳工作于SPI模式下。

3.6 狀態標志

應用程序通過3個狀態標志可以完全監控SPI總線的狀態。

1.發送緩沖器空閑標志(TBE)

此標志為’1’時表明發送緩沖器為空,可以寫下一個待發送的數據進入緩沖器中。當寫入SPI_DATA時,TBE標志被清除。

2.接收緩沖器非空(RBNE)

此標志為’1’時表明在接收緩沖器中包含有效的接收數據。讀SPI數據寄存器可以清除此標志。

3.忙(Busy)標志

TRANS標志由硬件設置與清除(寫入此位無效果),此標志表明SPI通信層的狀態。

3.7 SPI中斷

SPI的相關中斷標志如下:

中斷事件 事件標志 使能控制位
發送緩沖器空標志 TBE TBEIE
接收緩沖器非空標志 RBNE RBNEIE
主模式失效事件 CONFERR ERRIE
溢出錯誤 RXORERR
CRC****錯誤標志 CRCERR

4 硬件連接

GD25Q16BS是兆易創新推出的一款 SPI 接口的 NOR Flash 芯片,其存儲空間為 16Mbit,相當于2M 字節。

GD25Q16BS可以支持 SPI 的模式 0 和模式 3,也就是 CKPL=0/CKPH=0和CKPL=1/CKPH=1這兩種模式。

GD25Q16BS芯片支持 standard spi,Dual/Quad I/O SPI。

GD25Q16BS的擦寫周期多達5W 次,具有10年的數據保存期限,支持電壓為1.65~3.6V,GD25Q16BS支持標準的 SPI,還支持雙輸出/四輸出的 SPI,最大 SPI 時鐘可以到133Mhz(雙輸出時相當于266Mhz,四輸出時相當于532M)。

GD25Q16BS內部有一個“SPI Command & Control Logic”,可以通過 SPI 接口向其發送指令,從而執行相應操作。

【注】

①、Flash 寫入數據時和 EEPROM 類似,不能跨頁寫入,一次最多寫入一頁,GD25Q16BS的一頁是 256 字節。寫入數據一旦跨頁,必須在寫滿上一頁的時候,等待 Flash 將數據從緩存搬移到非易失區,重新再次往里寫。

②、Flash 有一個特點,就是可以將 1 寫成 0,但是不能將 0 寫成 1,要想將 0 寫成 1,必須進行擦除操作。因此通常要改寫某部分空間的數據,必須首先進行一定物理存儲空間擦除,最小的擦除空間,通常稱之為扇區,扇區擦除就是將這整個扇區每個字節全部變成 0xFF。

我的開發板選用的Flash是GD25Q16BS,容量為2M,掛載在SPI0上,如下圖所示。

1684842212747bclncfwwvf

5 SPI具體代碼實現

首先是SPI的硬件初始化。

/*
   brief      initialize SPI1 GPIO and parameter
   param[in]  none
   param[out] none
   retval     none
*/
void spi_flash_init(void)
{
    spi_parameter_struct spi_init_struct;

    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_AF);
    rcu_periph_clock_enable(RCU_SPI0);

    /* SPI0_CLK(PA5), SPI0_MISO_IO1(PA6), SPI0_MOSI_IO0(PA7) GPIO pin configuration */
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
    /* SPI0_CS(PB1) GPIO pin configuration */
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);

    /* chip select invalid */
    SPI_FLASH_CS_HIGH();

    /* SPI0 parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX; /*SPI receive and send data at fullduplex communication*/
    spi_init_struct.device_mode          = SPI_MASTER; /* SPI as master*/
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT; /* SPI frame size is 8 bits*/
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; /*SPI clock polarity is low level and phase is first edge*/
    spi_init_struct.nss                  = SPI_NSS_SOFT; /* SPI NSS control by sofrware */
    spi_init_struct.prescale             = SPI_PSC_32;  /* SPI clock prescale factor is 32 */
    spi_init_struct.endian               = SPI_ENDIAN_MSB; /* SPI transmit way is big endian: transmit MSB first */
    spi_init(SPI0, &spi_init_struct);
    /* enable SPI0 */
    spi_enable(SPI0);
}

SPI的硬件初始化最重要的函數就是spi_init ()。

void spi_init(uint32_t spi_periph, spi_parameter_struct *spi_struct)

其中SPI參數配置的結構體為spi_parameter_struct;。

/* SPI and I2S parameter struct definitions */
typedef struct {
    uint32_t device_mode;                                                       /*!< SPI master or slave */
    uint32_t trans_mode;                                                        /*!< SPI transfer type */
    uint32_t frame_size;                                                        /*!< SPI frame size */
    uint32_t nss;                                                               /*!< SPI NSS control by hardware or software */
    uint32_t endian;                                                            /*!< SPI big endian or little endian */
    uint32_t clock_polarity_phase;                                              /*!< SPI clock phase and polarity */
    uint32_t prescale;                                                          /*!< SPI prescaler factor */
} spi_parameter_struct;

spi_parameter_struct結構體成員變量如下:

  • trans_mode用來設置 SPI 的通信方式,可以選擇為半雙工,全雙工,以及串行發和串行收方式,這里設置的全雙工(SPI_TRANSMODE_FULLDUPLEX)。
  • device_mode用來設置 SPI 的主從模式。SCK 的時序是由通訊中的主機產生的。若被配置為從機模式,GD32的 SPI 外設將接受外來的 SCK 信號。
  • frame_size為 8 位還是 16 位幀格式選擇項。
  • clock_polarity_phase 用來設置時鐘極性與設置時鐘相位,就是選擇在串行同步時鐘的第幾個跳變沿(上升或下降)數據被采樣。
  • nss設置NSS 信號由硬件(NSS 管腳)還是軟件控制。可以選擇為硬件模式(SPI_NSS_HARD)與軟件模式(SPI_NSS_SOFT),在硬件模式中的 SPI 片選信號由 SPI 硬件自動產生,而軟件模式則需要我們親自把相應的 GPIO 端口拉高或置低產生非片選和片選信號。實際中軟件模式應用比較多。
  • prescale設置 SPI 波特率預分頻值決定 SPI 的時鐘的參數,從不分頻道 256 分頻 8 個可選值。2-156,凡是2的幾次方都可以。
  • endian設置數據傳輸順序是 MSB 位在前還是 LSB 位在前

SPI Flash的讀寫操作如下:

/*
   brief      read a byte from the SPI flash
   param[in]  none
   param[out] none
   retval     byte read from the SPI flash
*/
uint8_t spi_flash_read_byte(void)
{
    return(spi_flash_send_byte(DUMMY_BYTE));
}

/*
   brief      send a byte through the SPI interface and return the byte received from the SPI bus
   param[in]  byte: byte to send
   param[out] none
   retval     the value of the received byte
*/
uint8_t spi_flash_send_byte(uint8_t byte)
{
    /* loop while data register in not emplty */
    while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE));

    /* send byte through the SPI0 peripheral */
    spi_i2s_data_transmit(SPI0, byte);

    /* wait to receive a byte */
    while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE));

    /* return the byte read from the SPI bus */
    return(spi_i2s_data_receive(SPI0));
}

發送數據前要等待發送緩沖區為空,靠TBE標志判斷,所以開始的while循環是等待發送緩沖區為空,同時,等待接收緩沖區是否有數據,靠RBNE標志來判斷,把接收緩沖區的數據作為返回值返回。由于發送和接收是同時進行的,而且要接收一個數據時必須在有效的SCK下,而只有發送數據才能產生有效的SCK,所以接收數據的函數時在發送數據的函數的基礎上,將發送的數據設置為Dummy_Byte假數據來騙取有效的SCK。

SPI Flash讀寫Buffer操作如下:

/*
   brief      write block of data to the flash
   param[in]  pbuffer: pointer to the buffer
   param[in]  write_addr: flash's internal address to write
   param[in]  num_byte_to_write: number of bytes to write to the flash
   param[out] none
   retval     none
*/
void spi_flash_buffer_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
{
    uint8_t num_of_page = 0, num_of_single = 0, addr = 0, count = 0, temp = 0;

    addr          = write_addr % SPI_FLASH_PAGE_SIZE;
    count         = SPI_FLASH_PAGE_SIZE - addr;
    num_of_page   = num_byte_to_write / SPI_FLASH_PAGE_SIZE;
    num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;

    /* write_addr is SPI_FLASH_PAGE_SIZE aligned */
    if(0 == addr)
    {
        /* num_byte_to_write < SPI_FLASH_PAGE_SIZE */
        if(0 == num_of_page)
        {
            spi_flash_page_write(pbuffer, write_addr, num_byte_to_write);
        }
        else
        {
            /* num_byte_to_write >= SPI_FLASH_PAGE_SIZE */
            while(num_of_page--)
            {
                spi_flash_page_write(pbuffer, write_addr, SPI_FLASH_PAGE_SIZE);
                write_addr += SPI_FLASH_PAGE_SIZE;
                pbuffer += SPI_FLASH_PAGE_SIZE;
            }
            spi_flash_page_write(pbuffer, write_addr, num_of_single);
        }
    }
    else
    {
        /* write_addr is not SPI_FLASH_PAGE_SIZE aligned */
        if(0 == num_of_page)
        {
            /* (num_byte_to_write + write_addr) > SPI_FLASH_PAGE_SIZE */
            if(num_of_single > count)
            {
                temp = num_of_single - count;
                spi_flash_page_write(pbuffer, write_addr, count);
                write_addr += count;
                pbuffer += count;
                spi_flash_page_write(pbuffer, write_addr, temp);
            }
            else
            {
                spi_flash_page_write(pbuffer, write_addr, num_byte_to_write);
            }
        }
        else
        {
            /* num_byte_to_write >= SPI_FLASH_PAGE_SIZE */
            num_byte_to_write -= count;
            num_of_page = num_byte_to_write / SPI_FLASH_PAGE_SIZE;
            num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;

            spi_flash_page_write(pbuffer, write_addr, count);
            write_addr += count;
            pbuffer += count;

            while(num_of_page--)
            {
                spi_flash_page_write(pbuffer, write_addr, SPI_FLASH_PAGE_SIZE);
                write_addr += SPI_FLASH_PAGE_SIZE;
                pbuffer += SPI_FLASH_PAGE_SIZE;
            }

            if(0 != num_of_single)
            {
                spi_flash_page_write(pbuffer, write_addr, num_of_single);
            }
        }
    }
}

/*
   brief      read a block of data from the flash
   param[in]  pbuffer: pointer to the buffer that receives the data read from the flash
   param[in]  read_addr: flash's internal address to read from
   param[in]  num_byte_to_read: number of bytes to read from the flash
   param[out] none
   retval     none
*/
void spi_flash_buffer_read(uint8_t *pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)
{
    /* select the flash: chip slect low */
    SPI_FLASH_CS_LOW();

    /* send "read from memory " instruction */
    spi_flash_send_byte(READ);

    /* send read_addr high nibble address byte to read from */
    spi_flash_send_byte((read_addr & 0xFF0000) >> 16);
    /* send read_addr medium nibble address byte to read from */
    spi_flash_send_byte((read_addr & 0xFF00) >> 8);
    /* send read_addr low nibble address byte to read from */
    spi_flash_send_byte(read_addr & 0xFF);

    /* while there is data to be read */
    while(num_byte_to_read--)
    {
        /* read a byte from the flash */
        *pbuffer = spi_flash_send_byte(DUMMY_BYTE);
        /* point to the next location where the byte read will be saved */
        pbuffer++;
    }

    /* deselect the flash: chip select high */
    SPI_FLASH_CS_HIGH();
}

主函數代碼如下:

/*
    brief      main function
    param[in]  none
    param[out] none
    retval     none
*/
int main(void)
{
    st_bsp_usart_dev bsp_usart_dev0 = USART_DEV0_CONFIG;
    st_bsp_led_dev bsp_led_dev0 = LED_DEV0_CONFIG;

    //systick init
    sysTick_init();

    // led init
    bsp_led_init(&bsp_led_dev0);

    //usart init 115200 8-N-1
    bsp_usart_init(&bsp_usart_dev0, USART_MODE_EXTI, 115200, 0, 1);

    /* configure SPI and parameter */
    spi_flash_init();

    /* GD32207i-EVAL start up */
    printf("\\n\\rGD32207i-EVAL System is Starting up...\\n\\r");
    printf("\\n\\rGD32207i-EVAL Flash:%dK\\n\\r", *(__IO uint16_t *)(0x1FFFF7E0));
   /* get chip serial number */
    get_chip_serial_num();

    /* printf CPU unique device id */
    printf("\\n\\rGD32207i-EVAL The CPU Unique Device ID:[%X-%X-%X]\\n\\r", int_device_serial[2], int_device_serial[1], int_device_serial[0]);

    printf("\\n\\rGD32207i-EVAL SPI Flash:GD25Q16 configured...\\n\\r");

    /* get flash id */
    flash_id = spi_flash_read_id();
    printf("\\n\\rThe Flash_ID:0x%X\\n\\r\\n\\r", flash_id);

    /* flash id is correct */
    if(SFLASH_ID == flash_id)
    {
        printf("\\n\\rWrite to tx_buffer:\\n\\r\\n\\r");

        /* printf tx_buffer value */
        for(i = 0; i < BUFFER_SIZE; i++) 
        {
            tx_buffer[i] = i;
            printf("0x%02X ", tx_buffer[i]);

            if(15 == i % 16)
            {
                printf("\\n\\r");
            }
        }

        printf("\\n\\r\\n\\rRead from rx_buffer:\\n\\r\\n\\r");

        /* erase the specified flash sector */
        spi_flash_sector_erase(FLASH_WRITE_ADDRESS);

        /* write tx_buffer data to the flash */
        spi_flash_buffer_write(tx_buffer, FLASH_WRITE_ADDRESS, 256);

        delay_ms(10);
        /* read a block of data from the flash to rx_buffer */
        spi_flash_buffer_read(rx_buffer, FLASH_READ_ADDRESS, 256);   

        /* printf rx_buffer value */
        for(i = 0; i < BUFFER_SIZE; i ++)
        {
            printf("0x%02X ", rx_buffer[i]);
            if(15 == i % 16)
            {
                printf("\\n\\r");
            }
        }

        if(ERROR == memory_compare(tx_buffer, rx_buffer, 256)) {
            printf("\\n\\rErr:Data Read and Write aren't Matching.\\n\\r");
            is_successful = 1;
        }

        /* spi qspi flash test passed */
        if(0 == is_successful)
        {
            printf("\\n\\rSPI-GD25Q16 Test Passed!\\n\\r");
        }
    }
    else
    {
        /* spi flash read id fail */
        printf("\\n\\rSPI Flash: Read ID Fail!\\n\\r");
    }
    while(1)
    {
        bsp_led_toggle(&bsp_led_dev0);
        delay_ms(1000);
    }
}

首先對SPI進行初始化,然后就極性FLASH的讀取,完整代碼請參看源碼。

6 實驗現象

電腦端打開串口調試助手工具,設置參數為115200 8-N-1。下載完程序之后,在串口調試助手窗口可接收到信息

16848422131184oulwdc9in

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • mcu
    mcu
    +關注

    關注

    146

    文章

    17123

    瀏覽量

    350983
  • SPI
    SPI
    +關注

    關注

    17

    文章

    1706

    瀏覽量

    91502
  • 開發板
    +關注

    關注

    25

    文章

    5032

    瀏覽量

    97371
  • Cortex-M
    +關注

    關注

    2

    文章

    229

    瀏覽量

    29752
  • GD32
    +關注

    關注

    7

    文章

    403

    瀏覽量

    24328
收藏 人收藏

    評論

    相關推薦

    GD32開發實戰指南(基礎) 1 開發環境搭建

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK 1 GD32F207I-EVAL
    的頭像 發表于 05-07 23:35 ?1.1w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>1<b class='flag-5'>章</b> <b class='flag-5'>開發</b>環境搭建

    GD32開發實戰指南(基礎) 4 GD32啟動流程詳解(Keil版)

    ,所有的一切都需要由開發者來設置,這里處理器是沒有堆棧,沒有中斷,更沒有外圍設備,這些工作是需要軟件來指定的,而且不同的CPU類型、不同大小的內存和不同種類的外設,其初始化工作都是不同的。本文將以GD32F207IK (基于Cortex-M3)為例進行講解。
    的頭像 發表于 05-10 09:00 ?1.7w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>4<b class='flag-5'>章</b> <b class='flag-5'>GD32</b>啟動流程詳解(Keil版)

    GD32開發實戰指南(基礎) 7 定時器

    系統滴答定時器一般用來提供“心跳”作用,而GD32定時器最基本功能也是定時,可以設置不同時間長度的定時。定時器除了最基本的定時功能外,定時器與GPIO有掛鉤使得它可以發揮強大的作用,比如可以輸出
    的頭像 發表于 05-11 09:00 ?1.2w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>7<b class='flag-5'>章</b> 定時器

    GD32開發實戰指南(基礎) 8 定時器

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK 1 PWM輸出的工作原理 脈沖寬度調制(PWM) ,是英文“Pulse Wi
    的頭像 發表于 05-12 22:14 ?7927次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>8<b class='flag-5'>章</b> 定時器

    GD32開發實戰指南(基礎) 11 CPU的高級代理-DMA

    或者存儲器和存儲器之間的高速數據傳輸,因而被廣泛地使用。早在 8086 的應用中就已經有 Intel 的 8237 這種典型的 DMA 控制器,而 GD32的 DMA 則是以類似外設的形式添加到 Cortex 內核之外的。可以說,DMA就是CPU的高級代理,DMA大大減輕了CPU的負擔。
    的頭像 發表于 05-16 08:59 ?3864次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>11<b class='flag-5'>章</b> CPU的高級代理-DMA

    GD32開發實戰指南(基礎) 12 ADC

    GD32F2系列有 3 個逐次逼近型的ADC,精度為 12 位,有18個多路復用通道,可以轉換來自16個外部通道和2個內部通道的模擬信號。其中ADC0 和 ADC1都有 16 個外部通道, ADC2
    的頭像 發表于 05-16 09:03 ?1.1w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>12<b class='flag-5'>章</b> ADC

    GD32開發實戰指南(基礎) 14 內部溫度傳感器

    GD32 有一個內部的溫度傳感器,可以用來測量 CPU 及周圍的溫度(TA)。該溫度傳感器在內部和 ADCx_IN16 輸入通道相連接,此通道把傳感器輸出的電壓轉換成數字值。溫度傳感器模擬輸入
    的頭像 發表于 05-17 08:58 ?5339次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>14<b class='flag-5'>章</b> 內部溫度傳感器

    GD32開發實戰指南(基礎) 15 低功耗

    GD32的工作電壓(VDD)為2.0~3.6V。通過內置的電壓調節器提供所需的1.8V電源。當主電源VDD掉電后,通過VBAT腳為實時時鐘(RTC)和備份寄存器提供電源。
    的頭像 發表于 05-17 08:59 ?8049次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>15<b class='flag-5'>章</b> 低功耗

    GD32開發實戰指南(基礎) 16 RTC

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK 1 RTC工作原理 1.1 RTC簡介
    的頭像 發表于 05-18 22:14 ?7153次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>16<b class='flag-5'>章</b> RTC

    GD32開發實戰指南(基礎) 17 看門狗

    開發環境: MDK:Keil 5.30 開發板:GD32F207I-EVAL MCU:GD32F207IK GD32 有兩個看門狗, 一個是
    的頭像 發表于 06-03 16:00 ?1.1w次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>17<b class='flag-5'>章</b> 看門狗

    【圖書分享】《STM32庫開發實戰指南

    畫板 22 字庫及BMP圖片顯示 23 OV7670攝像頭驅動 24
    發表于 03-13 17:01

    GD32 MCU原理及固件庫開發指南》 + 初讀感悟

    GD32 MCU原理固件庫開發指南這本書內容豐富,囊括了GD32中的所有外設,書中首先介紹了如何使用MDK或IAR軟件搭建GD32工程環境,讓初學者能快速基于工程上手編程。書中主要對
    發表于 03-31 22:11

    GD32 MCU原理及固件庫開發指南》+讀后感

    2介紹GD32 MCU快速入門與開發平臺搭建的方法,包括對軟硬件開發平臺、調試工具、GD32
    發表于 06-06 21:52

    GD32SPI部分需要特別注意的部分 以及STM32移植到GD32SPI需要修改的部分

    如題,本文主要分享我在將STM32代碼移植到GD32上時SPI部分出現的問題,(GD32F103VET6)。業務邏輯:GD32SPI作為從機接收傳感器發送的數據,傳感器為主機。
    發表于 12-02 15:21 ?51次下載
    <b class='flag-5'>GD32</b>中<b class='flag-5'>SPI</b>部分需要特別注意的部分 以及STM32移植到<b class='flag-5'>GD32SPI</b>需要修改的部分

    GD32開發實戰指南(基礎) 19 程序加密

    GD32通過讀取芯片唯一ID號來實現程序的保護,防止被抄襲。96位的產品唯一身份標識所提供的參考號碼對任意一個GD32微控制器
    的頭像 發表于 05-20 09:10 ?4140次閱讀
    <b class='flag-5'>GD32</b><b class='flag-5'>開發</b><b class='flag-5'>實戰</b><b class='flag-5'>指南</b>(基礎<b class='flag-5'>篇</b>) <b class='flag-5'>第</b>19<b class='flag-5'>章</b> 程序加密
    主站蜘蛛池模板: 无限资源在线完整高清观看1| 杨幂视频1分11未删减在线观看| 2020久久精品永久免费| 国产成人亚洲精品老王| 免费三级播放器| 亚洲一品AV片观看五月色婷婷 | 快播h动漫网站| 香蕉水蜜桃牛奶涩涩| WWW国产无套内射久久| 久久久中日AB精品综合| 午夜伦理伦理片在线观| 超碰 无码 中文字幕| 美女伸开两腿让我爽| 亚洲视频免费在线观看| 国产精品视频国产永久视频 | 成人在线观看国产| 哪灬你的鸣巴好大| 诱受H嗯啊巨肉舍友1V1| 国产亚洲精品久久久久小| 日韩欧美亚洲精品综合在线 | 久久亚洲精品AV成人无码| 小SAO货水真多把你CAO烂| 纯肉小黄文高H| 青青草在线视频| 99久久久无码国产精品不卡按摩| 久久精品观看| 亚洲中文字幕欧美自拍一区 | 乳欲性高清在线| sao虎影院桃红视频在线观看| 麻豆精品传媒卡一卡二传媒短视频| 亚洲精品中文字幕制| 国产麻豆剧看黄在线观看| 少妇高潮久久久久7777| 成人国产免费| 日本电影护士| G0GO人体大尺香蕉| 欧美特级另类xxx| 99热热在线精品久久| 嫩草影院永久在线一二三四| 538prom精品视频我们不只是 | 秋霞成人午夜鲁丝一区二区三区|