|
|
| Author |
Message |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 08 February 2006, 20:23 PM Post subject: How is a UnsignedInteger stored? |
|
|
I need to store some UnsignedIntegers(UI) in EEPROM and would like to know how ZBasic normally stores this two byte data type. My EEPROM is a serial FRAM device. I need to know if I should store the data with the lobyte at the lowest memory location followed by the hibyte or the other way around because I want to use something like:
| Code: |
Call BlockMove(2, MemAddress(data(7), MemAddress(UserFldEl))
|
to retrieve the UI and place it in UserFldEl. The data for UserFldEl is retrieved from the FRAM into data(7) and data(8). Should data(7) contain the low byte or the high byte? |
|
| Back to top |
|
 |
mikep
Joined: 24 Sep 2005
Posts: 765
Location: Austin, TX
|
|
Posted: 08 February 2006, 21:18 PM Post subject: |
|
|
Vic,
You are on the wrong track here. EEPROM memory is treated differently to RAM memory. Read sections 2.9 and 2.10 of the language reference manual. It is your decision whether to use Persistent memory (on chip EEPROM) or program memory (off chip EEPROM).
BlockMove only moves bytes from RAM to RAM as stated in the documentation: http://www.zbasic.net/doc/ZBasicSysLib/ZBasicSysLib26.html. |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 08 February 2006, 22:30 PM Post subject: |
|
|
The ZX is a "little endian" device. For those unfamiliar with the term, "little endian" implies that the less significant bytes are stored at lower numbered addresses, i.e. "little end" first. In contrast, "big endian" form has the more significant bytes stored at lower numbered addresses.
You'll have to write your own routines for writing data to and reading data from the external FRAM device. Consequently, you can adopt any byte order that you would like as long as you are consistent. From a practical perspective, it may be simpler to adopt the "little endian" form used by the ZX.
As Mike pointed out, BlockMove() copies from RAM to RAM. You can use GetPersistent() to copy from Persistent Memory to RAM and PutPersistent() to copy from RAM to Persistent Memory. GetEEPROM() and PutEEPROM() perform the same function between RAM and Program Memory. |
|
| Back to top |
|
 |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 09 February 2006, 2:44 AM Post subject: |
|
|
First, let me thank you both for your response to my question.
I believe I misled you both about this code
| Code: |
Call BlockMove(2, MemAddress(data(7), MemAddress(UserFldEl))
|
This was extracted from my FRAMRead subroutine. The information read from the FRAM in the routine is stored in the array data() and, in particular, the two bytes of data representing UserFldEl has been stored into data(7) and data( 8 ) and i wish to move those two bytes from data() to UserFldEl. I have a subroutine FRAMWrite that puts the bytes of UserFldEl sequentially into FRAM and I want to be sure that I am putting them in the proper order so that when I call FRAMRead and extract the bytes from the FRAM I can get them back into USerFLdEl in the proper order using the above BlockMove call. The array data() is defined as an array of bytes. So I guess I would put the high byte of UserFldEl into the FRAM at address x and the low byte into FRAM at address x+1. Then, later I can extract UserFldEl using my block move and have the stored value replaced properly in UserFldEl
Does this make any sense?
Vic |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 09 February 2006, 3:13 AM Post subject: |
|
|
| Quote: | | So I guess I would put the high byte of UserFldEl into the FRAM at address x and the low byte into FRAM at address x+1. |
That would be "big endian" byte order, backwards from the ZX's "little endian" byte order. However, as I pointed out earlier, the difference is moot provided your "get" and "put" routines operate in a consistent manner.
As usual, it is best to document details such as byte order in your code's comments just so you won't forget what's going on and why. |
|
| Back to top |
|
 |
mikep
Joined: 24 Sep 2005
Posts: 765
Location: Austin, TX
|
|
Posted: 09 February 2006, 3:15 AM Post subject: |
|
|
So your problem is that you have a data structure that you want to manipulate in memory and it should contain an unsigned integer at offset 7 in the array. The fact that the memory is written or read from FRAM, EEPROM, Floppy disk etc is actually not relevant to this problem.
And in fact you shouldn't have to care about byte ordering either providing you do it the same for writing and reading. The simplest solution in ZBasic is to use Aliases (http://www.zbasic.net/doc/ZBasicRef/ZBasicRef69.html): | Code: | Dim data(1 to 20) as Byte
Dim UserFldEl as UnsignedInteger alias data(7) |
BasicX does not have Aliases so you have to use the BlockMove idea. Aren't you glad you are using Zbasic now  |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 09 February 2006, 3:26 AM Post subject: |
|
|
| Quote: | | The simplest solution in ZBasic is to use Aliases... |
In order to use an Alias (or BlockMove() for that matter) the byte order must match the native byte order, i.e., little endian.
If you assemble the values byte-by-byte using the shift/add approach, the byte order of the data source is immaterial. This method (or something similar) must be used to attain code portability among platforms with different byte orders. For example, in network-oriented code written in C/C++ and similar languages macros are often employed that convert values from network byte order (big endian) to host byte order and back. On systems where the host byte order is the same as network byte order the macros do nothing. |
|
| Back to top |
|
 |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 09 February 2006, 12:11 PM Post subject: |
|
|
OK here's the that I am using. I have tried to figure out how to do the alias thing. The variables being written to FRAM are global:
| Code: |
Public Function WriteSystemData() as byte
'System data is maintained in at the top of the FRAM starting at the address 32700. This
'System data is:
'
'UserToFromFlag as Boolean (1) 32700
'UserHgtUnits as Boolean (1) 32701
'UserFldElUnits as Boolean (1) 32702
'UserAscRtUnits as Boolean (1) 32703
'UserSpdUnits as Byte (1) 32704
'UserSampleTime as Byte (1) 32705
'UserFldEl as UnsignedInteger (2) 32706 32707
'UserAscRate as UnsignedInteger (2) 32708 32709
'
'this Function returns 0 if successful else:
'-1 if invalid fram channel
'-2 if invalid sda pin number
'-3 if invalid scl pin number
'-4 if invalid bit rate
'-5 if fram device addressing problem
'-6 if write address problem
'-7 if error writing data
'
dim data(1 to 10) of byte
dim rvalue as integer
dim
data(1) = CByte(UserToFromFlag)
data(2) = CByte(UserHgtUnits)
data(3) = CByte(UserFldElUnits)
data(4) = CByte(UserAscRtUnits)
data(5) = UserSpdUnits
data(6) = UserSampleTime
data(7) = HiByte(UserFldEl)
data(8) = LoByte(UserFldEl)
data(9)= HiByte(UserAscRate)
data(10) = LoByte(UserAscRate)
rvalue = FRAMInit(HRDWARE, SDA, SCL, 0) 'use hardware I2C channel w/defaults
if rvalue < 0 then 'error initializing FRAM occured
WriteSystemData = rvalue
exit Function
End if
rvalue = FRAMWrite(HRDWARE, FRAMDEV, FRAMUSRADR, data(), SizeOf(data))
if rvalue < 0 then 'error initializing FRAM occured
select Case rvalue
case -1
rvalue = -5
case -2, -3
rvalue = -6
case -4
rvalue = -7
End select
WriteSystemData = rvalue
exit Function
End if
WriteSystemData = rvalue
End Function 'WriteSystemData
|
| Code: |
Public Function ReadSystemData() as integer
'System data is maintained in at the top of the FRAM starting at the address 32000. This
'System data is:
'
'UserToFromFlag as Boolean (1) 32700
'UserHgtUnits as Boolean (1) 32701
'UserFldElUnits as Boolean (1) 32702
'UserAscRtUnits as Boolean (1) 32703
'UserSpdUnits as Byte (1) 32704
'UserSampleTime as Byte (1) 32705
'UserFldEl as UnsignedInteger (2) 32706 32707
'UserAscRate as UnsignedInteger (2) 32708 32709
'
'Function returns number of bytes read from FRAM if successful
'else it returns a negative number
dim data(1 to 10) of byte
dim rvalue as integer
rvalue = FRAMRead(HRDWARE, FRAMDEV, FRAMUSRADR, data(), SizeOf(data))
if rvalue < 0 then
ReadSystemData = rvalue
exit Function
End if
UserToFromFlag = CBool(data(1))
UserToFromFlag = CBool(data(2))
UserFldElUnits = CBool(data(3))
UserAscRtUnits = CBool(data(4))
UserSpdUnits = data(5)
UserSampleTime = data(6)
Call BlockMove(2, MemAddress(data(7), MemAddress(UserFldEl))
Call BlockMove(2, MemAddress(data(9), MemAddress(UserAscRate))
ReadSystemData = rvalue
End Function 'ReadSystemData
|
By the way, here are the declarations of the system variables being discussed:
| Code: |
'User Parameters
Public UserToFromFlag as Boolean 'True = To or False = From
Public UserHgtUnits as Boolean 'True = Feet or False = Meters
Public UserSpdUnits as byte '0 = Knots, 1 = Km/Hr, 2 = M/Sec
Public UserFldElUnits as Boolean 'True = Feet or False = Meters
Public UserFldEl as UnsignedInteger '0 to 65535 units
Public UserAscRtUnits as Boolean 'True = Ft/Min or False = M/Min
Public UserAscRate as UnsignedInteger 'pibal ascent rate in user units
Public UserSampleTime as byte 'sample period 0 = 15 sec, 1 = 30 sec, 2 = 60 sec
|
Without declaring a whole bunch of new variables for use in these routines, I just want to move the already existing values into my scratchpad FRAM and later retrieve them, usually in another running of the program. Notice that I am putting these valuse in the high end of the FRAM. This is because I am using the low addresses to store variables computed during the running of the program. I have my reasons for doing this. The variables under discussion are not LIKELY to be changed by the user in any case as they define the system, but they could be!
I like the "alias" thing but I am not sure how to apply it here.
BTW:this forum editor does not seem to handle tabs very gracefully Nor the insertion of Emoticons
Any enlightenment will be appreciated.
Vic |
|
| Back to top |
|
 |
mikep
Joined: 24 Sep 2005
Posts: 765
Location: Austin, TX
|
|
Posted: 09 February 2006, 14:29 PM Post subject: |
|
|
Here is the set of global declarations you need for your system variables and data array: | Code: | Private data(1 to 10) as Byte
Private UserToFromFlag as Boolean Alias data(1)
Private UserHgtUnits as Boolean Alias data(2)
Private UserFldElUnits as Boolean Alias data(3)
Private UserAscRtUnits as Boolean Alias data(4)
Private UserSpdUnits as Byte Alias data(5)
Private UserSampleTime as Byte Alias data(6)
Private UserFldEl as UnsignedInteger Alias data(7)
Private UserAscRate as UnsignedInteger Alias data(9) |
and here is the rest of the (untested) code to make the system work
| Code: | Private Const HRDWARE as Byte = 0
Private Const FRAMDEV as Byte = 6
Private Const FRAMUSRADR as Byte = 32700
Private Const SDA as Byte = 11
private Const SCL as Byte = 12
'System data is maintained in at the top of the FRAM starting at the address 32700.
Public Function WriteSystemData() as Integer
Dim rvalue as Integer
rvalue = FRAMInit(HRDWARE, SDA, SCL, 0) 'use hardware I2C channel w/defaults
rvalue = FRAMWrite(HRDWARE, FRAMDEV, FRAMUSRADR, data, SizeOf(data))
WriteSystemData = rvalue
End Function 'WriteSystemData
Public Function FRAMInit(ByVal channel as Byte, ByVal SDAPin as Byte, ByVal SCLPin as Byte, ByVal BitRate as Byte) as Integer
Call OpenI2C(channel, SDAPin, SCLPin, BitRate)
FRAMInit = 0
End Function
Public Function FRAMWrite(ByVal channel as Byte, ByVal slaveId as Byte, ByVal address as Byte, _
ByRef dataToWrite() as Byte, ByVal length as Integer) as Integer
FRAMWrite = I2CCmd(channel, slaveId, address, length, 0, 0)
FRAMWrite = I2CCmd(channel, slaveId, dataToWrite(1), length, 0, 0)
End Function
Public Sub Main()
Debug.Print "Saving system data returns ";CStr(WriteSystemData())
End Sub
|
|
|
| Back to top |
|
 |
mikep
Joined: 24 Sep 2005
Posts: 765
Location: Austin, TX
|
|
Posted: 10 February 2006, 3:47 AM Post subject: |
|
|
For those of you who are awake there is a bug in the routine below. | Code: | Public Function FRAMWrite(ByVal channel as Byte, ByVal slaveId as Byte, ByVal address as Byte, _
ByRef dataToWrite() as Byte, ByVal length as Integer) as Integer
FRAMWrite = I2CCmd(channel, slaveId, address, length, 0, 0)
FRAMWrite = I2CCmd(channel, slaveId, dataToWrite(1), length, 0, 0)
End Function | I wasn't able to test this code but it should look something like this: | Code: | Public Function FRAMWrite(ByVal channel as Byte, ByVal slaveId as Byte, ByVal address as Byte, _
ByRef dataToWrite() as Byte, ByVal length as Integer) as Integer
Dim L as Byte
L = 1
FRAMWrite = I2CCmd(channel, slaveId, address, L, 0, 0)
FRAMWrite = I2CCmd(channel, slaveId, dataToWrite(1), length, 0, 0)
End Function |
|
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR
|
|
Posted: 10 February 2006, 5:07 AM Post subject: |
|
|
The "number of bytes to write" parameter comes before the "data to write" so the code below would be closer to being correct.
| Code: | Public Function FRAMWrite(ByVal channel as Byte, ByVal slaveId as Byte, ByVal address as Byte, _
ByRef dataToWrite() as Byte, ByVal length as Integer) as Integer
FRAMWrite = I2CCmd(channel, slaveId, 1, address, 0, 0)
FRAMWrite = I2CCmd(channel, slaveId, CByte(length), dataToWrite, 0, 0)
End Function |
Upon further reflection, however, it would seem that the 'address' parameter probably should be wider than a Byte. However, if the address will always be less than 256 it doesn't matter.
Note that the I2CCmd() procedure is capable of writing at most 255 bytes at a time. If this function needs to be able to write more than that, it will have to do it iteratively. |
|
| Back to top |
|
 |
|