Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : DS3231 code mmbasic
Page 2 of 3 | |||||
Author | Message | ||||
MOBI Guru Joined: 02/12/2012 Location: AustraliaPosts: 819 |
Hi Grogster, It is probably better to always read to an array rather than a single byte variable, in which case, dim an array with the maximum number of elements that you are likely to need. e.g. in the case of the RTC, DIM RTC_data(12) then when you specify the number of bytes to read each register will fit in the array. DIM rtc_data(12) i2c write RTC_address,0,1,0 i2c read RTC_address,0,3,rtc_data(0) This will read the seconds, minutes and hours into RTC_data(0), (1) and (2) The data will be in BCD format. In your example, you are trying to assign a byte value to a string instead of a numeric variable. (sorry I didn't get back straight away but visitors dropped in for a cuppa.) David M. |
||||
Grogster Admin Group Joined: 31/12/2012 Location: New ZealandPosts: 9308 |
OK, but how does the read NOT just end up putting ALL the bytes, one after another, into rtc_data(0)? Is this being handled internally by MMBasic? Not sure I understand the BCD data being returned by the RTC - I seem to recall having similar problems understanding things when I was trying to get it working on the PICAXE(and on their forums too). EDIT: @ MOBI - I warned you about me and I2C!!!! ANOTHER EDIT: This does not seem to want to work on the uM chip - it is rejected - see above image I posted. Smoke makes things work. When the smoke gets out, it stops! |
||||
MOBI Guru Joined: 02/12/2012 Location: AustraliaPosts: 819 |
Yes, it looks like that, but MMBasic puts the received bytes in successive array positions. I don't know if you understand BCD ? If you do, then sorry about the sucked eggs. If not, in BCD, a byte is divided into a left nybble and right nybble each of 4 bits and each with a numeric value 0 to 9. When you print it, it will come up with the binary version of the variable. If you look at the example in the back of Geoff's uMite manual, it might (or might not ) make things there a bit clearer. BCD 23 would look like 100011 but displays as decimal 35. BCD makes displaying numeric data easier. Don't know why your read command isn't working - it is here ok. Unless you are still trying to read to a string variable? David M. |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
Hi, this Basic code works for me with the ds3231 and mmbasic Beta 10 and the 28 pin pic. Connect Clock and Data from the RTC to Pins 18/17 of the pic with short wires. pin 18 I2C DATA pin 17 I2C CLOCK Tis Routine doesnt make any conversions. It only reads and prints the BCD values. You have to convert the BCD values to human readable "Numbers".... The datasheet is your friend ;-) Dietmar Dim RTCbuff(6) ' Received data is stored here I2C OPEN 100,100 ' Start up the I2C module I2C WRITE &b1101000, 0, 1, 0 ' Request the time If MM.I2C <> 0 Then Error "RTC not responding" I2C READ &b1101000, 0, 7, RTCbuff(0) ' Get 7 bytes from RTC I2C CLOSE ' Close the I2C function For i=0 To 6 Print rtcbuff(i) Next i |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
@ Grogster here an advanced example how to extract and calculate the seconds from the ds3231. Try the rest yourself ;-) Dietmar Dim RTCbuff(19) ' Received data is stored here I2C OPEN 100,100 ' Start up the I2C module I2C WRITE &b1101000, 0, 1, 0 ' Request the time If MM.I2C <> 0 Then Error "RTC not responding" I2C READ &b1101000, 0, 7, RTCbuff(0) ' Get 7 bytes from RTC I2C CLOSE ' Close the I2C function For i=0 To 19 Print rtcbuff(i) Next i seconds = rtcbuff(0) And &b00001111 zehnerseconds = rtcbuff(0) And &b01110000 zehnerseconds = (zehnerseconds \ 16) + zehnerseconds Mod 16 zehnerseconds = zehnerseconds * 10 Print zehnerseconds + seconds |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
And here the minutes...;-) minutes= rtcbuff(1) And &b00001111 zehnerminutes = rtcbuff(1) And &b01110000 zehnerminutes = (zehnerminutes \ 16) * 10 + zehnerminutes Mod 16 Print zehnerminutes + minutes |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
zehner is german for "ten" or "tens" Hours : hours = rtcbuff(2) And &b00001111 zehnerhours = rtcbuff(2) And &b00110000 zehnerhours = (zehnerhours \ 16) + zehnerhours Mod 16 Print zehnerhours + hours |
||||
BobD Guru Joined: 07/12/2011 Location: AustraliaPosts: 935 |
Try this program to read your DS3231. It may have some syntax errors. I can't test it all. Unless you have set the clock and date data then the output may be crap although the temperature should read correct. ' Read first 19 registers of RTC DS3231 ' Bob D 2014 03 09 'how much data do we want BuffLen = 19 ' RTC address RTCaddr = &h68 Dim RTCdata(Bufflen) Length 2 I2C open 100,100 I2C write RTCaddr, 0,1,0 If MM.I2C <> 0 then print "I2C error" end Endif I2C read RTCaddr, 0, Bufflen, RTCdata(0) I2C close ' Convert time and date data ' the data in the registers is BCD (aka Packed Decimal) format ' with a decimal number from 0 to 9 in each half byte (nibble) ' each byte has a 10s decimal and a units decimal ' except day of the week which is units only ' Not all bits are used in each half byte - only what is needed for that data ' Example: Day of month range is 1 - 31 so we need 2 bits for the 10s (decimal 0 to 3) ' and 4 bits for the units (decimal 0 - 9) so we can then express ' all decimal numbers from 1 - 31 in one byte ' each half byte needs to be processed 4 bits at a time and then added together ' this done by right shifting the left nibble 4 bits right (\16) and multiply by 10 ' to get the 10s value and then add the right nibble Seconds = (RTCdata(0) and &h70)\16*10 + (RTCdata(0) and &h0f) Minutes = (RTCdata(1) and &h70)\16*10 + (RTCdata(1) and &h0f) Hours = (RTCdata(2) and &h70)\16*10 + (RTCdata(2) and &h0f) DoW = (RTCdata(3) and &h07) DoM = (RTCdata(4) and &h30)\16*10 + (RTCdata(4) and &h0f) Month = (RTCdata(5) and &h10)\16*10 + (RTCdata(5) and &h0f) Year = (RTCdata(6) and &hf0)\16*10 + (RTCdata(6) and &h0f) print Hours; Minutes; Seconds print DoM; Month; Year ' from here is optional but is dependent on previous code ' Convert Day of the week to day name ' Sunday=1 - Saturday=7 DayNames$ = "123456789Sunday 6Monday 6Tuesday 7Wednesday9Thursday 8Friday 6Saturday 8" Day$ = Mid$(DayNames$, DoW*10, 10) L = Val(Right$(Day$, 1)) Day$ = Left$(Day$,L) Print "The day of the week is "; Day$ ' Convert temperature and Aging Offset data ' This data is in Twos Complement format ' in register 17 and bits 7 & 6 of register 18 If RTCdata(17) < &h80 Then Temperature = RTCdata(17) + (RTCdata(18) / 256) Else Temperature = -(((RTCdata(17) Xor &hFF) + 1) + (RTCdata(18) / 256)) EndIf t$=Format$(Temperature,"%+6.2f") Print "Temperature is "; T$; "degrees Celsius" If RTCdata(16) < &h80 then Offset = RTCdata(16) Else Offset = -(RTCdata(16) Xor &hFF) Endif Print "The Aging Offset data is "; Offset End |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
Hi BobD, your code is definitely much more elegant than what i wrote!;-) Do you also have such an elegant sample code for writing to the rtc?? Thank you Dietmar |
||||
BobD Guru Joined: 07/12/2011 Location: AustraliaPosts: 935 |
Dietmar, thanks for that. I hope it works OK for you. I was unable to test it as presented because I don't have a working machine, MM or uM. The hardest part was commenting it for publication given that the format of the time and date data (in the RTC) is not an easy concept for some of us. I do have the code to load the DS3231 but it will require some preparation. I am also doing some house renovations at the moment (I am under instructions from the boss). If anyone is impatient to load the RTC it can be done by computing the register values manually, putting them into an array and dumping the content of the array to the RTC. Example: put time (UTC now) and date into an array RTCdata(0) = &h00 ' seconds RTCdata(1) = &h09 ' minutes RTCdata(2) = &h18 ' hours RTCdata(3) = &h01 ' Day of Week - Sunday RTCdata(4) = &h09 ' Day of Month RTCdata(5) = &h03 ' Month RTCdata(6) = &h14 ' Year then give it some I2C code and you should be off and running. |
||||
Grogster Admin Group Joined: 31/12/2012 Location: New ZealandPosts: 9308 |
Have been away Sunday, and lots of activity here! I will read all these posts and try some of the demo codes and post back in due course. Thanks guys! Lots of info for me to play with. Smoke makes things work. When the smoke gets out, it stops! |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
Hi Bob, it will be some more effort to put the right values to the RTC. We have to do BCD conversion, you know that the RTC uses it for the internal Format. I stuck in Problems for writing to the RTC.. I2C write seems to have some Problems with writing to the RTC. So strange things happen, that i can't explain what ist going on here. I hope there is not a bug in the micromite.. In most cases i'am the bug;-) Dietmar |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
This part of code is working very well. I Ddidn't test the optional part, tried to do some writes to set the RTC first, with th above described probs.. ' Read first 19 registers of RTC DS3231 ' Bob D 2014 03 09 'how much data do we want BuffLen = 19 ' RTC address RTCaddr = &h68 Dim RTCdata(Bufflen) Length 2 I2C open 100,100 I2C write RTCaddr, 0,1,0 If MM.I2C <> 0 then print "I2C error" end Endif I2C read RTCaddr, 0, Bufflen, RTCdata(0) I2C close ' Convert time and date data ' the data in the registers is BCD (aka Packed Decimal) format ' with a decimal number from 0 to 9 in each half byte (nibble) ' each byte has a 10s decimal and a units decimal ' except day of the week which is units only ' Not all bits are used in each half byte - only what is needed for that data ' Example: Day of month range is 1 - 31 so we need 2 bits for the 10s (decimal 0 to 3) ' and 4 bits for the units (decimal 0 - 9) so we can then express ' all decimal numbers from 1 - 31 in one byte ' each half byte needs to be processed 4 bits at a time and then added together ' this done by right shifting the left nibble 4 bits right (\16) and multiply by 10 ' to get the 10s value and then add the right nibble Seconds = (RTCdata(0) and &h70)\16*10 + (RTCdata(0) and &h0f) Minutes = (RTCdata(1) and &h70)\16*10 + (RTCdata(1) and &h0f) Hours = (RTCdata(2) and &h70)\16*10 + (RTCdata(2) and &h0f) DoW = (RTCdata(3) and &h07) DoM = (RTCdata(4) and &h30)\16*10 + (RTCdata(4) and &h0f) Month = (RTCdata(5) and &h10)\16*10 + (RTCdata(5) and &h0f) Year = (RTCdata(6) and &hf0)\16*10 + (RTCdata(6) and &h0f) print Hours; Minutes; Seconds print DoM; Month; Year |
||||
atmega8 Guru Joined: 19/11/2013 Location: GermanyPosts: 722 |
Here an Example of how to write to the DS3231. Not complete, but it shows the principles. It will not take to much time to complete it. If someone wants do this for all, so please use it.. Tested un 28 PIN OIC with latest umite version Dim RBUFF(10) Sekunden = 55 Minuten = 59 Stunden = 23 'if use i2c write do not beginn with 0 in array but with one.. ' I would call it a litte bug in mmbasic ?? what do you think geoff? ' It is strange to beginn with 1 in the Array, but to start with 0 in i2c write.. ' i think i saw somewhere in the docu an option to configure where the Array ' index starts??; then it is a Feature not a bug ;-) RBUFF(1) = (sekunden \ 10*16 + sekunden Mod 10) RBUFF(2) = (Minuten \ 10*16 + minuten Mod 10) RBUFF(3) = (Stunden \ 10*16 + Stunden Mod 10) RBUFF(4) = &b00010001 RBUFF(5) = &b00010001 RBUFF(6) = &b00010011 RBUFF(7) = &b00010001 RBUFF(8) = &b00010001 I2C OPEN 100,100 I2C write &H68, 0, 8,RBUFF(0) I2C close |
||||
jman Guru Joined: 12/06/2011 Location: New ZealandPosts: 711 |
Try this to set the time It will get the current Time and date from the uMite and write it to the RTC SetTime: ' Get time from time$ and date$ tempdec = val(left$(time$, 2)) BCDtoHex TempDec hours = hex tempdec = val(mid$(time$, 4, 2)) BCDtoHex TempDec minutes = hex tempdec = val(right$(time$, 2)) BCDtoHex TempDec seconds = hex tempdec = val(left$(date$, 2)) BCDtoHex TempDec day = hex tempdec = val(mid$(date$, 4, 2)) BCDtoHex TempDec month = hex tempdec = (val(right$(date$, 4)) - 2000) BCDtoHex TempDec year = hex rtcctrl = &h10 rtcwday= &h1 ' Write Time to RTC i2caddr = &h68 ' RTC I2C address i2c Open 100,100 ' Enable I2C ' i2csend i2caddr, 1, 1 , 1 I2C Write i2caddr, 0, 9, &h0, seconds, minutes, hours, rtcwday, day, month, year, rtcctrl I2C Close ? "0=ok 1=nack 2=timeout"; mm.i2c ? "RTC has been set to ";time$;" ";date$ end ' Convert to Hex Sub BCDtoHex TempDec hex = fix(tempdec / 10) * 16 hex = hex OR ((tempdec / 10) - (fix(tempdec / 10))) * 10 return Regards Jman |
||||
MOBI Guru Joined: 02/12/2012 Location: AustraliaPosts: 819 |
I originally wrote this for the MM using only i2c devices. I've modded it for uMite but still with I2c keys and i2c LCD. The structure at least might be of some use. It works on my uM. I don't know how many TBS members have i2c LCD and 12 Key keypad but I can supply the source, hex and schema for those interested. Pressing the "*" key activates the register set mode. Other wise it is a timing loop. Option autorun on
Option break 3 Dim RTCbuff(12) ' Received data is stored here Dim keys(2) Dim bcd_byte Dim bcd blank$ = " " mylcd = &b0111000 'LCD address myrtc = &b1101000 'real time clock address mykeypad = &b0110100 'key pad address I2C OPEN 100,100 ' Start up the I2C module I2C write mylcd,0,3,0,0,2 I2C write mylcd,0,3,0,1,4: Pause 100 'LCD reset. (my LCD pic module) I2C write mylcd,0,3,0,44,2 'two line display 'I2C write mylcd,0,3,0,15,2 'Main Routine starts here Do 'scan past keys - check for keypress If readkey() = 42 Then set_rtc 'if Keypad "*" key then modify register. I2C write mylcd,0,3,0,12,2 'LCD On, Cursor Off I2C WRITE myrtc, 0, 1, 0 ' Request the time If MM.I2C <> 0 Then Error "PCF8563 not responding" I2C READ myrtc, 0, 7, RTCbuff(0) ' Get the time as 7 bytes bcdtemp = RTCBuff(0) And &H7F ' Get just the bits that we want sec$ = Str$((bcdtemp \ 16) * 10 + bcdtemp Mod 16) bcdtemp = RTCBuff(1) And &H7F min$ = Str$((bcdtemp \ 16) * 10 + bcdtemp Mod 16) bcdtemp = RTCBuff(2) And &H3f hours$ = Str$((bcdtemp \ 16) * 10 + bcdtemp Mod 16) bcdtemp = RTCBuff(4) And &H3f day$ = Str$((bcdtemp \ 16) * 10 + bcdtemp Mod 16) bcdtemp = RTCBuff(5) And &h1F month$ = Str$((bcdtemp \ 16) * 10 + bcdtemp Mod 16) bcdtemp = rtcbuff(6) year$ = Str$((bcdtemp \ 16) * 10 + (bcdtemp Mod 16) + 2000) If Len(sec$)<2 Then sec$ = "0" + sec$ 'insert leading 0s for appearances If Len(min$)<2 Then min$ = "0" + min$ t$ = hours$+":"+min$+":"+sec$+" " D$ = day$+"/"+month$+"/"+year$+" " disp_str(128,t$) 'display time - or use uMite LCD commands disp_str(192,d$) 'and date on LCD Loop 'end of main routine Function getkey() 'wait for a key Do I2C write mykeypad,0,1,0 'request a key I2C read mykeypad,0,1,keys(0) 'read keypad Loop Until keys(0) <> 0 'non zero value means a valid key getkey = keys(0) I2C write mykeypad,0,2,0,0 'Tell the keypad that it can send new data End Function Function readkey() 'read a key value no waiting for key press I2C write mykeypad,0,1,0 'request key Pause 10 I2C read mykeypad,0,1,keys(0) 'read keypad regardless readkey = keys(0) I2C write mykeypad,0,1,0 End Function Sub set_rtc 'read RTC register and new time value I2C write mylcd,0,3,0,14,2 'cursor on I2C write mykeypad,0,2,0,0 'clear any leftover key press I2C write mylcd,0,3,0,1,2 'clear LCD disp_str(192,blank$):disp_str(192,"Register ") 'blank lines 1 and 2 register = get_dec() 'get register number in decimal format disp_str(128,blank$): disp_str(128,"# to accept ") If getkey() <> 35 Then Exit Sub '35 is # key disp_str(128,blank$) disp_str(192,blank$):disp_str(192,"Value ") bcd=get_bcd() 'get Register value in BCD format disp_str(128,blank$): disp_str(128,"# to accept ") If getkey() <> 35 Then Exit Sub I2C write myrtc,0,2,register,bcd 'write the new time value to the appropriate register End Sub Sub disp_str(ptr,a$) 'display a string to numinated screen position Local x, char$ I2C write mylcd,0,3,0,ptr,2 For x = 1 To Len(a$) char$=(Mid$(a$,x,1)) I2C write mylcd,0,3,0,Asc(char$),1) Next x End Sub Function get_dec() 'get a 2 key decimal value include leading zero Do key=getkey() Loop Until key >= 48 I2C write mylcd,0,3,0,key,1 'display key left = (key-48)*10 Do key=getkey() Loop Until key >= 48 I2C write mylcd,0,3,0,key,1 'display key right=key-48 get_dec=left+right 'combine digits End Function Function get_bcd() 'get 2 key BCD value include leading zero Do key = getkey() Loop Until key >= 48 'i.e, a number left = (key-48)*16 I2C write mylcd,0,3,0,key,1 'display key Do key = getkey() Loop Until key >= 48 'a number right = key-48 I2C write mylcd,0,3,0,key,1 'display key get_bcd=left Or right 'combine digits End Function David M. |
||||
BobD Guru Joined: 07/12/2011 Location: AustraliaPosts: 935 |
The I2C write command to a DS3231 needs to have a pointer to DS3231 register 0 followed by the data. This makes it impossible to use an array to write the data unless the register pointer is in the first used array index followed by the data in subsequent contiguous indexes. This is either a bug, a feature or a limitation (select one) of the MMBasic I2C implementation. Example: this works dim array(7) length 2 I2Cen I2Csend &H68, 0, 7, array(0) I2Cdis Example: This does not work dim array(7) length 2 I2Cen I2Csend &H68, 0, 8, 0, array(0) I2Cdis It gets an error Error: Insufficient Arguments It seems it is not possible to mix non arrayed variables or constants with arrayed variables in an I2Csend command. I have used the MMBasic I2C commands because I tested on MMBasic v4.4. It seems that Dietmar has run into similar problems with MicroMite basic. |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 6102 |
I am surprised that this line didn't give an error: dim array(7) length 2 LENGTH is only valid for string arrays. According to the MicroMite manual, You can send data as either individual bytes OR a one dimensional array (but not mixed) You can start the data in the array from any position in the array. This is valid: dim mydata(10) I2Csend &H68, 0, 7, mydata(2) which would send 7 bytes starting at mydata(2) Jim VK7JH MMedit MMBasic Help |
||||
MOBI Guru Joined: 02/12/2012 Location: AustraliaPosts: 819 |
In the second example, the array is set to 7 but 8 bytes are being sent. Wouldn't this throw an error? David M. |
||||
BobD Guru Joined: 07/12/2011 Location: AustraliaPosts: 935 |
Two reasons it didn't happen is the mixed variables bombs it first but if mixed variables were allowed then it is legit because all after the length is data including the 0 preceding the array. It is the address of register 0 of the RTC. |
||||
Page 2 of 3 |
Print this page |