最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言軟件spi虛擬總線中間層設計詳解

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

簡介

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

SPI-協(xié)議

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

接線方式

主機從機
CLKCLK
MISOMISO
MOSIMOSI
CS/NSSCS/NSS

主機從機一 一對應相接。

總線

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

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

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

工作本質

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

4種工作模式

時鐘極性

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

時鐘相位

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

工作模式

MODECPOLCPHA
000
101
210
311

時序圖:

h2>虛擬總線(中間層)設計

首先 SPI 總線的CLK、MISO、 MOSI這3條線是不會變動的,所以我們可以把這部分單獨設計為spi-bus,SPI總線需要知道當前有哪個設備擁有SPI總線的使用權,為了防止出現(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;				// 當前該總線的所有者
  mr_uint8_t lock;							// 互斥鎖
};

SPI設備唯一獨有的只有CS/NSS一條線,所以我們把這部分定義為spi-device。SPI設備還需要知道自己歸屬于哪條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;				// 該設備歸屬的總線
  void (*set_cs)(mr_uint8_t level);			// 操作 CS/NSS 的函數(shù)指針
};

當創(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;
}

那么由于是虛擬總線設計,當我們要開始傳輸前需要先去獲取總線。

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;
}

當我們使用完畢后需要釋放總線

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;
}

到此其實虛擬總線已經設計完畢,設備需要使用僅需通過掛載 、獲取釋放 三步操作即可,其余操作交由中間層處理。 為調用接口的統(tǒng)一,設計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ù)據地址
  mr_size_t send_size;							// 發(fā)送數(shù)據個數(shù)
  mr_uint8_t *recv_buffer;						// 接收數(shù)據地址
  mr_size_t recv_size;							//接收數(shù)據個數(shù)
};

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

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 設備 */
struct mr_soft_spi spi_device;
/* 適配 spi 設備接口 */
void set_cs(mr_uint8_t level)
{
  GPIO_WriteBit(GPIOA,GPIO_Pin_3,level);
}
/* 配置 spi 設備 */
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ù)據 */
    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 設備到 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ù)據地址
    spi_msg.send_size = 10;         //發(fā)送數(shù)據數(shù)量
    spi_msg.recv_buffer = MR_NULL;  //讀取數(shù)據地址
    spi_msg.recv_size = 0;          //讀取數(shù)據數(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

請仔細閱讀README.md ?。。。。?/p>

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

相關文章

  • C語言實現(xiàn)支持動態(tài)拓展和銷毀的線程池

    C語言實現(xiàn)支持動態(tài)拓展和銷毀的線程池

    這篇文章主要為大家介紹了C語言實現(xiàn)支持動態(tài)拓展和銷毀的線程池,感興趣的小伙伴們可以參考一下
    2016-01-01
  • C++17中的std::optional的具體使用

    C++17中的std::optional的具體使用

    這篇文章主要介紹了C++17中的std::optional的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • Qt實現(xiàn)TCP網絡編程

    Qt實現(xiàn)TCP網絡編程

    這篇文章主要為大家詳細介紹了Qt實現(xiàn)TCP網絡編程,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C++11并發(fā)編程:多線程std::thread

    C++11并發(fā)編程:多線程std::thread

    今天小編就為大家分享一篇關于C++11并發(fā)編程:多線程std::thread,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 詳解C++之類和對象(1)

    詳解C++之類和對象(1)

    類是創(chuàng)建對象的模板,一個類可以創(chuàng)建多個對象,每個對象都是類類型的一個變量;創(chuàng)建對象的過程也叫類的實例化。每個對象都是類的一個具體實例(Instance),擁有類的成員變量和成員函數(shù)
    2021-11-11
  • C語言實現(xiàn)三子棋實例代碼

    C語言實現(xiàn)三子棋實例代碼

    大家好,本篇文章主要講的是C語言實現(xiàn)三子棋實例代碼,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • C語言實現(xiàn)職工工資管理系統(tǒng)的示例代碼

    C語言實現(xiàn)職工工資管理系統(tǒng)的示例代碼

    這篇文章主要為大家詳細介紹了C語言如何實現(xiàn)職工工資管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C++線程間的互斥和通信場景分析

    C++線程間的互斥和通信場景分析

    很多朋友對C++線程間的互斥和通信知識掌握不是多牢靠,今天小編通過模擬車站賣票應用場景給大家詳細解析C++線程間的互斥和通信知識,感興趣的朋友跟隨小編一起看看吧
    2021-05-05
  • C++ 11和C++98相比有哪些新特性

    C++ 11和C++98相比有哪些新特性

    C++11標準提供了許多有用的新特性。這篇文章特別針對使C++11和C++98相比看上去像一門新語言的特性
    2017-03-03
  • C++命名空間5種常見用法實例解析

    C++命名空間5種常見用法實例解析

    這篇文章主要介紹了C++命名空間5種常見用法實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06

最新評論

万宁市| 富裕县| 柞水县| 东港市| 洪江市| 嘉鱼县| 湖南省| 潜江市| 周宁县| 连江县| 垦利县| 调兵山市| 社旗县| 当涂县| 马鞍山市| 肇东市| 东乌| 滁州市| 涞水县| 麦盖提县| 阿拉善右旗| 长宁县| 绩溪县| 肃北| 东乡| 峨眉山市| 济源市| 任丘市| 平昌县| 海伦市| 达州市| 中阳县| 铜梁县| 游戏| 尼勒克县| 泾阳县| 厦门市| 资中县| 白沙| 龙岩市| 玉溪市|