亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C語言軟件spi虛擬總線中間層設(shè)計(jì)詳解

 更新時間:2023年01月30日 11:27:10   作者:MacRsh  
這篇文章主要為大家介紹了C語言軟件spi虛擬總線中間層設(shè)計(jì)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

簡介

mr-soft-spi 模塊為 mr-library 項(xiàng)目下的可裁剪模塊,以C語言編寫,可快速移植到各種平臺(主要以嵌入式mcu為主)。 mr-soft-spi 模塊通過 io 模擬實(shí)現(xiàn) spi 協(xié)議。

SPI-協(xié)議

SPI 一般為一主多從設(shè)計(jì)。由4根線組成:CLK(時鐘)、MISO(主機(jī)輸入-從機(jī)輸出)、MOSI(主機(jī)輸出-從機(jī)輸入)、CS/NSS(片選)。

接線方式

主機(jī)從機(jī)
CLKCLK
MISOMISO
MOSIMOSI
CS/NSSCS/NSS

主機(jī)從機(jī)一 一對應(yīng)相接。

總線

SPI之所以被稱為總線,是其可以在一條總線上掛載多個設(shè)備,不同于IIC的地址碼設(shè)計(jì),設(shè)備通過CS/NSS切換,更加高效。

理論上SPI可以掛載無限多的設(shè)備,只要有足夠的CS/NSS。但在實(shí)際應(yīng)用中IO資源是極為稀缺的,所以利用SPI的特性,有了菊花鏈設(shè)計(jì)。

主機(jī)將數(shù)據(jù)發(fā)送給從機(jī)1,從機(jī)1將數(shù)據(jù)發(fā)送給從機(jī)2,從機(jī)2將數(shù)據(jù)發(fā)送給從機(jī)3。菊花鏈充分利用了SPI的工作本質(zhì),減少了IO的占用。

工作本質(zhì)

我們可以看到在MOSIMISO之間有一個移位寄存器,需要發(fā)送的數(shù)據(jù)從主機(jī)內(nèi)部被寫入到Tx buffer,然后移位寄存器移動一位,那么數(shù)據(jù)就被“擠”出到MOSI上,因?yàn)檎w結(jié)構(gòu)為環(huán)形,與此同時MISO上也被“擠”進(jìn)了一位數(shù)據(jù),存入Rx buffer,以此往復(fù),就完成了全雙工通信。

4種工作模式

時鐘極性

CPOL空閑時電平
0空閑時為低電平
1空閑時為高電平

時鐘相位

CPHA采集數(shù)據(jù)在第幾個邊緣
0第一個跳變沿采樣
1第二個跳變沿采樣

工作模式

MODECPOLCPHA
000
101
210
311

時序圖:

h2>虛擬總線(中間層)設(shè)計(jì)

首先 SPI 總線的CLK、MISO、 MOSI這3條線是不會變動的,所以我們可以把這部分單獨(dú)設(shè)計(jì)為spi-bus,SPI總線需要知道當(dāng)前有哪個設(shè)備擁有SPI總線的使用權(quán),為了防止出現(xiàn)搶占還需要配置一個互斥鎖。

struct mr_soft_spi_bus
{
  void (*set_clk)(mr_uint8_t level);		// 操作 CLK 的函數(shù)指針
  void (*set_mosi)(mr_uint8_t level);		// 操作 MOSI 的函數(shù)指針
  mr_uint8_t (*get_miso)(void);				// 讀取 MISO 的函數(shù)指針
  struct mr_soft_spi *owner;				// 當(dāng)前該總線的所有者
  mr_uint8_t lock;							// 互斥鎖
};

SPI設(shè)備唯一獨(dú)有的只有CS/NSS一條線,所以我們把這部分定義為spi-device。SPI設(shè)備還需要知道自己歸屬于哪條SPI總線。

struct mr_soft_spi
{
  mr_uint8_t mode         :2;				// SPI 工作模式
  mr_uint8_t cs_active    :1;				// CS/NSS 的有效電平(一般為低)
  struct mr_soft_spi_bus *bus;				// 該設(shè)備歸屬的總線
  void (*set_cs)(mr_uint8_t level);			// 操作 CS/NSS 的函數(shù)指針
};

當(dāng)創(chuàng)建了一條spi-bus,一個spi-device后我們需要一個掛載函數(shù),即將spi-device掛載到spi-bus

void mr_soft_spi_attach(struct mr_soft_spi *spi, struct mr_soft_spi_bus *spi_bus)
{
  spi->bus = spi_bus;
}

那么由于是虛擬總線設(shè)計(jì),當(dāng)我們要開始傳輸前需要先去獲取總線。

mr_err_t mr_soft_spi_bus_take(struct mr_soft_spi *spi)
{
  mr_uint8_t spi_bus_lock;
  /* check spi-bus owner */
  if(spi->bus->owner != spi)
  {
    /* check mutex lock */
    do{
      spi_bus_lock = spi->bus->lock;
    } while(spi_bus_lock != MR_UNLOCK);
    /* lock mutex lock */
    spi->bus->lock = MR_LOCK;
    /* stop spi cs */
    if(spi->bus->owner != MR_NULL)
      spi->bus->owner->set_cs(!spi->bus->owner->cs_active);
    /* exchange spi-bus owner */
    spi->bus->owner = spi;
    /* start spi cs */
    spi->set_cs(spi->cs_active);
  }
  else
  {
    /* lock mutex lock */
    spi->bus->lock = MR_LOCK;
    /* start spi cs */
    spi->set_cs(spi->cs_active);
  }
  return MR_EOK;
}

當(dāng)我們使用完畢后需要釋放總線

mr_err_t mr_soft_spi_bus_release(struct mr_soft_spi *spi)
{
  /* check spi-bus owner */
  if(spi->bus->owner == spi)
  {
    /* stop spi cs */
    spi->set_cs(!spi->cs_active);
    /* unlock mutex lock */
    spi->bus->lock = MR_UNLOCK;
    return MR_EOK;
  }
  return -MR_ERROR;
}

到此其實(shí)虛擬總線已經(jīng)設(shè)計(jì)完畢,設(shè)備需要使用僅需通過掛載 、獲取、釋放 三步操作即可,其余操作交由中間層處理。 為調(diào)用接口的統(tǒng)一,設(shè)計(jì)spi-msg

struct mr_soft_spi_msg
{
  mr_uint8_t read_write;                        // 讀寫模式:SPI_WR/ SPI_RD/ SPI_RDWR/ SPI_WR_THEN_RD
  mr_uint8_t *send_buffer;						// 發(fā)送數(shù)據(jù)地址
  mr_size_t send_size;							// 發(fā)送數(shù)據(jù)個數(shù)
  mr_uint8_t *recv_buffer;						// 接收數(shù)據(jù)地址
  mr_size_t recv_size;							//接收數(shù)據(jù)個數(shù)
};

然后通過transfer函數(shù)統(tǒng)一調(diào)用接口。

mr_err_t mr_soft_spi_transfer(struct mr_soft_spi *spi, struct mr_soft_spi_msg msg)
{
  mr_err_t ret;
  /* check function args */
  MR_DEBUG_ARGS_NULL(spi,-MR_EINVAL);
  MR_DEBUG_ARGS_IF(msg.read_write > SPI_WR_THEN_RD,-MR_EINVAL);
  /* take spi-bus */
  ret = mr_soft_spi_bus_take(spi);
  if(ret != MR_EOK)
    return ret;
  if(msg.read_write == SPI_WR || msg.recv_buffer == MR_NULL)
    msg.recv_size = 0;
  if(msg.read_write == SPI_RD || msg.send_buffer == MR_NULL)
    msg.send_size = 0;
  switch (msg.read_write) {
    case SPI_RD:
      /* receive */
      while (msg.recv_size) {
        *msg.recv_buffer = mr_soft_spi_bus_transmit(spi,0u);
        ++msg.recv_buffer;
        --msg.recv_size;
      }
      break;
    case SPI_WR:
      /* send */
      while (msg.send_size) {
        mr_soft_spi_bus_transmit(spi,*msg.send_buffer);
        ++msg.send_buffer;
        --msg.send_size;
      }
      break;
    case SPI_WR_THEN_RD:
      /* send */
      while (msg.send_size) {
        mr_soft_spi_bus_transmit(spi,*msg.send_buffer);
        ++msg.send_buffer;
        --msg.send_size;
      }
      /* receive */
      while (msg.recv_size) {
        *msg.recv_buffer = mr_soft_spi_bus_transmit(spi,0u);
        ++msg.recv_buffer;
        --msg.recv_size;
      }
      break;
    case SPI_RDWR:
      /* transmit */
      while (msg.send_size) {
        *msg.recv_buffer = mr_soft_spi_bus_transmit(spi,*msg.send_buffer);
        ++msg.send_buffer;
        ++msg.recv_buffer;
        --msg.send_size;
      }
      break;
  }
  /* release spi-bus */
  mr_soft_spi_bus_release(spi);
  return MR_EOK;
}

使用示例

/* -------------------- 配置 -------------------- */
/* 創(chuàng)建一條 spi 總線 */
struct mr_soft_spi_bus spi_bus;
/* 適配 spi 總線接口 */
void set_clk(mr_uint8_t level)
{
  GPIO_WriteBit(GPIOA,GPIO_Pin_0,level);
}
void set_mosi(mr_uint8_t level)
{
  GPIO_WriteBit(GPIOA,GPIO_Pin_1,level);
}
mr_uint8_t get_miso(void)
{
  return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
}
/* 配置 spi 總線 */
spi_bus.set_clk = set_clk;
spi_bus.set_mosi = set_mosi;
spi_bus.get_miso = get_miso;
spi_bus.lock = MR_UNLOCK;
spi_bus.owner = MR_NULL;
/* 創(chuàng)建一個 spi 設(shè)備 */
struct mr_soft_spi spi_device;
/* 適配 spi 設(shè)備接口 */
void set_cs(mr_uint8_t level)
{
  GPIO_WriteBit(GPIOA,GPIO_Pin_3,level);
}
/* 配置 spi 設(shè)備 */
spi_device.mode = SPI_MODE_0;       //SPI MODE 0
spi_device.cs_active = LEVEL_LOW;   //CS 引腳低電平有效
spi_device.set_cs = set_cs;
/* -------------------- 使用 -------------------- */
int main(void)
{
    /* 需要發(fā)送的數(shù)據(jù) */
    mr_uint8_t buffer[10]={0,1,2,3,4,5,6,7,8,9};
    /* 初始化 gpio */
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    /* 掛載 spi 設(shè)備到 spi 總線 */
    mr_soft_spi_attach(&spi_device,&spi_bus);
    /* 創(chuàng)建 spi 消息 */
    struct mr_soft_spi_msg spi_msg;
    spi_msg.send_buffer = buffer;   //發(fā)送數(shù)據(jù)地址
    spi_msg.send_size = 10;         //發(fā)送數(shù)據(jù)數(shù)量
    spi_msg.recv_buffer = MR_NULL;  //讀取數(shù)據(jù)地址
    spi_msg.recv_size = 0;          //讀取數(shù)據(jù)數(shù)量
    spi_msg.read_write = SPI_WR;    //只讀模式
    /* 發(fā)送消息 */
    mr_soft_spi_transfer(&spi_device,spi_msg);
}

剩余底層代碼位于開源代碼中,請下載開源代碼。

開源代碼倉庫鏈接 gitee.com/chen-fanyi/…

路徑:master/mr-library/ device / mr_soft_spi

請仔細(xì)閱讀README.md ?。。。。?/p>

以上就是C語言軟件spi虛擬總線中間層設(shè)計(jì)詳解的詳細(xì)內(nèi)容,更多關(guān)于C語言軟件spi虛擬總線中間層的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 圖解AVL樹數(shù)據(jù)結(jié)構(gòu)輸入與輸出及實(shí)現(xiàn)示例

    圖解AVL樹數(shù)據(jù)結(jié)構(gòu)輸入與輸出及實(shí)現(xiàn)示例

    這篇文章主要為大家介紹了C++圖解AVL樹數(shù)據(jù)結(jié)構(gòu)輸入與輸出操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • C語言實(shí)現(xiàn)磁盤映射

    C語言實(shí)現(xiàn)磁盤映射

    磁盤映射技術(shù)通過將文件映射到內(nèi)存中,提高了文件操作的效率,本文就來介紹一下C語言實(shí)現(xiàn)磁盤映射,感興趣的可以了解一下
    2024-09-09
  • C++中關(guān)于getchar()的使用方法

    C++中關(guān)于getchar()的使用方法

    這篇文章主要介紹了C++中關(guān)于getchar()的使用方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 解析C語言中結(jié)構(gòu)體struct的對齊問題

    解析C語言中結(jié)構(gòu)體struct的對齊問題

    這篇文章主要介紹了C語言中結(jié)構(gòu)體struct的對齊問題,作者深入到內(nèi)存分配方面來進(jìn)行解析,需要的朋友可以參考下
    2016-04-04
  • Qt實(shí)現(xiàn)TCP同步與異步讀寫消息的示例代碼

    Qt實(shí)現(xiàn)TCP同步與異步讀寫消息的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何在?Qt?中實(shí)現(xiàn)?TCP?客戶端和服務(wù)器的同步和異步讀寫消息,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-04-04
  • C語言樹與二叉樹基礎(chǔ)全刨析

    C語言樹與二叉樹基礎(chǔ)全刨析

    二叉樹可以簡單理解為對于一個節(jié)點(diǎn)來說,最多擁有一個上級節(jié)點(diǎn),同時最多具備左右兩個下級節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。本文將詳細(xì)介紹一下C中二叉樹與樹的概念和結(jié)構(gòu),需要的可以參考一下
    2022-04-04
  • C++實(shí)現(xiàn)掃雷小游戲

    C++實(shí)現(xiàn)掃雷小游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • 利用c++和easyx圖形庫做一個低配版掃雷游戲

    利用c++和easyx圖形庫做一個低配版掃雷游戲

    這篇文章主要介紹了用c++和easyx圖形庫做一個低配版掃雷游戲,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01
  • C++深入刨析優(yōu)先級隊(duì)列priority_queue的使用

    C++深入刨析優(yōu)先級隊(duì)列priority_queue的使用

    最近我學(xué)習(xí)了C++中的STL庫中的優(yōu)先級隊(duì)列(priority_queue)容器適配器,對于優(yōu)先級隊(duì)列,我們不僅要會使用常用的函數(shù)接口,我們還有明白這些接口在其底層是如何實(shí)現(xiàn)的
    2022-08-08
  • C++趣味算法之偵探推理

    C++趣味算法之偵探推理

    本文詳細(xì)講解了C++趣味算法之偵探推理,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12

最新評論