chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

RT-Thread記錄(十五、I/O 設(shè)備模型之SPI設(shè)備)

矜辰所致 ? 來(lái)源:矜辰所致 ? 作者:矜辰所致 ? 2022-07-04 15:46 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

本文學(xué)習(xí)一下I/O 設(shè)備模型之SPI設(shè)備使用,I/O 設(shè)備模型篇的最后一篇文章。

目錄

前言
一、SPI 通訊基礎(chǔ)
二、SPI 設(shè)備操作函數(shù)

2.1 掛載 SPI 設(shè)備
2.2 配置 SPI 設(shè)備
2.3 訪問(wèn) SPI設(shè)備
2.3.1 查找 SPI 設(shè)備
2.3.2 自定義數(shù)據(jù)傳輸
2.3.3 數(shù)據(jù)收發(fā)函數(shù)
2.3.4 特殊場(chǎng)景

三、SPI 設(shè)備測(cè)試

3.1 SPI 設(shè)備使用步驟
3.2 測(cè)試

結(jié)語(yǔ)

前言

本文應(yīng)該是 RT-Thread I/O 設(shè)備模型最后一篇,SPI 設(shè)備的學(xué)習(xí)測(cè)試。

我以前就說(shuō)過(guò),我的記錄是以應(yīng)用為目的,實(shí)際上我們?cè)谑褂?RT-Thread 的時(shí)候,有很多常用的設(shè)備,官方或者很多開(kāi)發(fā)者都已經(jīng)給我們寫好了驅(qū)動(dòng)和軟件包,我們并不需要自己重新寫一篇,很多時(shí)候直接導(dǎo)入軟件包,直接調(diào)用現(xiàn)成的 API 函數(shù)就可以。

RT-Thread 文章接下來(lái)的系列,應(yīng)該會(huì)更新幾篇 軟件包和組件的使用,本文把 SPI 設(shè)備做一個(gè)學(xué)習(xí)測(cè)試。

??
本 RT-Thread 專欄記錄的開(kāi)發(fā)環(huán)境:
RT-Thread記錄(一、RT-Thread 版本、RT-Thread Studio開(kāi)發(fā)環(huán)境 及 配合CubeMX開(kāi)發(fā)快速上手)
RT-Thread記錄(二、RT-Thread內(nèi)核啟動(dòng)流程 — 啟動(dòng)文件和源碼分析)
??
RT-Thread 內(nèi)核篇系列博文鏈接:
RT-Thread記錄(三、RT-Thread 線程操作函數(shù)及線程管理與FreeRTOS的比較)
RT-Thread記錄(四、RT-Thread 時(shí)鐘節(jié)拍和軟件定時(shí)器
RT-Thread記錄(五、RT-Thread 臨界區(qū)保護(hù))
RT-Thread記錄(六、IPC機(jī)制之信號(hào)量、互斥量和事件集)
RT-Thread記錄(七、IPC機(jī)制之郵箱、消息隊(duì)列)
RT-Thread記錄(八、理解 RT-Thread 內(nèi)存管理)
RT-Thread記錄(九、RT-Thread 中斷處理與階段小結(jié))
??
STM32L051C8 上使用 RT-Thread 應(yīng)用篇系列博文連接:
RT-Thread 應(yīng)用篇 — 在STM32L051上使用 RT-Thread (一、無(wú)線溫濕度傳感器 之 新建項(xiàng)目)
RT-Thread 應(yīng)用篇 — 在STM32L051上使用 RT-Thread (二、無(wú)線溫濕度傳感器 之 CubeMX配置)
RT-Thread 應(yīng)用篇 — 在STM32L051上使用 RT-Thread (三、無(wú)線溫濕度傳感器 之 I2C通訊)
RT-Thread 應(yīng)用篇 — 在STM32L051上使用 RT-Thread (四、無(wú)線溫濕度傳感器 之 串口通訊)
??
RT-Thread 設(shè)備篇系列博文鏈接:
RT-Thread記錄(十、全面認(rèn)識(shí) RT-Thread I/O 設(shè)備模型)
RT-Thread記錄(十一、I/O 設(shè)備模型之UART設(shè)備 — 源碼解析)
RT-Thread記錄(十二、I/O 設(shè)備模型之UART設(shè)備 — 使用測(cè)試)
RT-Thread記錄(十三、I/O 設(shè)備模型之PIN設(shè)備)
RT-Thread記錄(十四、I/O 設(shè)備模型之ADC設(shè)備)

一、SPI 通訊基礎(chǔ)

SPI 通訊基本知識(shí)不過(guò)多介紹,原理與基礎(chǔ)可自行網(wǎng)上查詢,本文這里只做應(yīng)用所需的簡(jiǎn)單概述:

SPI是串行外設(shè)接口(Serial Peripheral Interface)的縮寫,是一種高速的,全雙工,同步的通信總線,SPI 的通訊速度可以達(dá)到幾十M,并且在芯片的管腳上只占用四根線:

(1)MISO– Master Input Slave Output,主設(shè)備數(shù)據(jù)輸入,從設(shè)備數(shù)據(jù)輸出;
(2)MOSI– Master Output Slave Input,主設(shè)備數(shù)據(jù)輸出,從設(shè)備數(shù)據(jù)輸入;
(3)SCLK – Serial Clock,時(shí)鐘信號(hào),由主設(shè)備產(chǎn)生;
(4)CS – Chip Select,從設(shè)備使能信號(hào),由主設(shè)備控制。

pYYBAGLCmuWAfYoIAAA25IYPAK4385.png

SPI 以主從方式工作,通常有一個(gè)主設(shè)備和一個(gè)或多個(gè)從設(shè)備。

SPI 通訊有4中模式,由 CPOL (時(shí)鐘的極性)和 CPHA (時(shí)鐘的相位)決定:

CPOL=0,表示當(dāng)SCLK=0時(shí)處于空閑態(tài),空閑低電平,所以有效狀態(tài)就是SCLK處于高電平時(shí)
CPOL=1,表示當(dāng)SCLK=1時(shí)處于空閑態(tài),空閑高電平,所以有效狀態(tài)就是SCLK處于低電平時(shí)
CPHA=0,表示數(shù)據(jù)采樣是在第1個(gè)邊沿
CPHA=1,表示數(shù)據(jù)采樣是在第2個(gè)邊沿

如下表格:

pYYBAGLCjryAbKy8AAA8Tlo2bN8553.png

對(duì)于我們的從機(jī)設(shè)備,比如傳感器,支持的模式會(huì)使用手冊(cè)中說(shuō)明:比如我們今天要測(cè)試的 SPI Flash:

poYBAGLCmuWANaQPAARKR5WikzQ812.png

二、SPI 設(shè)備操作函數(shù)

來(lái)了解一下 RT-Thread 提供的 SPI 設(shè)備操作函數(shù):

poYBAGLCjuGAI_AjAAB4Oi-n-08144.png

與前面的設(shè)備不同的地方在于,SPI 因?yàn)榭梢砸恢鞫鄰?,所?SPI 設(shè)備多了一個(gè)掛載操作,就是 RT-Thread 系統(tǒng)驅(qū)動(dòng)會(huì)注冊(cè)好 SPI 總線,然后我們需要把自己所用的 SPI 設(shè)備掛載到總線上,使得可以對(duì)該設(shè)備進(jìn)行操作 。

☆ 自定義傳輸數(shù)據(jù)函數(shù) rt_spi_transfer_message 為核心,其實(shí)在其之后的那些都可以使用這個(gè)函數(shù)來(lái)表達(dá),這個(gè)下文會(huì)說(shuō)明?!?/strong>

2.1 掛載 SPI 設(shè)備

SPI 驅(qū)動(dòng)注冊(cè)完 SPI 總線,需要用 SPI 掛載函數(shù)將要使用的 SPI 設(shè)備需要掛載到已經(jīng)注冊(cè)好的 SPI 總線上:

/*
參數(shù) 	描述
device 		SPI 設(shè)備句柄
name 		SPI 設(shè)備名稱
bus_name 	SPI 總線名稱
user_data 	用戶數(shù)據(jù)指針
返回 	——
RT_EOK 		成功
其他錯(cuò)誤碼 	失敗
*/
rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
                                  const char           *name,
                                  const char           *bus_name,
                                  void                 *user_data)

此函數(shù)用于掛載一個(gè) SPI 設(shè)備到指定的 SPI 總線,并向內(nèi)核注冊(cè) SPI 設(shè)備,并將 user_data 保存到 SPI 設(shè)備的控制塊里。

一般 SPI 總線命名原則為 spix, SPI 設(shè)備命名原則為 spixy ,如 spi10 表示掛載在 spi1 總線上的 0 號(hào)設(shè)備。
user_data 一般為 SPI 設(shè)備的 CS 引腳指針,進(jìn)行數(shù)據(jù)傳輸時(shí) SPI 控制器會(huì)操作此引腳進(jìn)行片選。

對(duì)于我們測(cè)試使用的 STM32 而言,有專門的掛載函數(shù) rt_hw_spi_device_attach

/*
參數(shù) 	描述
bus_name 			SPI 總線名稱
device_name 		SPI 設(shè)備名稱

后面2個(gè)參數(shù)是設(shè)置片選引腳:

cs_gpiox			 GPIOA、GPIOB之類...
cs_gpio_pin  		 GPIO口名稱
返回 	——
RT_EOK 		成功
其他錯(cuò)誤碼 	失敗
*/
rt_err_t rt_hw_spi_device_attach(const char *bus_name, 
								 const char *device_name, 
								 GPIO_TypeDef *cs_gpiox, 
								 uint16_t cs_gpio_pin)

2.2 配置 SPI 設(shè)備

上面介紹 SPI 通訊基礎(chǔ)的時(shí)候講到過(guò) SPI 的工作模式等細(xì)節(jié),RT-Thread 里使用 SPI 配置函數(shù)進(jìn)行配置:

/*
參數(shù) 		描述
device 	SPI 設(shè)備句柄
cfg 	SPI 配置參數(shù)指針
返回 	——
RT_EOK 	成功
*/
rt_err_t rt_spi_configure(struct rt_spi_device        *device,
                          struct rt_spi_configuration *cfg)

...

/**
 * SPI configuration structure
 */
struct rt_spi_configuration
{
    rt_uint8_t mode;        /* 模式 */
    rt_uint8_t data_width;  /* 數(shù)據(jù)寬度,可取8位、16位、32位 */
    rt_uint16_t reserved;   /* 保留 */
    
    rt_uint32_t max_hz;     /* 最大頻率 */
};


/**
 * 上面結(jié)構(gòu)體第一個(gè)參數(shù): mode
 * SPI configuration structure
 * 其中與 SPI mode 相關(guān)的宏定義有
 */
#define RT_SPI_CPHA     (1<<0)                             /* bit[0]:CPHA, clock phase */
#define RT_SPI_CPOL     (1<<1)                             /* bit[1]:CPOL, clock polarity */
/* 設(shè)置數(shù)據(jù)傳輸順序是MSB位在前還是LSB位在前 */
#define RT_SPI_LSB      (0<<2)                             /* bit[2]: 0-LSB */
#define RT_SPI_MSB      (1<<2)                             /* bit[2]: 1-MSB */
/* 設(shè)置SPI的主從模式 */
#define RT_SPI_MASTER   (0<<3)                             /* SPI master device */
#define RT_SPI_SLAVE    (1<<3)                             /* SPI slave device */

#define RT_SPI_CS_HIGH  (1<<4)                             /* Chipselect active high */
#define RT_SPI_NO_CS    (1<<5)                             /* No chipselect */
#define RT_SPI_3WIRE    (1<<6)                             /* SI/SO pin shared */
#define RT_SPI_READY    (1<<7)                             /* Slave pulls low to pause */

#define RT_SPI_MODE_MASK    (RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_MSB | RT_SPI_SLAVE | RT_SPI_CS_HIGH | RT_SPI_NO_CS | RT_SPI_3WIRE | RT_SPI_READY)
/* 設(shè)置時(shí)鐘極性和時(shí)鐘相位 */
#define RT_SPI_MODE_0       (0 | 0)                        /* CPOL = 0, CPHA = 0 */
#define RT_SPI_MODE_1       (0 | RT_SPI_CPHA)              /* CPOL = 0, CPHA = 1 */
#define RT_SPI_MODE_2       (RT_SPI_CPOL | 0)              /* CPOL = 1, CPHA = 0 */
#define RT_SPI_MODE_3       (RT_SPI_CPOL | RT_SPI_CPHA)    /* CPOL = 1, CPHA = 1 */

#define RT_SPI_BUS_MODE_SPI         (1<<0)
#define RT_SPI_BUS_MODE_QSPI        (1<<1)

/**
 * 上面結(jié)構(gòu)體第二個(gè)和第四個(gè)參數(shù): data_width 和 max_hz
 */
//根據(jù) SPI 主設(shè)備及 SPI 從設(shè)備可發(fā)送及接收的數(shù)據(jù)寬度格式 和頻率 設(shè)置。


 /*
 * 示例程序
 */
 struct rt_spi_configuration cfg;
 cfg.data_width = 8;
 cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
 cfg.max_hz = 20 * 1000 *1000;                           /* 20M */

 rt_spi_configure(spi_dev, &cfg);

2.3 訪問(wèn) SPI設(shè)備

前面的兩個(gè)函數(shù)類似于 SPI 的初始化工作,接下來(lái)就是我們熟悉的設(shè)備操作函數(shù):

2.3.1 查找 SPI 設(shè)備

I/O 設(shè)備模型通用的查找函數(shù):

/*
參數(shù) 	描述
name 	SPI 設(shè)備名稱
返回 	——
設(shè)備句柄 	查找到對(duì)應(yīng)設(shè)備將返回相應(yīng)的設(shè)備句柄
RT_NULL 	沒(méi)有找到設(shè)備
*/
rt_device_t rt_device_find(const char* name);

注意事項(xiàng)和 ADC 設(shè)備一樣,用來(lái)接收的設(shè)備句柄不是使用rt_device_t ,但是與 ADC 也有不一樣的地方,具體如下圖:

pYYBAGLCmuWAHOXoAAC7LrtEE1E821.png

因?yàn)?SPI 設(shè)備的接口體并沒(méi)有 typedef 重定義,所以使用起來(lái)還得直接使用結(jié)構(gòu)體指針表示。

2.3.2 自定義數(shù)據(jù)傳輸

自定義傳輸函數(shù)rt_spi_transfer_message,是訪問(wèn) SPI 設(shè)備的關(guān)鍵函數(shù)!

獲取到 SPI 設(shè)備句柄就可以使用 SPI 設(shè)備管理接口訪問(wèn) SPI 設(shè)備器件,進(jìn)行數(shù)據(jù)收發(fā):

/*
參數(shù) 	描述
device 		SPI 設(shè)備句柄
message 	消息指針
返回 	——
RT_NULL 	成功發(fā)送
非空指針 	發(fā)送失敗,返回指向剩余未發(fā)送的 message 的指針
*/
struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device  *device,
                                               struct rt_spi_message *message)

其中第二個(gè)參數(shù),消息的結(jié)構(gòu)體,這也是發(fā)送消息的關(guān)鍵:

/**
 * SPI message structure
 */
struct rt_spi_message
{
    const void *send_buf;           /* 發(fā)送緩沖區(qū)指針,其值為 RT_NULL 時(shí),
    								表示本次傳輸為只接收狀態(tài),不需要發(fā)送數(shù)據(jù)。*/
    void *recv_buf;                 /* 接收緩沖區(qū)指針,其值為 RT_NULL 時(shí),
    								表示本次傳輸為只發(fā)送狀態(tài),不需要保存接收到的數(shù)據(jù) */
    rt_size_t length;               /* 發(fā)送 / 接收 數(shù)據(jù)字節(jié)數(shù),單位為 word ,
    								長(zhǎng)度為 8 位時(shí),每個(gè) length 占用 1 個(gè)字節(jié);
    								當(dāng)數(shù)據(jù)長(zhǎng)度為 16 位時(shí),每個(gè) length 占用 2 個(gè)字節(jié)*/
    struct rt_spi_message *next;    /* 指向繼續(xù)發(fā)送的下一條消息的指針 ,
    								若只發(fā)送一條消息,則此指針值為 RT_NULL。
    								多個(gè)待傳輸?shù)南⑼ㄟ^(guò) next 指針以單向鏈表的形式連接在一起。*/
    
    unsigned cs_take    : 1;        /* 片選選中 
    								cs_take 值為 1 時(shí),表示在傳輸數(shù)據(jù)前,設(shè)置對(duì)應(yīng)的 CS 為有效狀態(tài)。*/
    unsigned cs_release : 1;        /* 釋放片選 
   									 cs_release 值為 1 時(shí),表示在數(shù)據(jù)傳輸結(jié)束后,釋放對(duì)應(yīng)的 CS。*/
};

關(guān)于最后兩個(gè)參數(shù):

傳輸?shù)牡谝粭l消息 cs_take 需置為 1,設(shè)置片選為有效,

傳輸?shù)淖詈笠粭l消息的 cs_release 需置 1,釋放片選。

示例 1 ,只發(fā)一條(主要關(guān)注最后兩個(gè)參數(shù)的設(shè)置):

struct rt_spi_message msg1;


msg1.send_buf   = send_buf;
msg1.recv_buf   = receive_buf;
msg1.length     = send_length;
msg1.cs_take    = 1;     			// 傳輸之前要先把總線拉低
msg1.cs_release = 1;				// 傳輸之后要把總線釋放
msg1.next       = RT_NULL;


rt_spi_transfer_message(struct rt_spi_device *device, &msg1);

示例 2 ,先發(fā)后收(主要關(guān)注最后兩個(gè)參數(shù)的設(shè)置):

struct rt_spi_message msg1,msg2;

uint8 id[5] = {0};

msg1.send_buf   = send_buf;
msg1.recv_buf   = RT_NULL;
msg1.length     = send_length;
msg1.cs_take    = 1;     		// 傳輸之前要先把總線拉低
msg1.cs_release = 0;				// 本次結(jié)束之后并不釋放總線,因?yàn)檫€要發(fā)送,所以為0
msg1.next       = &msg2;

msg2.send_buf   = RT_NULL;
msg2.recv_buf   = id;
msg2.length     = 5; 		//接收5個(gè)字節(jié)
msg2.cs_take    = 0; 		//前面已經(jīng)拉低了,沒(méi)有釋放,所以這里是不需要拉低的
msg2.cs_release = 1;		//但是這個(gè)完成以后,需要釋放總線,這是結(jié)尾
msg2.next       = RT_NULL;


rt_spi_transfer_message(struct rt_spi_device *device, &msg1);

示例 3 ,假如有3個(gè) message:

struct rt_spi_message msg1,msg2,msg3;


msg1.send_buf   = send_buf;
msg1.recv_buf   = RT_NULL;
msg1.length     = length1;
msg1.cs_take    = 1;     		// 傳輸之前要先把總線拉低
msg1.cs_release = 0;				// 本次結(jié)束之后并不釋放總線,因?yàn)檫€要發(fā)送,所以為0
msg1.next       = &msg2;

msg2.send_buf   = RT_NULL;
msg2.recv_buf   = receive_buff;
msg2.length     = length2; 		
msg2.cs_take    = 0; 		//前面已經(jīng)拉低了,沒(méi)有釋放,所以這里是不需要拉低的
msg2.cs_release = 0;		//這里也不需要釋放,前面會(huì)拉,后面會(huì)放
msg2.next       = &msg3;


msg3.send_buf   = RT_NULL;
msg3.recv_buf   = receive_buff;
msg3.length     = len3; 		//
msg3.cs_take    = 0; 		//前面已經(jīng)拉低了,沒(méi)有釋放,所以這里是不需要拉低的
msg3.cs_release = 1;		//但是這個(gè)完成以后,需要釋放總線,這是結(jié)尾
msg3.next       = RT_NULL;


rt_spi_transfer_message(struct rt_spi_device *device, &msg1)

2.3.3 數(shù)據(jù)收發(fā)函數(shù)

除了上面通用的自定義數(shù)據(jù)傳輸函數(shù), RT-Thread 還提供了一系列簡(jiǎn)單的數(shù)據(jù)收發(fā)函數(shù),其實(shí)都是通過(guò)上面的函數(shù)演變而來(lái),我們也簡(jiǎn)單的過(guò)一遍:

傳輸一次數(shù)據(jù):

/*
參數(shù) 	描述
device 		SPI 設(shè)備句柄
send_buf 	發(fā)送數(shù)據(jù)緩沖區(qū)指針
recv_buf 	接收數(shù)據(jù)緩沖區(qū)指針
length 		發(fā)送/接收 數(shù)據(jù)字節(jié)數(shù)
返回 	——
0 	傳輸失敗
非 0 值 	成功傳輸?shù)淖止?jié)數(shù)
*/
rt_size_t rt_spi_transfer(struct rt_spi_device *device,
                          const void           *send_buf,
                          void                 *recv_buf,
                          rt_size_t             length)

使用此函數(shù)等同于:

struct rt_spi_message msg;

msg.send_buf   = send_buf;
msg.recv_buf   = recv_buf;
msg.length     = length;
msg.cs_take    = 1;
msg.cs_release = 1;
msg.next        = RT_NULL;


rt_spi_transfer_message(struct rt_spi_device *device, &msg);

發(fā)送一次數(shù)據(jù):

/*
參數(shù) 	描述
device 		SPI 設(shè)備句柄
send_buf 	發(fā)送數(shù)據(jù)緩沖區(qū)指針
length 		發(fā)送數(shù)據(jù)字節(jié)數(shù)
返回 	——
0 	發(fā)送失敗
非 0 值 	成功發(fā)送的字節(jié)數(shù)
*/
rt_inline rt_size_t rt_spi_send(struct rt_spi_device *device,
                                const void           *send_buf,
                                rt_size_t             length)
{
    return rt_spi_transfer(device, send_buf, RT_NULL, length);
}

此函數(shù)直接是上面函數(shù)忽略接收數(shù)據(jù)的效果,可以直接看上面的函數(shù)內(nèi)容。

接收一次數(shù)據(jù):

/*
參數(shù) 	描述
device 		SPI 設(shè)備句柄
recv_buf 	接收數(shù)據(jù)緩沖區(qū)指針
length 		接收數(shù)據(jù)字節(jié)數(shù)
返回 	——
0 		接收失敗
非 0 值 	成功接收的字節(jié)數(shù)
*/
rt_inline rt_size_t rt_spi_recv(struct rt_spi_device *device,
                                void                 *recv_buf,
                                rt_size_t             length)
{
    return rt_spi_transfer(device, RT_NULL, recv_buf, length);
}

與上面發(fā)送一次數(shù)據(jù)相反,傳輸一次數(shù)據(jù)函數(shù)忽略接收的數(shù)據(jù)。

連續(xù)兩次發(fā)送數(shù)據(jù):

/*
參數(shù) 	描述
device 	SPI 設(shè)備句柄
send_buf1 	發(fā)送數(shù)據(jù)緩沖區(qū) 1 指針
send_length1 	發(fā)送數(shù)據(jù)緩沖區(qū) 1 數(shù)據(jù)字節(jié)數(shù)
send_buf2 	發(fā)送數(shù)據(jù)緩沖區(qū) 2 指針
send_length2 	發(fā)送數(shù)據(jù)緩沖區(qū) 2 數(shù)據(jù)字節(jié)數(shù)
返回 	——
RT_EOK 	發(fā)送成功
-RT_EIO 	發(fā)送失敗
*/
rt_err_t rt_spi_send_then_send(struct rt_spi_device *device,
                               const void           *send_buf1,
                               rt_size_t             send_length1,
                               const void           *send_buf2,
                               rt_size_t             send_length2)

本函數(shù)適合向 SPI 設(shè)備中寫入一塊數(shù)據(jù),第一次先發(fā)送命令和地址等數(shù)據(jù),第二次再發(fā)送指定長(zhǎng)度的數(shù)據(jù)。

之所以分兩次發(fā)送而不是合并成一個(gè)數(shù)據(jù)塊發(fā)送,或調(diào)用兩次 rt_spi_send(),是因?yàn)樵诖蟛糠值臄?shù)據(jù)寫操作中,都需要先發(fā)命令和地址,長(zhǎng)度一般只有幾個(gè)字節(jié)。如果與后面的數(shù)據(jù)合并在一起發(fā)送,將需要進(jìn)行內(nèi)存空間申請(qǐng)和大量的數(shù)據(jù)搬運(yùn)。

而如果調(diào)用兩次 rt_spi_send(),那么在發(fā)送完命令和地址后,片選會(huì)被釋放,大部分 SPI 設(shè)備都依靠設(shè)置片選一次有效為命令的起始,所以片選在發(fā)送完命令或地址數(shù)據(jù)后被釋放,則此次操作被丟棄。

使用此函數(shù)等同于:

struct rt_spi_message msg1,msg2;

msg1.send_buf   = send_buf1;
msg1.recv_buf   = RT_NULL;
msg1.length     = send_length1;
msg1.cs_take    = 1;
msg1.cs_release = 0;
msg1.next       = &msg2;

msg2.send_buf   = send_buf2;
msg2.recv_buf   = RT_NULL;
msg2.length     = send_length2;
msg2.cs_take    = 0;
msg2.cs_release = 1;
msg2.next       = RT_NULL;


rt_spi_transfer_message(struct rt_spi_device *device, &msg1);

先發(fā)送后接收數(shù)據(jù):

/*
參數(shù) 	描述
device 			SPI 從設(shè)備句柄
send_buf 		發(fā)送數(shù)據(jù)緩沖區(qū)指針
send_length 	發(fā)送數(shù)據(jù)緩沖區(qū)數(shù)據(jù)字節(jié)數(shù)
recv_buf 		接收數(shù)據(jù)緩沖區(qū)指針
recv_length 	接收數(shù)據(jù)字節(jié)數(shù)
返回 	——
RT_EOK 		成功
-RT_EIO 	失敗
*/
rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device,
                               const void           *send_buf,
                               rt_size_t             send_length,
                               void                 *recv_buf,
                               rt_size_t             recv_length)

本函數(shù)適合從 SPI 從設(shè)備中讀取一塊數(shù)據(jù),第一次會(huì)先發(fā)送一些命令和地址數(shù)據(jù),然后再接收指定長(zhǎng)度的數(shù)據(jù)。

使用此函數(shù)等同于:

struct rt_spi_message msg1,msg2;

msg1.send_buf   = send_buf;
msg1.recv_buf   = RT_NULL;
msg1.length     = send_length;
msg1.cs_take    = 1;
msg1.cs_release = 0;
msg1.next       = &msg2;

msg2.send_buf   = RT_NULL;
msg2.recv_buf   = recv_buf;
msg2.length     = recv_length;
msg2.cs_take    = 0;
msg2.cs_release = 1;
msg2.next       = RT_NULL;


rt_spi_transfer_message(struct rt_spi_device *device, &msg1);

2.3.4 特殊場(chǎng)景

特殊場(chǎng)景部分暫時(shí)并不能體會(huì)其中的意義,所以這里直接套用官方的說(shuō)明,等以后再使用過(guò)程中如果確實(shí)遇到問(wèn)題,再來(lái)更新自己的心得體會(huì)。

在一些特殊的使用場(chǎng)景,某個(gè)設(shè)備希望獨(dú)占總線一段時(shí)間,且期間要保持片選一直有效,期間數(shù)據(jù)傳輸可能是間斷的,則可以按照如所示步驟使用相關(guān)接口。傳輸數(shù)據(jù)函數(shù)必須使用 rt_spi_transfer_message(),并且此函數(shù)每個(gè)待傳輸消息的片選控制域 cs_take 和 cs_release 都要設(shè)置為 0 值,因?yàn)槠x已經(jīng)使用了其他接口控制,不需要在數(shù)據(jù)傳輸?shù)臅r(shí)候控制。

獲取總線:

在多線程的情況下,同一個(gè) SPI 總線可能會(huì)在不同的線程中使用,為了防止 SPI 總線正在傳輸?shù)臄?shù)據(jù)丟失,從設(shè)備在開(kāi)始傳輸數(shù)據(jù)前需要先獲取 SPI 總線的使用權(quán),獲取成功才能夠使用總線傳輸數(shù)據(jù):

/*
參數(shù) 	描述
device 	SPI 設(shè)備句柄
返回 	——
RT_EOK 	成功
錯(cuò)誤碼 	失敗
*/
rt_err_t rt_spi_take_bus(struct rt_spi_device *device)

選中片選:

從設(shè)備獲取總線的使用權(quán)后,需要設(shè)置自己對(duì)應(yīng)的片選信號(hào)為有效:

/*
參數(shù) 	描述
device 	SPI 設(shè)備句柄
返回 	——
0 	成功
錯(cuò)誤碼 	失敗
*/
rt_err_t rt_spi_take(struct rt_spi_device *device);

增加一條消息:

使用 rt_spi_transfer_message() 傳輸消息時(shí),所有待傳輸?shù)南⒍际且詥蜗蜴湵淼男问竭B接起來(lái)的:

/*
參數(shù) 	描述
list 		待傳輸?shù)南㈡湵砉?jié)點(diǎn)
message 	新增消息指針
*/
rt_inline void rt_spi_message_append(struct rt_spi_message *list,
                                     struct rt_spi_message *message)

釋放片選:

傳輸完成釋放片選:

/*
device 	SPI 設(shè)備句柄
返回 	——
0 		成功
錯(cuò)誤碼 	失敗
*/
rt_err_t rt_spi_release(struct rt_spi_device *device)

釋放總線:

從設(shè)備不在使用 SPI 總線傳輸數(shù)據(jù),必須盡快釋放總線,這樣其他從設(shè)備才能使用 SPI 總線傳輸數(shù)據(jù):

/*
參數(shù) 	描述
device 	SPI 設(shè)備句柄
返回 	——
RT_EOK 	成功
*/
rt_err_t rt_spi_release_bus(struct rt_spi_device *device);

三、SPI 設(shè)備測(cè)試

與上一篇文章說(shuō)的 ADC 設(shè)備類似,我們可以通過(guò),但是也需要注意他的使用步驟:

3.1 SPI 設(shè)備使用步驟

board.h 文件中,我們可以查看其中關(guān)于 SPI的 使用步驟的注釋:

poYBAGLCmuaAB_wYAACYsFC9_Bc498.png

1、首先,在 RT-Thread Studio 工程中,打開(kāi) RT-Thread Settings,使能 SPI 驅(qū)動(dòng),如下圖所示:

pYYBAGLCmuaANmPxAABEWC_r-C4633.png

2、 宏定義 #define BSP_USING_SPI1(根據(jù)自己使用的設(shè)備硬件連接定義):

比如我使用的開(kāi)發(fā)板原理圖(忽略當(dāng)時(shí)的引腳標(biāo)號(hào),這里應(yīng)該是 SPI1,當(dāng)時(shí)寫標(biāo)號(hào)居然寫的是 SPI2 ):

poYBAGLCmueAah4EAAB9cnNa4ws828.png

查看對(duì)應(yīng)的手冊(cè)資料:

pYYBAGLCmueAW-CxAACHzAL-lVQ135.png

所以我們需要使能的是 SPI1:

poYBAGLCmueAM0i7AAAiiAlf7hs304.png

3、通過(guò)STM32CubeMX 配置 SPI :

和上一篇文章的 ADC 設(shè)備一樣進(jìn)行操作,如下圖:

pYYBAGLCmueAeyVBAADYIyGYG5Y639.png

到這一步,我們已經(jīng)能夠找到我們需要的 HAL_SPI_MspInit 文件了,通過(guò) spi.h 頭文件找到 spi.c 文件中的這個(gè)函數(shù):

poYBAGLCmuiAQSnOAACatt529PI091.png

4、 把HAL_SPI_MspInit 函數(shù)復(fù)制到 board.c 文件最后面,如下圖:

pYYBAGLCmuiAEDhIAACi56zeZhc351.png

5. 查看 stm32xxxx_hal_config.h 文件SPI 模塊是否使能:

在上一篇文章 ADC 步驟中已經(jīng)講解過(guò),使用 STM32CubeMX 設(shè)置以后,文件會(huì)自動(dòng)使能:

poYBAGLCmuiAKyBXAABZrNA-IYk507.png

到這里 SPI 的配置就算全部完成了,我們可以直接在應(yīng)用程序中,使用 SPI 設(shè)備操作函數(shù)實(shí)現(xiàn) SPI 的讀取。

3.2 測(cè)試

我們板載的是SPI設(shè)備是 W25Q128 ,我們測(cè)試一下 RT-Thread 的 SPI 設(shè)備模型是否能夠正常通行,這里只做簡(jiǎn)單的讀取 ID 的測(cè)試,官方的示例也是針對(duì) W25Qxx 系列的,但是我還是按照自己的理解來(lái)進(jìn)行。

第一步:檢查 spi 總線

我們根據(jù)上面的使用步驟,配置好 SPI ,我們應(yīng)用程序什么都不操作,看看初始化以后是否有 spi1 總線設(shè)備,如下圖:

pYYBAGLCmuiAGbRJAABLCU3AwJI846.png

第二步:掛載 spi 設(shè)備至 spi 總線

確認(rèn)了上電初始化以后 spi1 總線就已經(jīng)存在,我們就可以使用 SPI 的操作函數(shù)進(jìn)行,我們先把 spi 設(shè)備掛載上 spi 總線,然后進(jìn)行必要的配置,操作代碼如圖:

poYBAGLCmumANjmaAACe1gqastk553.png

到這一步,看可以看設(shè)備是否正常注冊(cè):

pYYBAGLCmumAAGyJAABly8tj97o828.png

第三部,通訊

好了,接下來(lái)就可以經(jīng)常正常的操作了,官方的示例是讀取 W25Qxx 的 ID,至于讀取 ID 操作流程,是需要查看 芯片手冊(cè)的,但是我還想想到曾經(jīng)在裸機(jī)使用過(guò)這個(gè) SPI Flash ,那么我可以直接參考以前的驅(qū)動(dòng)代碼,這樣就省去了再一次的手冊(cè)查看資料 = = !

上一下裸機(jī)的有關(guān)操作代碼:

//讀取芯片ID W25Q128的ID:0XEF17
u16 SPI_Flash_ReadID()
{
	u16 Temp = 0;	  
	W25Qxx_CS_ON;				    
	SPI1_ReadWriteByte(W25X_ManufactDeviceID);// 
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00); 	 // 		   
	Temp|=SPI1_ReadWriteByte(0xFF)<<8;  
	Temp|=SPI1_ReadWriteByte(0xFF);	 
	W25Qxx_CS_OFF;				    
	return Temp;	
}


//指令表
#define W25X_WriteEnable		0x06 
#define W25X_WriteDisable		0x04 
#define W25X_ReadStatusReg		0x05 
#define W25X_WriteStatusReg		0x01 
#define W25X_ReadData			0x03 
#define W25X_FastReadData		0x0B 
#define W25X_FastReadDual		0x3B 
#define W25X_PageProgram		0x02 
#define W25X_BlockErase			0xD8 
#define W25X_SectorErase		0x20 
#define W25X_ChipErase			0xC7 
#define W25X_PowerDown			0xB9 
#define W25X_ReleasePowerDown	0xAB 
#define W25X_DeviceID			0xAB 
#define W25X_ManufactDeviceID	0x90 
#define W25X_JedecDeviceID		0x9F 

上電的時(shí)候讀取一次設(shè)備的 ID,如果 讀取的 ID 正常,說(shuō)明設(shè)備正常,可以進(jìn)行接下來(lái)的通訊。

通過(guò)上面的操作我們可以看到這個(gè)操作流程,先發(fā)送一個(gè)字節(jié)消息(讀取指令), 然后再讀取 5個(gè)字節(jié)的消息,第 4個(gè)字節(jié)和第5個(gè)字節(jié)就是 SPI Flash 的設(shè)備ID (數(shù)據(jù)寬度 8 位),通過(guò)手冊(cè)我們可以可以看到說(shuō)明:

poYBAGLCmumAM6mbAAIMKDFtcWs687.png

搞清楚了流程,下面的讀取代碼,其實(shí)和官方基本一樣:

pYYBAGLCmuqAblL3AACkwvivNFY297.png

測(cè)試結(jié)果:

poYBAGLCmuqADHF0AABJHUm8VLk989.png

測(cè)試出來(lái)居然是反了,這個(gè)倒是無(wú)所謂,因?yàn)楹?jiǎn)單,反的原因這里不深究了。

當(dāng)然上面是用的自定義數(shù)據(jù)傳輸函數(shù)rt_spi_transfer_message實(shí)現(xiàn),我們也可以通過(guò)上面講到的先發(fā)送后接收數(shù)據(jù)函數(shù)rt_spi_send_then_recv實(shí)現(xiàn):

pYYBAGLCmuqAUzdTAAA0lcJSsuM375.png

可以看到使用這種專有函數(shù),程序會(huì)更加簡(jiǎn)單,但是我更加建議使用自定義,因?yàn)榭梢詽M足不同需求。

結(jié)語(yǔ)

本文我們學(xué)習(xí)了 RT-Thread 中 SPI 設(shè)備的使用方法,最終也通過(guò)簡(jiǎn)單的測(cè)試成功操作了 SPI 設(shè)備。

但是我們并沒(méi)有進(jìn)行正真的數(shù)據(jù)讀寫,在實(shí)際應(yīng)用中,我們需要用到不同的 SPI 設(shè)備,就算是 SPI Flash 這一種設(shè)備,都有不同廠家不同型號(hào)的,難免有不同之處。

RT-Thread 有一個(gè)很大的特點(diǎn)在于他的生態(tài)比一般的 RTOS 完善,我們?cè)趯?shí)際應(yīng)用中,有許許多多現(xiàn)成的官方或者很多開(kāi)發(fā)者提供的組件或者軟件包,我們可以直接導(dǎo)入工程進(jìn)行使用。

比如就本文我們學(xué)習(xí)的 SPI 設(shè)備,我們就可以使用官方標(biāo)準(zhǔn)的組件 — SFUD組件。

對(duì)于RT-Thread 設(shè)備模型篇的內(nèi)容,我也就更新到這篇文章,接下來(lái)就要開(kāi)始學(xué)習(xí)使用 RT-Thread 的組件和軟件包。

希望大家多多支持!本文就到這里,謝謝!

審核編輯:符乾江

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 操作系統(tǒng)
    +關(guān)注

    關(guān)注

    37

    文章

    7329

    瀏覽量

    128651
  • RT-Thread
    +關(guān)注

    關(guān)注

    32

    文章

    1542

    瀏覽量

    44294
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    RT-Thread記錄(十、全面認(rèn)識(shí) I/O 設(shè)備模型

    學(xué)完 RT-Thread 內(nèi)核,從本文開(kāi)始熟悉了解 RT-Thread I/O 設(shè)備管理相關(guān)知識(shí)。
    的頭像 發(fā)表于 06-30 10:38 ?5270次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十、全面認(rèn)識(shí) <b class='flag-5'>I</b>/<b class='flag-5'>O</b> <b class='flag-5'>設(shè)備</b><b class='flag-5'>模型</b>)

    RT-Thread記錄(十一、UART設(shè)備—源碼解析)

    一文帶你深入理解 RT-Thread I/O 設(shè)備模型 — UART 設(shè)備源碼分析。
    的頭像 發(fā)表于 07-01 11:24 ?6750次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十一、UART<b class='flag-5'>設(shè)備</b>—源碼解析)

    RT-Thread記錄(十四、I/O 設(shè)備模型ADC設(shè)備

    我曾經(jīng)考慮過(guò)把 RT-Thread 常用的設(shè)備都寫完,其實(shí)通過(guò)前面的《全面認(rèn)識(shí) RT-Thread I/O
    的頭像 發(fā)表于 07-04 12:28 ?5501次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十四、<b class='flag-5'>I</b>/<b class='flag-5'>O</b> <b class='flag-5'>設(shè)備</b><b class='flag-5'>模型</b><b class='flag-5'>之</b>ADC<b class='flag-5'>設(shè)備</b>)

    RT-Thread記錄(十二、UART設(shè)備—使用測(cè)試)

    從 UART 設(shè)備開(kāi)始學(xué)會(huì)使用 RT-Thread I/O 設(shè)備模型
    的頭像 發(fā)表于 07-02 12:42 ?6372次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十二、UART<b class='flag-5'>設(shè)備</b>—使用測(cè)試)

    RT-Thread記錄(十三、I/O 設(shè)備模型PIN設(shè)備

    講完UART設(shè)備之后,我們已經(jīng)熟悉RT-Thread I/O 設(shè)備模型了,回頭看看基本的 PIN
    的頭像 發(fā)表于 07-03 11:28 ?6234次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>記錄</b>(十三、<b class='flag-5'>I</b>/<b class='flag-5'>O</b> <b class='flag-5'>設(shè)備</b><b class='flag-5'>模型</b><b class='flag-5'>之</b>PIN<b class='flag-5'>設(shè)備</b>)

    基于RT-ThreadSPI通訊

    驅(qū)動(dòng)層的驅(qū)動(dòng)。(rt-thread設(shè)備 I/O 模型設(shè)備管理層、
    的頭像 發(fā)表于 08-22 09:28 ?2612次閱讀

    RT-Thread 的 IO 設(shè)備模型框架是由哪些部分組成的呢

    RT-ThreadI/O 設(shè)備模型框架是由哪些部分組成的呢?接下來(lái)由小編給大家詳細(xì)介紹一下。1、R
    發(fā)表于 03-11 18:17

    【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集

    RT-Thread記錄(十二、UART設(shè)備—使用測(cè)試)RT-Thread記錄(十三、I/
    發(fā)表于 07-26 14:56

    RT-Thread設(shè)備模型框架及創(chuàng)建注冊(cè)設(shè)備的實(shí)現(xiàn)

    RT-Thread設(shè)備模型框架及創(chuàng)建注冊(cè)設(shè)備的實(shí)現(xiàn)方式介紹如下:
    的頭像 發(fā)表于 05-28 10:38 ?2613次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>設(shè)備</b><b class='flag-5'>模型</b>框架及創(chuàng)建注冊(cè)<b class='flag-5'>設(shè)備</b>的實(shí)現(xiàn)

    RT-Thread文檔_I/O 設(shè)備模型

    RT-Thread文檔_I/O 設(shè)備模型
    發(fā)表于 02-22 18:31 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>I</b>/<b class='flag-5'>O</b> <b class='flag-5'>設(shè)備</b><b class='flag-5'>模型</b>

    RT-Thread文檔_I2C 總線設(shè)備

    RT-Thread文檔_I2C 總線設(shè)備
    發(fā)表于 02-22 18:35 ?0次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>I</b>2C 總線<b class='flag-5'>設(shè)備</b>

    RT-Thread文檔_PWM 設(shè)備

    RT-Thread文檔_PWM 設(shè)備
    發(fā)表于 02-22 18:35 ?2次下載
    <b class='flag-5'>RT-Thread</b>文檔_PWM <b class='flag-5'>設(shè)備</b>

    RT-Thread文檔_SPI 設(shè)備

    RT-Thread文檔_SPI 設(shè)備
    發(fā)表于 02-22 18:36 ?3次下載
    <b class='flag-5'>RT-Thread</b>文檔_<b class='flag-5'>SPI</b> <b class='flag-5'>設(shè)備</b>

    RT-Thread文檔_Pulse Encoder 設(shè)備

    RT-Thread文檔_Pulse Encoder 設(shè)備
    發(fā)表于 02-22 18:39 ?1次下載
    <b class='flag-5'>RT-Thread</b>文檔_Pulse Encoder <b class='flag-5'>設(shè)備</b>

    RT-ThreadI/O設(shè)備模型與分類

    RT- ThreadI/O設(shè)備模型(簡(jiǎn)稱“設(shè)備
    的頭像 發(fā)表于 10-11 17:12 ?1393次閱讀
    <b class='flag-5'>RT-Thread</b>的<b class='flag-5'>I</b>/<b class='flag-5'>O</b><b class='flag-5'>設(shè)備</b><b class='flag-5'>模型</b>與分類