Microchip 25AA160/25LC160/25C160 EEPROM提供以SPI Slave方式供SPI Master裝置進行存取EEPROM
一般來說透過SPI介面上至少會提供:
1. CS (Chip Select)
2. SO (Data Output) --> 連接到MCU端的SPI 介面上的SI(資料輸入腳)
3. SI (Data Output) --> 連接到MCU端的SPI 介面上的SO(資料輸出腳)
4. SCK (Clock)
在Datasheet上一般都會提供一些用於讀寫的指令:
a. WRSR 0000 0001 (0x01)
b. WRITE 0000 0010 (0x02)
c. READ 0000 0011 (0x03)
d. WRDI 0000 0100 (0x04)
e. RDSR 0000 0101 (0x05)
f. WREN 0000 0110 (0x06)
讀取EEPROM裡頭的資料
1. CS 設於LOW狀態
2. 傳送8 bit的 READ 0000 0011 (0x03)指令
3. 傳送16 bit的EEPROM記憶區位址. 先送第 15 ~8 bits 再送 7~0bit.
4. 完成後再任意寫一個值到SSPBUF 中將資料強迫推出
5. 完成讀取後要再將CS設定於HIGH狀態
寫入資料到EEPROM中
1. CS 設於LOW狀態
2. 傳送8 bit的 WREN 0000 0110 (0x06))指令 //Write Enable命令要先傳送
3. CS設定於HIGH狀態
4. CS 設於LOW狀態
5. 傳送8 bit的 WRITE 0000 0010 (0x02)指令 //Write命令要先傳送
6. 傳送16 bit的EEPROM記憶區位址. 先送第 15 ~8 bits 再送 7~0bit.
7. 傳送所要寫的資料(一次只能寫一個 byte)
8. 寫完後要再 CS設定於HIGH狀態
範例如下:
CS = 0;
WriteSPI ( WREN ); //這裡是寫入 Write Enable命令
CS = 1;
CS = 0;
WriteSPI ( WRITE );
WriteSPI ( 0x00 ); //這裡是要寫在EEPROM上的位址 High
WriteSPI ( 0x01 ); //這裡是要寫在EEPROM上的位址 Low
WriteSPI ( 0x78 ); //這裡是資料
CS = 1;
檢查讀取暫存器狀態(Read Status Register RDSR)
1. CS 設於LOW狀態
2. 傳送8 bit的 RDSR 0000 0101 (0x05) //Read Status Register命令要先傳送
3. 檢查所讀回的資料中的第0個bit (WIP)是否還在寫入狀態. 0表示正在寫入 1.表示己經寫好
範例如下:
do{
CS = 0;
WriteSPI ( RDSR ); //Select Device
dummy=ReadSPI();
CS = 1;
}while(dummy&0x01);
經過上述的操作就可以達到對EEPROM以標準SPI Interface進行操作.
[註]感謝友人Resen Huang (首席) 的詳細解說.
2009年12月20日 星期日
MSB 與 LSB 的資料排列方式
假設資料結構子如下:
struct myData
{
unsigned addr_hi:2 // 佔 2 bits
unsigned instr :6 // 佔 6 bits
unsigned char instr_low // 佔 8 bits
}
資料內容為:
myData = 0x5A5B; // 0b0101101001011011
MSB表示方式
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|value| 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|--------> 1 byte <--------- |--------> 1 byte <--------- |
LSB表示方式(定義struct的順序要調整過)
struct myData
{
unsigned char instr_low // 佔 8 bits
unsigned instr :6 // 佔 6 bits
unsigned addr_hi:2 // 佔 2 bits
}
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|value| 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|--------> 1 byte <--------- |--------> 1 byte <--------- |
小結:
MSB的機器在訂struct時要從bit 7 ~ bit 0
LSB的機器在訂struct時要從bit 0 ~ bit 7
struct myData
{
unsigned addr_hi:2 // 佔 2 bits
unsigned instr :6 // 佔 6 bits
unsigned char instr_low // 佔 8 bits
}
資料內容為:
myData = 0x5A5B; // 0b0101101001011011
MSB表示方式
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|value| 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|--------> 1 byte <--------- |--------> 1 byte <--------- |
LSB表示方式(定義struct的順序要調整過)
struct myData
{
unsigned char instr_low // 佔 8 bits
unsigned instr :6 // 佔 6 bits
unsigned addr_hi:2 // 佔 2 bits
}
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|value| 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| bit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+-----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|--------> 1 byte <--------- |--------> 1 byte <--------- |
小結:
MSB的機器在訂struct時要從bit 7 ~ bit 0
LSB的機器在訂struct時要從bit 0 ~ bit 7
2009年10月28日 星期三
C# Bitwise -- How to get status of bit in a value
C# Bitwise
一般看到Bitwise的文章裡都會提到 “位元(Bit)操作運算方式”會有:
1. OR ( | )
2. AND ( & )
3. XOR ( ^ )
4. NOT ( ~ )
基本的原則在此就不多提了,有興趣的人可以直接上Google上查Bitwise operator
這篇的主題主要是要針對 “對某個位元進行操作”, 白話文的意思是就, 有時可能會遇到必須對1個byte裡頭的某個bit行操作. 用以判斷該bit的狀態為 0 或是 1.
由於過去大多數在寫應用程式時都用不太上這種位元操作,所以搞了好一段時間才明白,並做一個簡單的整理.
1 Byte = 8 Bits
所以在二進位裡看到的樣子會像是以下的結構
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
但由於我們在程式中取得到的值大多都是10進制 or 16進制. 所以在這個主題中我們先假設有一個值為 0x32 (16進制) 00110010 (2進制)
放到上述的表格裡頭之後會是以下的格式:
128 64 32 16 8 4 2 1
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 0 1 1 0 0 1 0
所以, 如果要將某個Bit的狀態取出的方法就會是:
int x = 0x32 & 128 (檢查bit 7的狀態)
if( x ==0 )
// 如果x 值為0時表示該bit 狀態為 0
else
// 如果 x 值不為0時表示該bit 狀態為 1
當然,也可以簡化一下寫成下面這個樣子
int x = 0x32 & 128
int y = (x==0)? 0 : 1;
這樣就可以輕鬆的取到期望的bit狀態.
最後, 如果要取其他bit怎麼辦? 答案是… 一個一個作唄.
int x = 0x32 & 1 //取bit 0的狀態
int x = 0x32 & 2 //取bit 1的狀態
int x = 0x32 & 4 //取bit 2的狀態
int x = 0x32 & 8 //取bit 3的狀態
int x = 0x32 & 16 //取bit 4的狀態
int x = 0x32 & 32 //取bit 5的狀態
int x = 0x32 & 64 //取bit 6的狀態
int x = 0x32 & 128 //取bit 7的狀態
這樣就可以把從 bit 0 ~ bit 7的狀態一一的取出進行判斷了.
一般看到Bitwise的文章裡都會提到 “位元(Bit)操作運算方式”會有:
1. OR ( | )
2. AND ( & )
3. XOR ( ^ )
4. NOT ( ~ )
基本的原則在此就不多提了,有興趣的人可以直接上Google上查Bitwise operator
這篇的主題主要是要針對 “對某個位元進行操作”, 白話文的意思是就, 有時可能會遇到必須對1個byte裡頭的某個bit行操作. 用以判斷該bit的狀態為 0 或是 1.
由於過去大多數在寫應用程式時都用不太上這種位元操作,所以搞了好一段時間才明白,並做一個簡單的整理.
1 Byte = 8 Bits
所以在二進位裡看到的樣子會像是以下的結構
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
但由於我們在程式中取得到的值大多都是10進制 or 16進制. 所以在這個主題中我們先假設有一個值為 0x32 (16進制) 00110010 (2進制)
放到上述的表格裡頭之後會是以下的格式:
128 64 32 16 8 4 2 1
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 0 1 1 0 0 1 0
所以, 如果要將某個Bit的狀態取出的方法就會是:
int x = 0x32 & 128 (檢查bit 7的狀態)
if( x ==0 )
// 如果x 值為0時表示該bit 狀態為 0
else
// 如果 x 值不為0時表示該bit 狀態為 1
當然,也可以簡化一下寫成下面這個樣子
int x = 0x32 & 128
int y = (x==0)? 0 : 1;
這樣就可以輕鬆的取到期望的bit狀態.
最後, 如果要取其他bit怎麼辦? 答案是… 一個一個作唄.
int x = 0x32 & 1 //取bit 0的狀態
int x = 0x32 & 2 //取bit 1的狀態
int x = 0x32 & 4 //取bit 2的狀態
int x = 0x32 & 8 //取bit 3的狀態
int x = 0x32 & 16 //取bit 4的狀態
int x = 0x32 & 32 //取bit 5的狀態
int x = 0x32 & 64 //取bit 6的狀態
int x = 0x32 & 128 //取bit 7的狀態
這樣就可以把從 bit 0 ~ bit 7的狀態一一的取出進行判斷了.
2009年10月17日 星期六
C# 找零錢範例程式
這程式內容沒有什麼特別的地方,但是如果用一堆if判斷式會寫到瘋掉.. :P
if (iReturn < 0)
{
MessageBox.Show("錢不夠! 再給我");
return;
}
if (iReturn == 0) //如果應找的錢等於0的話,就不需藥再繼續執行,直接結束
{
MessageBox.Show("不用找錢");
return;
}
int[] MoneyTypes = { 2000,1000,500,200, 100 ,50,10,5,1};
string strMessage = "找零數目:";
iReturn = Math.Abs(iReturn); //這是確保找錢金額為 正數..沒有負數
for (int i = 0; i < MoneyTypes.Length; i++)
{
int iNum = 0;
while ((iReturn - MoneyTypes[i]) >= 0)
{
iNum = iNum + 1;
iReturn = iReturn - MoneyTypes[i];
}
switch (i)
{
case 0: // 2000
strMessage = strMessage + " 2000元 x " + iNum + "張\t";
break;
case 1: //1000
strMessage = strMessage + " 1000元 x " + iNum + "張\t";
break;
case 2: // 500
strMessage = strMessage + " 500元 x " + iNum + " 張\t";
break;
case 3: // 200
strMessage = strMessage + " 200元 x " + iNum+ " 張\t";
break;
case 4: // 100
strMessage = strMessage + " 100元 x " + iNum+ " 張\t";
break;
case 5: // 50
strMessage = strMessage + " 50元 x " + iNum + " 張\t";
break;
case 6: // 10
strMessage = strMessage + " 10元 x " + iNum+ " 張\t";
break;
case 7: // 5
strMessage = strMessage + " 5元 x " + iNum+ " 張\t";
break;
case 8: // 1
strMessage = strMessage + " 1元 x " + iNum+ " 張\t";
break;
}
}
strMessage 就是找錢出來的結果.
if (iReturn < 0)
{
MessageBox.Show("錢不夠! 再給我");
return;
}
if (iReturn == 0) //如果應找的錢等於0的話,就不需藥再繼續執行,直接結束
{
MessageBox.Show("不用找錢");
return;
}
int[] MoneyTypes = { 2000,1000,500,200, 100 ,50,10,5,1};
string strMessage = "找零數目:";
iReturn = Math.Abs(iReturn); //這是確保找錢金額為 正數..沒有負數
for (int i = 0; i < MoneyTypes.Length; i++)
{
int iNum = 0;
while ((iReturn - MoneyTypes[i]) >= 0)
{
iNum = iNum + 1;
iReturn = iReturn - MoneyTypes[i];
}
switch (i)
{
case 0: // 2000
strMessage = strMessage + " 2000元 x " + iNum + "張\t";
break;
case 1: //1000
strMessage = strMessage + " 1000元 x " + iNum + "張\t";
break;
case 2: // 500
strMessage = strMessage + " 500元 x " + iNum + " 張\t";
break;
case 3: // 200
strMessage = strMessage + " 200元 x " + iNum+ " 張\t";
break;
case 4: // 100
strMessage = strMessage + " 100元 x " + iNum+ " 張\t";
break;
case 5: // 50
strMessage = strMessage + " 50元 x " + iNum + " 張\t";
break;
case 6: // 10
strMessage = strMessage + " 10元 x " + iNum+ " 張\t";
break;
case 7: // 5
strMessage = strMessage + " 5元 x " + iNum+ " 張\t";
break;
case 8: // 1
strMessage = strMessage + " 1元 x " + iNum+ " 張\t";
break;
}
}
strMessage 就是找錢出來的結果.
C# --Experience Timer Control Logic
最近正在進行一個RS-485通信資料的專案, 其中用到了常用到的Timer Control
原來的流程是這樣的..
step 1. 開啟Timer --> Timer1.Enabled = true
step 2. 等待Timer1_Tick事件觸發
step 3. 進入到Timer1_Tick事件後,由於不確定在這個Timer1_Tick事件中程式要執行多久才能將所有工作執行完畢. 所以在進入 Timer1_Tick事件後的第一件事就是將 Timer1.Enable 設定成false. 這樣理論上就能確保在Timer1_Tick 事件程序中的工作能完整的被執行完後才離開.
step 4. 當在Timer1_Tick事件程序中的工作都完成後便再將Timer1.Enable 設定成true. 這是為了讓Timer繼續的去執行. 之後程式就等待下一個Timer1_Tick事件被觸發.
當然,為了能夠提供使用者能自己控制這個 Timer1是否要開始計時, 所以在UI上得要提供一個Button來讓使用者操作Timer的Enable 為 true 或是false.
簡單的做法便是在Button_Click事件程序中寫一行:
Timer1.Enabled = !Timer1.Enabled.
經過以上的邏輯之後會發現好像就如同這麼簡單的方式可以被完成...
但, 是不幸的是... 上述的邏輯是不能被正常的完成上述所期望的工作.
經過執行的結果會發現.. 這個程式的Timer能被Enable 但無法被Disable.
原因是因為, Windows的程式是事件觸發.基本上,程式是一直處在一個等待的狀態. 而這個狀態除了是等待Timer1_Tick事件觸發之外,也等待使用者操作Button.. 等待Button_Click事件觸發.
在這個例子裡頭,原先所假定的Timer的Enable 或是 Disable 是透過Button_Click裡的 Timer1.Enabled = !Timer1.Enabled.
但是, 當使用者按下Button的同時, 程式有可能正在執行Timer1_Tick程序中的程式. 在這個Timer1_Tick程序中的最後一個步驟是... Timer1.Enabled = true;
所以會發生:
1.當使用者按下Button後, Button_Click程序中執行了Timer1.Enabled = !Timer1.Enabled (這裡先假定原先Timer1.Enabled = true 而在經過 !Timer1.Enable 則 Timer1.Enabled = false) 這表示.. 當使用者按下後Timer會被停止.
2. 在此同時,Timer1_Click事件被觸發了.. 第一個步驟是Timer1.Enabled = false (停止Timer)
3. 執行Timer1_Tick中所有的程式.
4.執行到最後一個步驟時.. Timer1.Enabled 要被設定成為true. (可是.. 之前我們明明己經按下Button讓Timer設定為停止的狀態) 這會兒又被設定為true. 結果就是真的執行的沒完沒了的.. 程式打死都停不下來.
要解決這個問題需要做些邏輯上的調整:
step 1. 開啟Timer --> Timer1.Enabled = true
step 2. 等待Timer1_Tick事件觸發
step 3. 進入到Timer1_Tick事件後,由於不確定在這個Timer1_Tick事件中程式要執行多久才能將所有工作執行完畢. 所以在進入 Timer1_Tick事件後的第一件事就是將 Timer1.Interval 設定成一個超長的時間. 這樣理論上就能確保在Timer1_Tick 事件程序中的工作能完整的被執行完後才會觸發下一個Timer_Tick事件.
step 4. 當在Timer1_Tick事件程序中的工作都完成後便再將Timer1.Interval 設定成一個超長的時間設定為"一個原先正常的時間" e.g 1000. 這是為了讓Timer繼續的去執行. 之後程式就等待下一個Timer1_Tick事件被觸發.
經過上述的邏輯調整後就能維持原來在Button_Click中的Timer.Enable=!Timer1.Enabled 的Timer啟停控制的機制.
這算是... 邏輯上的"誤區"吧...
原來的流程是這樣的..
step 1. 開啟Timer --> Timer1.Enabled = true
step 2. 等待Timer1_Tick事件觸發
step 3. 進入到Timer1_Tick事件後,由於不確定在這個Timer1_Tick事件中程式要執行多久才能將所有工作執行完畢. 所以在進入 Timer1_Tick事件後的第一件事就是將 Timer1.Enable 設定成false. 這樣理論上就能確保在Timer1_Tick 事件程序中的工作能完整的被執行完後才離開.
step 4. 當在Timer1_Tick事件程序中的工作都完成後便再將Timer1.Enable 設定成true. 這是為了讓Timer繼續的去執行. 之後程式就等待下一個Timer1_Tick事件被觸發.
當然,為了能夠提供使用者能自己控制這個 Timer1是否要開始計時, 所以在UI上得要提供一個Button來讓使用者操作Timer的Enable 為 true 或是false.
簡單的做法便是在Button_Click事件程序中寫一行:
Timer1.Enabled = !Timer1.Enabled.
經過以上的邏輯之後會發現好像就如同這麼簡單的方式可以被完成...
但, 是不幸的是... 上述的邏輯是不能被正常的完成上述所期望的工作.
經過執行的結果會發現.. 這個程式的Timer能被Enable 但無法被Disable.
原因是因為, Windows的程式是事件觸發.基本上,程式是一直處在一個等待的狀態. 而這個狀態除了是等待Timer1_Tick事件觸發之外,也等待使用者操作Button.. 等待Button_Click事件觸發.
在這個例子裡頭,原先所假定的Timer的Enable 或是 Disable 是透過Button_Click裡的 Timer1.Enabled = !Timer1.Enabled.
但是, 當使用者按下Button的同時, 程式有可能正在執行Timer1_Tick程序中的程式. 在這個Timer1_Tick程序中的最後一個步驟是... Timer1.Enabled = true;
所以會發生:
1.當使用者按下Button後, Button_Click程序中執行了Timer1.Enabled = !Timer1.Enabled (這裡先假定原先Timer1.Enabled = true 而在經過 !Timer1.Enable 則 Timer1.Enabled = false) 這表示.. 當使用者按下後Timer會被停止.
2. 在此同時,Timer1_Click事件被觸發了.. 第一個步驟是Timer1.Enabled = false (停止Timer)
3. 執行Timer1_Tick中所有的程式.
4.執行到最後一個步驟時.. Timer1.Enabled 要被設定成為true. (可是.. 之前我們明明己經按下Button讓Timer設定為停止的狀態) 這會兒又被設定為true. 結果就是真的執行的沒完沒了的.. 程式打死都停不下來.
要解決這個問題需要做些邏輯上的調整:
step 1. 開啟Timer --> Timer1.Enabled = true
step 2. 等待Timer1_Tick事件觸發
step 3. 進入到Timer1_Tick事件後,由於不確定在這個Timer1_Tick事件中程式要執行多久才能將所有工作執行完畢. 所以在進入 Timer1_Tick事件後的第一件事就是將 Timer1.Interval 設定成一個超長的時間. 這樣理論上就能確保在Timer1_Tick 事件程序中的工作能完整的被執行完後才會觸發下一個Timer_Tick事件.
step 4. 當在Timer1_Tick事件程序中的工作都完成後便再將Timer1.Interval 設定成一個超長的時間設定為"一個原先正常的時間" e.g 1000. 這是為了讓Timer繼續的去執行. 之後程式就等待下一個Timer1_Tick事件被觸發.
經過上述的邏輯調整後就能維持原來在Button_Click中的Timer.Enable=!Timer1.Enabled 的Timer啟停控制的機制.
這算是... 邏輯上的"誤區"吧...
訂閱:
文章 (Atom)