STC8H開發(十一): GPIO單線驅動多個DS18B20數字溫度計

来源:https://www.cnblogs.com/milton/archive/2022/06/04/16342989.html
-Advertisement-
Play Games

DS18B20 是一個單線通信的數字溫度計, 允許在一根匯流排上掛接多個 DS18B20 並分別通信, 在普通溫度下, 可以直接從數據口取電, 這時候只需要兩根連線. 供電電壓相容3.3V和5V, 溫度檢測範圍[-55°C, +125°C]攝氏度, 在 [-10°C, +85°C] 精確率可以達到 ±... ...


目錄

DS18B20

參數

  • 單線匯流排結構, 允許一根匯流排上掛接多個 DS18B20 並分別通信
  • 在普通溫度下, 可以直接從數據口取電, 這時候只需要兩根連線.
  • 供電電壓 [3.0V, 5.5V]
  • 溫度檢測範圍 [-55°C, +125°C]攝氏度, [-67°F, +257°F]華氏度
  • 精確率: 在 [-10°C, +85°C] 為 ±0.5°C

Pin腳

一般見到的都是3pin的To-92封裝, 和普通三極體一樣, 使平面朝向自己, Pin腳朝下, 從左往右依次為: GND, DQ, VDD

內部存儲結構

DS18B20內部有9位元組的暫存器和3個位元組的EEPROM存儲, EEPROM可以擦寫5萬次以上. 結構如下


測溫

DS18B20的核心功能就是數字化的溫度讀數, 可以設置為9, 10, 11, 12位解析度, 預設解析度是12位. 各解析度對應的讀數, 溫度解析度分別是0.5, 0.25, 0.125, 0.0625攝氏度.

在執行溫度轉換命令Convert T0x44後, 溫度會被轉換並存儲在一個2位元組的記憶體單元, 然後通過讀取命令Read Scratchpad0xBE讀出.

轉換時間

在溫度轉換命令Convert T0x44發起到採集完成需要的時間可能會長達750 ms. 實際使用中, 不同批次 DS18B20 的轉換時間差異也很大, 有的在200-300 ms, 有的接近 800 ms. 貌似越是最近製造的時間越短(可能是工藝改進了?).

如果沒有從VDD供電, DS18B20 的 DQ 必須在轉換過程中保持高電平以提供能量, 因此在這種場景下, 採集的過程中不允許進行其他活動.

讀數結構

這兩個位元組各個bit分別代表的數字含義如下, 高位元組的高5位僅用於表示溫度的正負, 正溫度是0, 負溫度是1, 後面11個bit表示的數字, 負值使用的是補碼, 讀數用 (0xFF - 讀數)

  • 正溫度時, 將16位整數乘以對應的溫度解析度
  • 負溫度時, 將16位整數取反加1後, 乘以對應的溫度解析度
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
S S S S S \(2^6\) \(2^5\) \(2^4\) \(2^3\) \(2^2\) \(2^1\) \(2^0\) \(2^-1\) \(2^-2\) \(2^-3\) \(2^-4\)
MSB LSB MSB LSB

讀數快查表

上電後的預設值為0x0550, 對應85°C, 如果一直讀出都是這個值, 需要檢查接線

TEMPERATURE DIGITAL OUTPUT (Binary) DIGITAL OUTPUT (Hex)
+125°C 0000 0111 1101 0000 07D0h
+85°C 0000 0101 0101 0000 0550h*
+25.0625°C 0000 0001 1001 0001 0191h
+10.125°C 0000 0000 1010 0010 00A2h
+0.5°C 0000 0000 0000 1000 0008h
0°C 0000 0000 0000 0000 0000h
-0.5°C 1111 1111 1111 1000 FFF8h
-10.125°C 1111 1111 0101 1110 FF5Eh
-25.0625°C 1111 1110 0110 1111 FF6Fh
-55°C 1111 1100 1001 0000 FC90h

ROM讀數

每個 DS18B20 包含一個唯一的只讀的64bit編碼, 其結構為

  1. 最初 8 bits 為固定的 0x28, 1-Wire family code
  2. 接下來的 48 bits 是唯一序列號
  3. 最後的 8 bits 是前面 56 bits 的 CRC 校驗碼.

這個 64-bit ROM 和 ROM 方法允許在單線(1-Wire)匯流排上運行多個 DS18B20, 使用單線匯流排需要使用下麵的方法之一發起:

  1. Read ROM,
  2. Match ROM,
  3. Search ROM,
  4. Skip ROM, or
  5. Alarm Search.

After a ROM function sequence has been successfully executed, the functions specific to the DS18B20 are accessible and the
bus master may then provide one of the six memory and control function commands.

CRC 計算

DS18B20 在讀取8位元組ROM和9位元組暫存器時, 最後一個位元組都是前面所有位元組的CRC校驗值. CRC值的比較與是否繼續操作完全由匯流排控制端決定, DS18B20 內部僅計算CRC, 並不會對CRC不匹配的情況進行處理, 需要匯流排控制端主動判斷.

計算CRC的等效多項式函數為(這是datasheet中的式子, 並非冪運算, 要結合後面的流程圖理解)

\(CRC = X^8 + X^5 + X^4 + 1\)

1-Wire匯流排的CRC計算由移位寄存器和異或門組成的多項式發生器來執行: 移位寄存器位初始化為0, 然後從第一個位元組的最低位開始, 一次移入一位, 根據計算結果決定是否與第4, 第5位作異或, 然後CRC也往右移, 最後移位寄存器的值就是CRC.

使用C語言表示的8位CRC計算函數為

uint8_t DS18B20_Crc(uint8_t *addr, uint8_t len)
{
    uint8_t crc = 0, inbyte, i, mix;

    while (len--)
    {
    	// inbyte 存儲當前參與計算的新位元組
        inbyte = *addr++;

        for (i = 8; i; i--) 
        {
        	// 將新位元組與CRC從低位到高位, 依次做異或運算, 每次運算完CRC右移一位
        	// 如果運算結果值為1, 則將CRC與 1000 1100 作異或
        	// 第3,4位代表流程圖中的異或運算, 第7位其實就是運算結果移入的1
            mix = (crc ^ inbyte) & 0x01;
            crc >>= 1;
            if (mix) 
            {
                crc ^= 0x8C;
            }
            inbyte >>= 1;
        }
    }
    return crc;
}

ROM Search 搜索演算法

當單線匯流排上掛接了多個DS18B20時, 匯流排控制端需要通過 ROM Search 命令來判斷匯流排上存在的設備以及獲取他們的8位元組唯一ROM.

ROM搜索演算法是重覆進行一個簡單的三步操作: 讀取一位, 讀取這位的補碼, 寫入這一位的目標值.

匯流排控制端在8位元組ROM的每一位上執行這個三步操作後, 就能知道一個 DS18B20 的 8位元組 ROM 值, 如果匯流排上有多個 DS18B20, 則需要重覆多次.

搜索示例

下麵的例子假設匯流排上有4個設備, 對應的ROM值分別為

  • ROM1 00110101...
  • ROM2 10101010...
  • ROM3 11110101...
  • ROM4 00010001...

搜索過程如下

  1. 單線匯流排控制端(以下簡稱總控)執行 RESET, 所有的 DS18B20設備(以下簡稱設備)響應這個RESET
  2. 總控執行 Search ROM 命令
  3. 總控讀取1個bit. 這時每個設備都會將自己的ROM的第一個bit放到匯流排上, ROM1 和 ROM4 會對匯流排寫0(拉低匯流排), 而 ROM2 和 ROM3 則會對匯流排寫1, 允許匯流排保持高電平. 這時候總控讀取的是0(低電平). 然後總控繼續讀下一個bit, 每個設備會將第一個bit的補碼放到匯流排上, 這時候 ROM1 和 ROM4 寫1, 而 ROM2 和 ROM3 寫0, 因此總控依然讀到一個0, 這時候總控會知道存在多個設備, 並且它們的ROM在這一位上的值不同. 從每次的兩步讀取中觀察到的值分別有以下的含義
  • 00 一定有多個設備, 且在這一位上值不同
  • 01 所有設備(不一定有多個), ROM在這一位上的值是0
  • 10 所有設備(不一定有多個), ROM在這一位上的值是1
  • 11 匯流排上沒有設備
  1. 總控寫入一個bit, 比如寫入0, 表示在後面的搜索中屏蔽 ROM2 和 ROM3, 僅留下 ROM1 和 ROM4
  2. 總控再執行兩次讀操作, 讀到的值為0,1, 這表示匯流排上所有設備在這一位上的值都是0
  3. 總控寫入一個bit, 因為值是確定的, 這次寫入的是0
  4. 總控再執行兩次讀操作, 讀到的值為0,0, 這表示匯流排上還有多個設備, 在這一位上的值不同
  5. 總控寫入一個bit, 這次寫入0, 這將屏蔽 ROM1, 僅留下 ROM4
  6. 總控重覆進行三步操作, 讀出 ROM4 剩餘的位, 完成第一次搜索
  7. 總控再次重覆之前的搜索直到第7位
  8. 總控寫入一個bit, 這次寫入1, 將屏蔽 ROM4, 僅保留 ROM1
  9. 總控通過重覆三步操作, 讀出 ROM1 剩餘的位
  10. 總控再次重覆之前的搜索直到第3位
  11. 總控寫入一個bit, 這次寫入1, 將屏蔽 ROM1 和 ROM4 僅保留 ROM2 和 ROM3
  12. 重覆之前的邏輯, 當所有00讀數都被處理, 說明設備的ROM已經全部被讀取.

總控通過單線匯流排讀取所有設備, 每個設備需要的時間為960 µs + (8 + 3 x 64) 61 µs = 13.16 ms, 識別速度為每秒鐘75個設備.

代碼邏輯

整體的邏輯是按一個固定的方向(先0後1)深度優先遍歷一個二叉樹.

數據結構

  • 預設一個8位元組數組 Buff 用於記錄路徑(即ROM的讀數)
  • 預設一個8位元組數組 Stack, 用於記錄每一位的值是否確定, 如果確定就是1, 未確定就是0.
  • 預設一個整數變數 Fork_Point 用於記錄每一輪搜索中得到的最深分叉點的位置, 下一次到這一位就用1進行分叉.

每一輪的遍歷邏輯

  1. 從低位開始, 每一位進行兩次讀, 得到這一位的值和補碼
  2. 對前面的結果進行判斷
    1. 如果為11, 說明沒有設備, 直接退出
    2. 如果為01, 說明這一位都是0, 寫入 Buff, 同時將 Stack 這一位設成 1 (已確認)
    3. 如果為10, 說明這一位都是1, 寫入 Buff, 同時將 Stack 這一位設成 1 (已確認)
    4. 如果為00, 說明這一位產生了分叉, 需要繼續判斷
  3. 對分叉的判斷
    1. 如果當前位置比已知的分叉點更淺, 說明還沒到該分叉的位置, 繼續設置成 Buff 中上一次使用的值, Stack不變
    2. 如果當前位置等於分叉點, 說明已經到了上次定好的分叉位置, 上次用0, 這次就用1進行分叉, 這次分叉完, 這一位就確認了, 將 Stack 這一位設成 1 (已確認)
    3. 如果當前位置比已知的分叉點位置還要深, 說明發現了新的分叉點(例如用1分叉後, 進入了新的子樹), 更新分叉點位置, 將 Stack 這一位設成 0 (未確認), 用預設的0繼續往下走
  4. 在這輪遍歷結束後, Buff 就得到了一個新的地址
  5. 在 Stack 上找到分叉點的位置, 將分叉點設置到最淺的一個0(未確定)的位置. 例如這次正好在分叉點使用1分叉, 當前點確認了, 而之後又全是確認的情況, 需要將分叉點往上移.

結束條件: 和深度遍歷一樣, 每一輪遍歷後分叉點可能會上下變化, 當分叉點的位置為0時, 說明遍歷結束

C語言代碼實現

uint8_t DS18B20_Buff[9];
uint8_t Search_Stack[8], Fork_Point;

uint8_t DS18B20_Detect(void)
{
    uint8_t len = 64, pos = 0;
    /* Reset line */
    DS18B20_Reset();
    /* Start searching */
    DS18B20_WriteByte(ONEWIRE_CMD_SEARCHROM);

    while (len--)
    {
        // Two reads
        __BIT pb = DS18B20_ReadBit();
        __BIT cb = DS18B20_ReadBit();
        if (pb && cb)
        {
            // no device
            return 0;
        }
        else if (pb)
        {
            // bit = 1
            DS18B20_Buff[pos / 8] |= 0x01 << (pos % 8);
            DS18B20_WriteBit(SET);
            // confirm: set this bit to 1
            Search_Stack[pos / 8] |= 0x01 << (pos % 8);
        }
        else if (cb)
        {
            // bit = 0
            DS18B20_Buff[pos / 8] &= ~(0x01 << (pos % 8));
            DS18B20_WriteBit(RESET);
            // confirm: set this bit to 1
            Search_Stack[pos / 8] |= 0x01 << (pos % 8);
        }
        else
        {
            // bit can be 0 or 1
            if (Fork_Point == 0xFF || pos > Fork_Point)
            {
                // new fork point, try 0
                DS18B20_Buff[pos / 8] &= ~(0x01 << (pos % 8));
                DS18B20_WriteBit(RESET);
                // unconfirm: set this bit to 0
                Search_Stack[pos / 8] &= ~(0x01 << (pos % 8));
                // record new fork point
                Fork_Point = pos;
            }
            else if (pos == Fork_Point)
            {
                // reach fork point, try 1
                DS18B20_Buff[pos / 8] |= 0x01 << (pos % 8);
                DS18B20_WriteBit(SET);
                // confirm: set this bit to 1
                Search_Stack[pos / 8] |= 0x01 << (pos % 8);
            }
            else // middle point, remain old value
            {
                DS18B20_WriteBit(DS18B20_Buff[pos / 8] >> (pos % 8) & 0x01);
            }
        }
        pos++;
    }
    // Reset fork point
    while (Fork_Point > 0 && Search_Stack[Fork_Point / 8] >> (Fork_Point % 8) & 0x01 == 0x01) Fork_Point--;
    return pos;
}

調用方法

Fork_Point = 0xFF;
while (Fork_Point && DS18B20_Detect())
{
    UART1_TxHex(Fork_Point);
    UART1_TxChar(' ');
    PrintBuff(0, 8);
    UART1_TxString("\r\n");
}

使用STC8H驅動DS18B20

接線

GND  -> GND
P35  -> DQ
3.3V -> VDD

代碼

代碼可以從GitHub或者Gitee下載

定義 IO

只需要一個Pin, 在STC8H中, 註意要將其設置為上拉, 否則讀出來的全是0

#define DS18B20_DQ           P35
#define DS18B20_DQ_PULLUP()  GPIO_SetPullUp(GPIO_Port_3, GPIO_Pin_5, HAL_State_ON)
#define DS18B20_DQ_INPUT()   GPIO_P3_SetMode(GPIO_Pin_5, GPIO_Mode_Input_HIP)
#define DS18B20_DQ_OUTPUT()  GPIO_P3_SetMode(GPIO_Pin_5, GPIO_Mode_InOut_OD)
#define DS18B20_DQ_LOW()     DS18B20_DQ=RESET
#define DS18B20_DQ_HIGH()    DS18B20_DQ=SET

IO 讀寫

讀一個bit和一個byte

__BIT DS18B20_ReadBit(void)
{
    __BIT b = RESET;

    /* Line low */
    DS18B20_DQ = RESET;
    DS18B20_DQ_OUTPUT();
    SYS_DelayUs(2);

    /* Release line */
    DS18B20_DQ_INPUT();
    SYS_DelayUs(10);

    /* Read line value */
    if (DS18B20_DQ) {
        /* Bit is HIGH */
        b = SET;
    }

    /* Wait 50us to complete 60us period */
    SYS_DelayUs(50);
    
    /* Return bit value */
    return b;
}

uint8_t DS18B20_ReadByte(void)
{
    uint8_t i = 8, byte = 0;
    while (i--) 
    {
        byte >>= 1;
        byte |= (DS18B20_ReadBit() << 7);
    }
    return byte;
}

寫一個bit和一個byte

void DS18B20_WriteBit(__BIT b)
{
    if (b)
    {
        /* Set line low */
        DS18B20_DQ = RESET;
        DS18B20_DQ_OUTPUT();
        SYS_DelayUs(10);

        /* Bit high */
        DS18B20_DQ_INPUT();
        
        /* Wait for 55 us and release the line */
        SYS_DelayUs(55);
        DS18B20_DQ_INPUT();
    } 
    else 
    {
        /* Set line low */
        DS18B20_DQ = RESET;
        DS18B20_DQ_OUTPUT();
        SYS_DelayUs(65);
        
        /* Bit high */
        DS18B20_DQ_INPUT();
        
        /* Wait for 5 us and release the line */
        SYS_DelayUs(5);
        DS18B20_DQ_INPUT();
    }
}

void DS18B20_WriteByte(uint8_t byte)
{
    uint8_t i = 8;
    /* Write 8 bits */
    while (i--) 
    {
        /* LSB bit is first */
        DS18B20_WriteBit(byte & 0x01);
        byte >>= 1;
    }
}

單個 DS18B20 的場景

初始化, 註意設置上拉, 以及輸入和輸出模式的切換

void DS18B20_Init(void)
{
    DS18B20_DQ_PULLUP();
    DS18B20_DQ_OUTPUT();
    DS18B20_DQ = SET;
    SYS_DelayUs(1000);
    DS18B20_DQ = RESET;
    SYS_DelayUs(1000);
    DS18B20_DQ = SET;
    SYS_DelayUs(2000);
}

讀取溫度, 這樣讀出的值並非溫度值, 需要根據上面的溫度轉換, 乘以對應的溫度單元值(預設為0.0625攝氏度)

// 發起轉換
DS18B20_StartAll();
// 讀取匯流排, 當轉換完成時會變為高電平
while (!DS18B20_AllDone())
{
    UART1_TxChar('.');
    SYS_Delay(1);
}

// 重置匯流排
DS18B20_Reset();
// 跳過ROM選擇
DS18B20_WriteByte(ONEWIRE_CMD_SKIPROM);
// 寫入讀取暫存器指令
DS18B20_WriteByte(ONEWIRE_CMD_RSCRATCHPAD);

// 讀出9個位元組的數據
for (i = 0; i < 9; i++) 
{
    /* Read byte by byte */
    data[i] = DS18B20_ReadByte();
}
// 溫度值位於第1和第2個位元組
temperature = data[1];
temperature = temperature << 8 | data[0];

讀取ROM

// 重置匯流排
DS18B20_Reset();
// 寫入讀取ROM指令, 註意這個命令不能用於連接多個設備的匯流排, 否則結果讀數是無意義的
DS18B20_WriteByte(ONEWIRE_CMD_READROM);
// 讀出數據
for (i = 0; i < 8; i++) 
{
    *buf++ = DS18B20_ReadByte();
}

多個 DS18B20 的場景

指定設備地址, 讀取溫度

// 重置匯流排
DS18B20_Reset();
// 根據地址選擇設備
DS18B20_Select(addr);
// 對選中的設備, 發起轉換
DS18B20_WriteByte(DS18B20_CMD_CONVERTTEMP);

// 等待轉換結束

// 重置匯流排
DS18B20_Reset();
// 根據地址選擇設備
DS18B20_Select(addr);
// 寫入讀取暫存器指令
DS18B20_WriteByte(ONEWIRE_CMD_RSCRATCHPAD);
// 讀取數據
for (i = 0; i < 9; i++) 
{
    *buf++ = DS18B20_ReadByte();
}

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 非常感謝小趙同學給我反饋的這個 Bug
  • <!-- JS代碼的書寫位置 1、可以將JS代碼書寫在script標簽中 2、可以將JS代碼寫在標簽的onclick屬性中,當我們點擊按鈕時,JS代碼才會執行 3、可以將JS代碼寫在超鏈接的href屬性中,這些點擊超鏈接時,會執行JS代碼 --> tips: 1、可以在超鏈接的href屬性中寫上空的 ...
  • 前言 在平常的後端項目開發中,狀態機模式的使用其實沒有大家想象中那麼常見,筆者之前由於不在電商領域工作,很少在業務代碼中用狀態機來管理各種狀態,一般都是手動get/set狀態值。去年筆者進入了電商領域從事後端開發。電商領域,狀態又多又複雜,如果仍然在業務代碼中東一塊西一塊維護狀態值,很容易陷入出了問 ...
  • 註解 前言 以前學習到「註解」的時候,沒有好好理解註解是如何工作的,只是知道註解可以實現一些功能,總而言之,就是懵懵懂懂。 不過,即使你不知道什麼是註解,但肯定接觸過註解,比如方法的重寫,在方法上面寫著 @Override,這個東西就是註解。 好了,下麵就開始回爐重造!打好基礎! 什麼是註解? 註解 ...
  • 1. Spring IOC源碼研究筆記(2)——ApplicationContext系列 1.1. 繼承關係 非web環境下,一般來說常用的就兩類ApplicationContext: 配置形式為XML的:ClassPathXmlApplicationContext、FileSystemXmlApp ...
  • C++異常處理(較淺) 基本概念 異常處理,是編程語言或電腦硬體里的一種機制,用於處理軟體或信息系統中出現異常的情況,保證程式運行的穩定性和健壯性,防止程式崩潰。但是過渡使用會影響程式運行的效率。 把可能出現的問題放到try里,進行監控 throw前面的語句如果滿足,就拋出**(下麵的語句就不執行 ...
  • 來源:my.oschina.net/xiaolyuh/blog/1615639 在日常開發中有很多地方都有類似扣減庫存的操作,比如電商系統中的商品庫存,抽獎系統中的獎品庫存等。 解決方案 使用mysql資料庫,使用一個欄位來存儲庫存,每次扣減庫存去更新這個欄位。 還是使用資料庫,但是將庫存分層多份存 ...
  • 初始Linux Linux可劃分為以下四部分: Linux內核 GNU工具 圖形化桌面環境 應用軟體 每一部分在Linux系統中各司其職,下圖是各部分對應關係: 1、Linux內核 Linux系統的核心是內核,內核控制著電腦系統上的所有硬體和軟體,在必要時分配硬體,並根據需要執行軟體。內核主要負責 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...