[英]i2c byte write function, how works this code? I can´t understand complety
有人能解釋一下這條線是如何工作的嗎
template <class T>
...const T& value)...
.
.
.
const uint8_t* p = (const uint8_t*)(const void*)&value;
在此代碼上(eeprom 的 i2c 字節寫入)
template <class T>
uint16_t writeObjectSimple(uint8_t i2cAddr, uint16_t addr, const T& value){
const uint8_t* p = (const uint8_t*)(const void*)&value;
uint16_t i;
for (i = 0; i < sizeof(value); i++){
Wire.beginTransmission(i2cAddr);
Wire.write((uint16_t)(addr >> 8)); // MSB
Wire.write((uint16_t)(addr & 0xFF));// LSB
Wire.write(*p++);
Wire.endTransmission();
addr++;
delay(5); //max time for writing in 24LC256
}
return i;
}
template <class T>
uint16_t readObjectSimple(uint8_t i2cAddr, uint16_t addr, T& value){
uint8_t* p = (uint8_t*)(void*)&value;
uint8_t objSize = sizeof(value);
uint16_t i;
for (i = 0; i < objSize; i++){
Wire.beginTransmission (i2cAddr);
Wire.write((uint16_t)(addr >> 8)); // MSB
Wire.write((uint16_t)(addr & 0xFF));// LSB
Wire.endTransmission();
Wire.requestFrom(i2cAddr, (uint8_t)1);
if(Wire.available()){
*p++ = Wire.read();
}
addr++;
}
return i;
}
我認為這些線條像指針一樣工作? 當我這樣做時,我無法理解代碼如何正確存儲每種類型的數據
struct data{
uint16_t yr;
uint8_t mont;
uint8_t dy;
uint8_t hr;
uint8_t mn;
uint8_t ss;
};
.
.
.
data myString;
writeObjectSimple(0x50,0,myString);
然后使用正確恢復值
data myStringRead;
readObjectSimple(0x50,0,myStringRead)
函數 i2c byte write 檢測每種數據類型之間的一些特殊字符以存儲在正確的位置?
謝謝
首先我必須聲明,這段代碼是由一個不完全熟悉 C++ 和 C 如何處理指針類型之間的差異的人編寫的。 我的印象是這個人有很強的 C 背景,只是試圖關閉 C++ 編譯器來拋出警告。
讓我們分解一下這行代碼的作用
const uint8_t* p = (const uint8_t*)(const void*)&value;
這里的目的是獲取一個任意類型的緩沖區——我們在這里甚至不知道,因為它是一個模板類型——並將其視為一個無符號 8 位整數的緩沖區。 這樣做的原因是,稍后該緩沖區的內容將通過線路逐位發送(這稱為“位碰撞”)。
在 C 中,這樣做的方法是編寫
const uint8_t* p = (const void*)&value;
這是有效的,因為在 C 中將void*
類型的指針分配給非 void 指針是完全有效的,反之亦然。 然而,C 語言設置的重要規則是——從技術上講——當你將void*
指針轉換為非 void 類型時, void*
指針必須是通過獲取對象的地址( &
運算符)獲得的相同的類型。 然而,在實踐中,實現允許將void*
類型指針轉換為與原始對象對齊兼容的任何類型,並且對於大多數 - 但不是全部! – 架構uint8_t
緩沖區可以與任何地址對齊。
然而,在 C++ 中,這種來回分配的void*
指針是不允許隱式的。 C++ 需要顯式轉換(這也是為什么您經常會看到 C++ 程序員用 C 代碼編寫類似struct foo *p = (struct foo*)malloc(…)
)。
所以你用 C++ 寫的是
const uint8_t* p = (const uint8_t*)&value;
這實際上有效並且不會發出任何警告。 然而,一些靜態的 linter工具會對它不屑一顧。 因此,第一次強制轉換(您必須從右到左閱讀強制轉換)首先通過強制轉換為void*
來丟棄原始類型以滿足 linter,然后第二次強制轉換為目標類型以滿足編譯器。
然而,正確的 C++ 習慣用法是使用reinterpret_cast
,大多數 linter 也將接受
const uint8_t* p = reinterpret_cast<const uint8_t*>(&value);
然而,所有這些轉換仍然調用實現定義的行為,當涉及到位碰撞時,您將受到字節序問題(最少)的影響。
Bit banging 本身通過一個一個地提取值的每一位並相應地對進出處理器端口的電線進行撓曲來工作。 這里使用的運算符是>>
來移動位和二進制&
來“選擇”特定位。
例如,當您看到類似的語句時
(v & (1<<x))
然后是檢查是否在變量v
設置了位號x
。 您還可以通過屏蔽(= 應用二元&
運算符 - 不要與產生指針的一元“地址”運算符混淆)來屏蔽變量中位的整個子集。
同樣,您可以使用|
運算符將幾個變量的位相互“疊加”。 結合移位運算符,您可以使用它來逐位構建變量的內容(位來自端口)。
目標設備是一個 I2C EEPROM,所以寫的一般形式是發送目標地址,然后是一些數據。 要從 EEPROM 讀取數據,您需要寫入源地址,然后切換到讀取模式以時鍾輸出數據。
首先,該行:
const uint8_t* p = (const uint8_t*)(const void*)&value;
只是采用模板化類型T
並丟棄其類型,並將其轉換為字節數組 ( uint8_t*
)。 該指針用於在包含value
的內存中一次推進一個字節。
在writeObjectSimple
方法中,它首先寫入 16 位目標地址(采用大端格式),然后寫入一個數據字節(其中p
是指向value
的數據指針):
Wire.write(*p++);
這將從value
寫入當前字節並將指針沿一個字節移動。 無論T
類型的字節有多少,它都會重復此操作。 寫入每個字節后,目標地址也遞增,並重復。
編碼時:
data myString;
writeObjectSimple(0x50,0,myString);
模板化的writeObjectSimple
將在data
類型上實例化,並將其內容(一次一個字節)從地址 0 開始寫入地址為 0x50 的設備。 它使用sizeof(data)
來知道要迭代多少字節。
讀取操作的工作方式非常相似,但寫入源地址,然后請求讀取(隱含在 I2C 地址的 LSB 中)並一次從設備讀取一個字節。
函數 i2c byte write 檢測每種數據類型之間的一些特殊字符以存儲在正確的位置?
並非如此,每筆交易只包含地址后跟數據。
[addr_hi] [addr_lo] [data]
已經解釋了所有這些,一次操作一個字節是實現這一目標的一種非常低效的方式。 該器件是 24LC256,該 24LC 系列 EEPROM 支持在單個 I2C 事務中按大小順序寫入(最多一頁)。 因此,您可以輕松地在一次傳輸中發送整個data
結構,並避免重新傳輸地址(每個數據字節 2 個字節)。 查看數據表了解完整的細節。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.