Jump to content

ppulle

EstablishedMember
  • Content Count

    71
  • Joined

  • Last visited

Everything posted by ppulle

  1. oh well, perhaps someday. Keep up the good work on Sourceboost, it's a great compiler. As an alternative, perhaps easier...perhaps not. Would it be possible to link to libraries created with MPLAB? The idea being a blob of code such as the much desired CDC stack can be created with MPLAB then linked to a sourceboost codebase. Even a manual process of static linking would help. Surely it's just a matter of getting the right function locations, making sure sourceboost doesn't clobber MPLAB variables and getting call stack conventions right. A shim of sourceboost code could surround the calls. Phil
  2. Hi, Has there been any progress in implementing bit fields in the latest releases of Sourceboost. I'd basically like to drop in Microchip CDC USB stack without major surgery....ie have something like typedef union _USB_DEVICE_STATUS2 { byte _byte; struct { unsigned RemoteWakeup:1;// [0]Disabled [1]Enabled: See usbdrv.c,usb9.c unsigned ctrl_trf_mem:1;// [0]RAM [1]ROM }; } USB_DEVICE_STATUS2; compile and access it like USB_DEVICE_STATUS usb_stat; usb_stat.ctrl_trf_mem = 1;
  3. Looks OK to me too. Lets look at the asm assume the argument is greater than 3, lets say 4; MOVF test_Func_00000_arg_test, W will move 4 into W SUBLW 0x03 will subtract 3 from W, leaving 1 in W and no carry in the status register. No need to use SUBWF, we possibly save some code space by avoiding any bank switching if it were needed. BTFSC STATUS,C tests carry flag, and skips the next statement 'goto' if the flag is clear (Bit Test F, skip if Clear) GOTO label1 goto will be skipped as expected and execution will begin in the main part of the if statement ..... {if...} label1 {else}
  4. Hi Dave, Sorry if the below sounds confused and incoherent...but I'm a little lost at the moment. I updated to version 6.96 but with the same result. Next I looked closely at the i2c_driver.h file, and noticed that some of the variable definitions were short instead of char and the #defines pointing to the variable allocations were out of order (I think this has been noted in another post). I fixed these up and the code works properly. This is a bit puzzling, as even if I don't call the i2c functions, the code fails (if I set the i2c_driver.h back to the way it was that is). I can only think that the changes (short to int) in the i2c_driver.h file just does a rearrangement of the memory allocation to a harmless format as I noted below. Without the changes the memory map may have done something bad. In the asm file I've noticed that there are a lot of CompTempVarxx variables reusing memory allocation (as well as function arguments and return values). No doubt the linker makes sure that nothing is shared when it shouldn't be for a normal call tree. I've also go no doubt the linker makes sure call trees starting from an interrupt call don't inappropriately share temporary variables. Could there be a issue with the fact I'm calling a library function inside my interrupt which may be confusing the linker with regards to temporary variable allocation and sharing? Just to add to the question above, the library function in the interrupt call tree also uses a trick to emulate a function pointer call. This is my current suspect. The library code was written long ago before function pointers were supported by Sourceboost, however does the current version deal with any temporary variables etc called from a function pointer in an interrupt function? Here is the function pointer call emulation code: void __osEmulateFunctionPtr(pFunc pFPtr) { byte Savedintcon=intcon; intcon.GIEH=0; intcon.GIEL=0; asm { // push a dummy entry onto the hardware stack push // copy the three bytes which define the address movff _pFPtr,_tosl movff _pFPtr+1,_tosh movff _pFPtr+2,_tosu }; intcon=Savedintcon; // now enter the routine who's address is at (TOS) return; } Obviously this would confuse any compiler or linker, the question above is to clarify whether the new version of sourceboost properly makes sure the temp vars of a function pointer call are not shared, particularly when done in an interrupt function. FYI the reason for this was the library was adapted from Microchips CDC code which used function pointer arrays to call the appropriate USB service handlers if you had a USB connection with multiple protocols. Fortunately I'm only using one, so I can make a direct call and bypass the function pointer call, probably save some code space as well. I'll update this thread when I've re-written the library to use the newly support function pointer calls or have simply bypassed the use of any function pointers. Phil PS: As an aside, is there an existing option, or can an option be added to a new release, to show the variables used (including temp variables) for each function in the map file?
  5. Hi Dave, I've had a close look at the asm code, and it appears that in the 'bad' project, the linker is adding all the appropriate bank switch statements to access the reshuffled variables (ie movb instructions), as well as using the different op codes for page zero vs page something else accesses. It looks like this is being done even for the few asm{ } constructs as well. In other words it appears the linker and compiler are doing their proper function. It's probable then that maybe I've looped somewhere and done a buffer overrun or equivalent. In the 'good' project the buffer may be overrun into something harmless like a temporary variable of a function that doesn't interfere with things and in the 'bad' project the reshuffling of variables puts the overrun into something critical. Without an ICD I can't tell.....unfortunately my comms is the thing going wrong so debug statements won't help either. Looks like I'll have to manually check all my looping/array operations..yuk. Can you make a suggestion on any free tools to help? I'll give a later version of the compiler a go, and see if that helps. Only problem is I won't know what was causing the problem, which is never a good thing. I was wondering if you could answer whether there is an optimisation option in the linker that re-shuffles variable allocation to minimise bank switching. The compiles I've been doing that manually shuffle things have been showing quite big differences (hundreds of bytes, important when you're close to the limit) in the code size due to bank switching either being necessary or not necessary, or being inserted in code fragments when multiple variables across various pages are being used. Phil
  6. Hi Dave, Thanks for the headsup. It saved me a fair bit of time unnecessarily rearranging my switch statement to see if I could shorten it. I found the problem, though it's a little off topic (nothing to do with switch statements or function sizes), I thought I might explain it here as the process I went through might help others. I had some hardware which basically logged accelerometer and other data to a memory chip. It has a USB connection to download these logs and control bits and pieces. I use a library of USB functions to do the USB connection, that is a compiled library linked in, not some code in C compiled every time. I added an i2c peripheral, a magnetometer, and started logging this data. For various reasons I was using software i2c emulation. Initial tests were OK. Next I added some more functionality and I found that when I was doing a USB conversation things would stuff up. I originally thought this was in the case statement as this was where the new functions were added...hence the topic question. Luckily I keep a weekly archive of my code (hint number one for new programmers). I went back to the old stuff before the new code was added and looked at the '.asm' file. I copied the EQU statements where memory was allocated to a spreadsheet and sorted it. I did the same for the new code. Flicking between spreadsheet pages let me see what was different. I noticed that there was a data structure in the old working code that was allocated into the first page of memory (fyi the USB configuration structure). With the new code added it had been allocated out of that page. Looking a bit further it appeared that the #defines for the i2c software emulation were by default being allocated right in the middle of my data structure, so naturally the linker moved the data structure around it....fair enough Unfortunately the code that used the data structure must have been expecting this structure to be in the first page, when it was moved it behaved erratically. I simply redefined the i2c software emulation #defines out of the data structures path. Now the linker puts everything where the library code expects it to be. So some lessons: - when writing code for compiled libraries be aware that the linker may move data structures around. If your code is optimised for one page or another, there will be trouble. - I still might have some problems if the linker moves data around....but at least I know to check for it now. - Check your asm file if things go weird. Some questions: - how should code for a compiled library be written to ensure that it is impervious to data structures being moved about by the linker? I checked that there was no assembly code, so I presume the compiler and linker at library creation time put the data access optimisations in (I used linker -O1 -rb 0x0800, no optimisations on the compiler command line). - What optimisations are not recommended for compiled libraries? Is this a bug? Some suggestions: - can we get an IDE function to provide a sorted memory map on the codebar, preferably showing whats defined, how big it is and where. Even better showing what functions are using the memory allocations. Using a spreadsheet was OK.....but a little fiddly. Phil As an afterword as I was moving the #defines for the i2c driver about I noticed that one build overfilled by 400 locations, yet another build left me with 206 bytes free.....I only had to move it 8 places to get it good......but just goes to show how memory allocation can affect the compiled code size, presumably due to extra page whacking being necessary or not necessary. I wonder if there is scope for improvement in the linker/compiler combination there?
  7. Hi, I'm using Sourceboost 6.87 on a PIC18F4550 target. I've just started getting strange behavior that looks like a stack overflow though my stack depth is less than 8 for the functions being called. That is functions are stopping for no apparent reason. I was wondering if there is a limit on the number of case statements in a switch function, or if there is a limit on function sizes. This seems the likely culprit as the problems started occuring just after adding new functionality. In my case I have a command handler which is now 2188 bytes long with 43 case statements..... is this too much?? Phil
  8. Just to let you know letting Sourceboost do the days lookup worked well....clocks been running for several days without problems.
  9. Hi, Thanks for the suggestion on using the array. As a quick fix it should work. I must admit to forgetting that Sourceboost can now properly reference variables in asm code.....I've been using it for a while and remember having to use globals for that sort of thing (on a very old version), so have been a bit shy to do so. I'll get to it as soon as possible and come back with the result. I share your mystification on how the code works, one reason why if I get the chance I'll do a re-write. I suppose we can summarize some lessons for other users: - be wary of using PCL lookup tables, in or out of interrupt routines because of the page boundary problem - be careful of dropping in someone elses code, particularly if you don't fathom it entirely, even more particularly if it's been ported from another chip - if you must use PCL lookups, use the fixed function location as Dave suggests. Anyway, thanks all. Phil
  10. Hi IanM, Thanks for you reply. I agree the code is complex, I'd like to make it simpler which is usually important in an interrupt routine....that's what you get using cut and paste from different sources I suppose (I sloppy way of saying 'don't blame me....I didn't write it). Oops I'm sorry I forgot to mention it's a 18F4450. Interestingly the fault doesn't manifest is such a dramatic way as a reset or bus fault. I would have noticed that early on. FYI I have a logging system. The RTC chugs along quite nicely for days on end till the battery runs out, when logging events occur they are logged with the correct time and date. It's only when I start up the USB system again there is a problem. Presumably, as I mentioned, my usb service interrupt gets slyly disabled. Looking at my .lst the days routine; although it is outside the interrupt routine page it doesn't get put across a page boundary...but I get your point, it might, depending on the compilers whim, so PCL lookups are not good. It shall be removed. Looks like I'll have to re-write my RTC clock/calendar.....in 'C'....where possible.....because it's a C compiler! (just being frivolous above...no offense.....please don't wield the dreaded big bold font at me.....arrrrggghghghh mercy ) I suppose it's tempting fate to wonder if anyone has a safe 18F4550 clock calendar code example, I'm wryly ruing wrangling the code from a 16F84 example. Phil
  11. Hi, I have an interrupt servicing a RTC (real time clock) routine. However it uses a lookup table to determine the days of the month that does a call/PCL jump/retlw technique that doesn't seem to work. I'm using TMR1 to set flags to do ADC samples (polled in main code) as well as do the RTC. The samples are sub multiples of the RTC clock. On each second various variables (seconds, minutes, hours, days, months, year) are adjusted to the time. TMR0 is used to call a USB service routine. Here is the gist of the code, various flags and variables have been omitted: void days(void) { asm { ;_days: addwf _pcl,F ; Number of days per month retlw 0x00 ; Leap-day 29 retlw 0x1f ; January 31 retlw 0x1c ; February 28 retlw 0x1f ; Mars 31 retlw 0x1e ; April 30 retlw 0x1f ; May 31 retlw 0x1e ; June 30 retlw 0x1f ; July 31 retlw 0x1f ; August 31 retlw 0x1e ; September 30 retlw 0x1f ; October 31 retlw 0x1e ; November 30 retlw 0x1f ; December 31 } } void interrupt(void) { //Clock code from Microchip datasheet, calendar code from Jaakko Ala-Paavola asm { btfss _pir1,TMR1IF ; check if this is a timer interrupt goto usb_service ; no so do usb stuff bsf _nRTCFlags,fSAMPLE_READY; sets a flag which is polled in main code to do an ADC sample movf _nRTCTMR1H,W ; movwf _tmr1h bcf _pir1,TMR1IF ;clear interrupt flag incf _nRTCRecord,F movf _nRTCRecord,W subwf _nRTCRecordRate,W; more ADC stuff.....no relevant to forum question btfss _status,Z goto int_dortc bsf _nRTCFlags, fREPORT_READY clrf _nRTCRecord ;This is the RTC stuff int_dortc: incf _nRTCSubSecs,F movf _nRTCSubSecs,W subwf _nRTCSampleRate,W btfss _status,Z ;number of samples per second elapsed? goto int_exit ;no clrf _nRTCSubSecs ;Now we adjust the RTC clock on each second interval incf _nRTCSecs,F ;increment seconds movf _nRTCSecs,W sublw 60 ; btfss _status,Z ;60 seconds elapsed?? goto int_exit clrf _nRTCSecs ;yes, so clear seconds incf _nRTCMins,F movf _nRTCMins,W sublw 60 ;60 minutes elapsed?? btfss _status,Z goto int_exit ;no so return clrf _nRTCMins incf _nRTCHours,F ;increment hours movf _nRTCHours,W sublw 24 ;24 hours elapsed?? btfss _status,Z goto int_exit clrf _nRTCHours ; Calendar code from Jaakko Ala-Paavola http://users.tkk.fi/~jalapaav/Electronics/Pic/Clock/index.html bsf _nRTCFlags,dayf ; dayf = 1; incf _nRTCDay,F ; day++; movf _nRTCMonth,W ; ACCU = month; sublw 0x02 ; btfss _status,Z ; if ((ACCU - 2) == 0) goto _noleap ; { btfsc _nRTCFlags,lyf ; if (leap_year) andlw 0x00 ; ACCU = 0; goto _leap ; }else _noleap: movf _nRTCMonth,W ; ACCU = month; _leap: ;>>>>>>>>>>>>>> this seems to be the problem code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< call days ; ACCU = days[ACCU]; ;>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< subwf _nRTCDay,W ; if ((day-ACCU) != 0) btfss _status,Z ; return; goto int_exit ; else { movlw 0x01 ; ACCU = 1; clrf _nRTCDay ; day = ACCU; bsf _nRTCFlags,monthf; monthf = 1; incf _nRTCMonth,F ; month++; movf _nRTCMonth,W ; ACCU = month; sublw 0x0d ; if ((ACCU-13) != 0) btfss _status,Z ; return; goto int_exit ; else { movlw 0x01 ; ACCU = 1; movwf _nRTCMonth ; month = ACCU; bsf _nRTCFlags,yearf; yearf = 1; incf _nRTCYear,F ; year++; bcf _nRTCFlags,lyf ; lyf = 0; movf _nRTCYear,W ; ACCU = year; andlw 0x03 ; if ((ACCU & 00000011) == 0) btfsc _status,Z ; { bsf _nRTCFlags,lyf ; lyf = 1;} movf _nRTCYear,W ; ACCU = year; sublw 0x64 ; if ((ACCU-100) != 0) btfss _status,Z ; return; goto int_exit ; else { clrf _nRTCYear ; year = 0; incf _nRTCCentury,F ; century++; bcf _nRTCFlags,lyf ; lyf = 0; movf _nRTCCentury,W ; ACCU = century; andlw 0x03 ; if ((ACCU & 00000011) == 0) btfsc _status,Z ; { bsf _nRTCFlags,lyf ; lyf = 1; goto int_exit usb_service: btfsc _intcon,TMR0IF call cdc_service bcf _intcon,TMR0IF ;clear sample interrupt flag goto fin_int int_exit: ;had some stuff here before fin_int: } } The code basically checks if a TMR1 interrupt has occured, if it has it does some flag setting and RTC updates. If not, then it checks if TMR0 interrupt has occured and does a usb service call. The problem is that when the clock clicks over from 23:59:59 to the next day, some undefined chaos occurs and my USB stops working, probably because it misses a service call or doesn't get reset (so never TMR0 interrupt never occurs again). If I comment out all the lines in the 'days' routine, and replace it with a fixed line like RETLW 0x1f, all is well. It appears it is the table lookup method using the line: addwf _pcl,F ; Number of days per month that is the problem. Is fiddling with the pcl during an interrupt routine a bad thing? Could it be bollocksing up something that interferes with everything else? Is there an alternative to using a PCL/RETLW sequence to do a table lookup? I've looked at using the TBLRD opcode, but have no experience with using table reads and am looking for some advice on setting them up. I don't think it's a timing issue, because if I replace all the RETLW and addwf PCL,F statements with loads of nops everything works. eg: asm { nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop retlw 0x1f } presumably this would actually take longer to execute than the original code. So the only thing I can think of is adding something to the pcl during the interrupt routine (servicing TMR1..the RTC) then TMR0 goes off. So I suppose there are a couple of questions: - what can I replace my ADDWF PCL,F/RETLW sequence with that is safe during an interrupt, can anyone provide a short code fragment please. - what is causing the problem in the first place. Obviously making calls during an interrupt is OK (though not advised)...it's just the PCL fiddling. Thanks Phil
  12. Hi All, I'll leave it to others to check out the method russ has come up with (I've got my solution and moved on to other stuff)...it does look interesting however. I don't think rounding (in terms of just the multiplication) will be an issue if the numbers are limited to the precision of the accumulator (ie for 16bit signed limit the additional samples to 4096 ie 16-sign bit-divisor bits)....however the division gives me a bit of a worry, as there will nearly always be a remainder...that's probably the main area where loss of accuracy will occur. Note that we're doing an average and effectively throwing away bits to smooth things out anyway...so maybe its a case of try it and see. My main concern is that a divide by seven operation (or divide by one less than the multiplication for other selections) will by it's nature never be a clean shifting operation, hence you'll need a progressive subtraction routine (ie division) added to the code...increasing the code length and processing time. Alternatively if you keep the division a multiple of 2 (eg 8) you'll need a progressive addition (ie multiplication) to do the +1....either way it's not an even multiply or divide. If the 8x8bit multiply opcode on the 18F4450 is used, maybe you can multiply by 9 using the opcode and divide by 8 using shifts and not call a separate routine....in which case you'd have a 9 tap filter....of course that only works with 8 bit numbers. Phil
  13. Thanks for the heads up on how compilers deal with signed ints.......it's sure to be a trap for the unwary. I was hoping there'd be a generic bit fiddling algorithm that'd handle that sort of thing. Anyway I've taken everyones advice, subtracting the old history value before adding the new one to quickly calculate the totals as well as converting negative ints to positive (and back again) before doing the division. Thanks to all who've helped. Below is my contribution to the sourceboost world.....a 12 bit precision, signed, eight channel, eight tap finite impulse response filter....aka moving average on eight inputs. Any comments on stripping more bytes from the code would be appreciated, I'm sure there are more efficient ways of doing some things like converting negative to positive numbers, dividing and adding 16bit ints etc. In any case it compiles to 164 bytes, which is fine for my application, and a lot less than I thought it would be. Phil #define SENSOR_CHANNELS 8 #define TAP_COUNT 8 #define NEXT_ROW_OFF 16 #define TAP_MASK 0eh int aSamples[SENSOR_CHANNELS * TAP_COUNT ]@100; int aSensors[SENSOR_CHANNELS]@0x20; int aTotals[SENSOR_CHANNELS]@0x40; unsigned char nSamplePos; //FUNCTION to implement an eight channel eight tap finite impulse response filter (aka Moving average filter) //eight separate sensor inputs can be averaged //INPUTS: // aSensors := array of eight int sensor inputs (will be modified after the function) //OUTPUTS: // aSensors := averaged sensor inputs based on previous inputs //USES: // aSamples := array of histories of each sensor // aTotals := array of total values of the histories, used in calculating the averages // nSamplePos := byte position in the history array, nb we keep it at 0,2,4..14 not element (eg 0,1,2,3) //RESTRICTIONS: // TAP_COUNT must be a multiple of 2 // ints are used, so precision is 16bits - sign bit - divisor bit count, eg 16-1-3 = 12 bits for 8 tap // so this code can only be used for values +-4096 max // need to manually modify division section to do division if 8 tap is not used // TAP_MASK should be defined as SENSOR_CHANNELS*2-1 void av_samples(void) { unsigned char nChannel; asm { //Setup the indirect registers movlw LOW(_aSamples) ;fsr0 := aSamples + nSamplePos movwf _fsr0l movlw HIGH(_aSamples) movwf _fsr0h movf _nSamplePos,W addwf _fsr0l,F ;add the nSamplePos to get to old history sample btfsc _status,C incf _fsr0h,F movlw LOW(_aTotals) ;fsr1 := aTotals movwf _fsr1l movlw HIGH(_aTotals) movwf _fsr1h movlw LOW(_aSensors) ;fsr2 := aSensors movwf _fsr2l movlw HIGH(_aSensors) movwf _fsr2h movlw SENSOR_CHANNELS ;we are doing eight channels movwf _nChannel AV_SAMP_LOOP: movf _postinc0,W ;get old sample value, low byte subwf _postinc1,F ;and subtract from total, low byte movf _postdec0,W ;ie Total := Total - OldSample subwfb _postdec1,F movf _postinc2,W ;Insert new sample into history array movwf _postinc0 movf _postdec2,W movwf _postdec0 ;aSamples[nSamplePos/2,nChannel] := aSensor[nChannel] movf _postinc2,W addwf _postinc1,F ;add new sensor value to totals movf _postdec2,W ;nTotal := Total + NewSample addwfc _postdec1,F movf _postinc1,W movwf _postinc2 movf _postdec1,W ;gee it'd be nice if microchip implemented a predecX operand movwf _indf2 ;move total to samples array btfss _postdec2,7 ;check if value is negative bra AV_SAMPLES_SHIFT comf _postinc2,F comf _postdec2,F ;invert value movlw 1 addwf _postinc2,F clrf _wreg,F addwfc _postdec2,F AV_SAMPLES_SHIFT: bcf _status,C ;now everything is positive so we can divide by eight rrcf _preinc2,F movf _postdec2,W rrcf _indf2,F ;divide by 2 bcf _status,C ;divide by 2 rrcf _preinc2,F movf _postdec2,W rrcf _indf2,F bcf _status,C rrcf _preinc2,F movf _postdec2,W rrcf _indf2,F ;divide by 2 for a total division of 8 movf _postinc1,W btfss _postdec1,7 ;check if total was negative again bra AV_SAMPLES_NEXT comf _postinc2,F ;invert back to negative if the original total was negative comf _postdec2,F movlw 1 addwf _postinc2,F clrf _wreg,F addwfc _postdec2,F AV_SAMPLES_NEXT: movlw 2 addwf _fsr1l,F ;move the aTotals pointer along to next int movlw 0 addwfc _fsr1h,F movlw 2 addwf _fsr2l,F ;move the aSensors pointer along to next int movlw 0 addwfc _fsr2h,F movlw NEXT_ROW_OFF addwf _fsr0l,F movlw 0 addwfc _fsr0h,F ;move to the next row of samples for next channel decfsz _nChannel,F bra AV_SAMP_LOOP ;do next channel movlw TAP_MASK incf _nSamplePos,F incf _nSamplePos,F andwf _nSamplePos,F ;increment and mask nSamplePos to do circular buffer } } void main() { unsigned char i; int k; //value we're putting in the sensors array. Note when it goes above/below +-4096 the code explodes int n; //a counter to see how many passes we've done k = 0; n = 0; while (1) { for(i=0;i<SENSOR_CHANNELS;i++) aSensors[i] = k; k = k - 1; n = n + 1; av_samples(); } }
  14. Hi, I've got a nice little asm code adding sensors, performing array totals etc and doing everything up to the dividing by 8 to take the average. Rather than figure it out for myself, I thought I'd just re-use the code that sourceboost generates to do a divide by 8 on a signed int. However the results were not as expected. eg, compile and step through this in the debugger int k; int z; k = 0; z = 0; while(1) { k = k - 1; z = k / 8; } for different values of k, I get the following results for z k=-1,z=-1 k=-2,z=-1 k=-3,z=-1 k=-4,z=-1 k=-5,z=-1 k=-6,z=-1 k=-7,z=-1 k=-8,z=-1 k=-9,z=-2 etc k=-16,z=-2 k=-17,z=-3 etc etc Am I missing something here.....it looks like the optimisation to do a divisor that is a multiple of 2 isn't handling signed ints very well. I would assume for k=-1...-7 -> k/8=0 , k=-8...-15 -> k/8=-1, k=-16...-24 ->k/8=-2 etc Does anyone know asm code sequence that will do a signed int division by eight properly? It works for k>0...I don't want to add extra code to check the sign and artificially add one to correct the division. Phil
  15. Thanks for looking into that Pavel, I've got enough to go on with....now I've got to incoorporate this into a single routine that will take a set of eight samples, making this an eight tap finite impulse response filter with two byte precision.
  16. Thanks for the responses....the idea of keeping a running track of the total sum, and removing it before adding the new value is great...saves needing code to sum over the array, plus saves time....(got plenty of RAM left, very tight on ROM). Does new value insertion and summation in one step as well. Thanks for the input Pavel, that's an interesting approach....however I'm wondering how future proof it will be. It assumes the FSR register is maintained after the array access...also you don't specify which indf register to use, indf0, indf1 or indf2 (for PIC18F4550). No biggie, could just add some extra code to explicitly load the FSR. eg unsigned int *ptrSamplesEnd; unsigned int aSamples[8]; unsigned int *ptrSamplePos; unsigned int nTotalSamples; ptrSamplesEnd = &aSamples[8]; //point to one value over the end of the Samples array nTotalSamples -= *ptrSamplePos; //remove the old sample *ptrSamplePos++ = nSample; //add the new one if (ptrSamplePos = ptrSampleEnd) ptrSamplePos = aSamples; or in asm (note I haven't checked this....just coding off my head here, hope I got my high/low order bytes right) void add_sample(unsigned int nSample) { asm { movf _ptrSamplePos+1,W movwf _fsr0h movf _ptrSamplePos,W movwf _fsr0l ;set up the FSR movf _postinc0,W subwf _nTotalSamples movf _postdec0,W subwfb _nTotalSamples+1 ;do int subtraction, subtract first low byte, then high byte with borrow. ;Use postinc/postdev to get fsr back to the array element position we want movf _nSample,W movwf _postinc0 movf _nSample+1,W movwf _indf0 ;we could do the comparison below on fsr0h and fsr0l using another postinc....but we'd have to load it back to ptrSamplePos anyway, might as well increment ptrSamplePos directly infsnz _ptrSamplePos,F ;increment low byte of ptrSamplePos incf _ptrSamplePos+1 ;and increment the high byte if the low byte went 0xff->0x00 movf _ptrSamplePos+1,W ;now see if we've pointed off the end of the cpfseq _ptrSamplesEnd+1 bra finish_add ;not equal so ptrSamplePos+1<>ptrSamplesEnd+1 movf _ptrSamplesPos,W ;if we're here, high bytes were equal...so we check low bytes cpfseq _ptrSamplesEnd bra finish_add ;no so we finish movf _aSamples+1,W ;ptrSamplePos high and low were equal to ptrSamplesEnd (high and low)...so we reset it to start of array. movwf _ptrSamplesPos+1 movf _aSamples+1,W movwf _ptrSamplesPos finish_add: } //now nTotalSamples is the sum of all ints in the array aSamples and we can do our average correctly. } question? could we have used the movff opcode in some places above eg movff _aSamples+1,_ptrSamplesPos+1 movff _aSamples, ptrSamplesPos would still take four cycles, but might be clearer than fiddling around with the W register.
  17. Hi Reynard, Thanks for the response. I've been fiddling around getting my headspace back into assembler mode. Firstly, I'm not sure there is a memmove opcode for the PIC18F4450....I'd love to be wrong about this, in the meantime I'll stick with the circular buffer technique. The shift right for divisors of multiples of 2 I'm already up on.....in fact if you look at the opcode created by sourceboost it'll optimise divisions by doing multiple shifts anyway. Not sure about right shifting signed ints....I think you need to preserve the high order bit. In any case for 2 byte ints with 18F4450 you need to do two byte shifts, making sure on the high byte you clear the carry, rotate right through carry so the low order bit gets saved in the carry then rotate right through carry on the low order byte (with the saved carry). The indirect addressing vs paging isn't an issue I've found out by reading the datasheet. I'll assign the address of the array to one of the FSRx pointers and use the POSTINCx register to access the elements. From the datasheet the INDFx, POSTINCx, PREINCx...etc registors all use 12 bit addressing......so paging isn't a problem. Here's a trick I found when trying to make my code more efficient. I originally had code simliar to: nSensorX = read_sensor_adc(SENSOR_NUMBER) ;//which does an ADC read on the sensor specified and returns an unsigned int. aSensorX[circular_buffer_pointer] = nSensorX; or aSensorX[circular_buffer_pointer] = read_sensor_adc(SENSOR_NUMBER); ///where aSensorX is an array all well and good...however with lots of sensors you end up with lots of code being generated the does array handling....bloating code out as each array access gets handled seperately. I found it more efficient (though less readable) to re-write my sensor reading function to use a pointer void read_sensor_adc(unsigned char nSensorNum, unsigned int *dest); .... read_sensor_adc(SENSOR_NUMBER, aSensorX+circular_buffer_pointer); this way code is only generated for array/pointer access once in the function and doesn't need to be repeated for each sensor. Only useful if you are going to put your values in an array. Phil
  18. Hi, I'm looking for a bit of help to save some time re-inventing the wheel. I'm sampling a number of sensors, three accelerometer, three magnetometer and two ADC channels. The ADC and Accelerometer channels give me unsigned int data and the magnetometer gives me signed ints. I have a PIC18F4550 I'd like to store these in a table, and do a moving average filter on them. eg: int aMagXArray[8]; //allocate arrays of 8 values each //.... //Now sample the samples, putting them in variables like SensorMagX, SensorMagY etc aMagXArray[nPos] = SensorMagX; aMagYArray[nPos] = SensorMagY; etc nPos++; nPos = nPos & 0x07; //ie keep nPos from 0..7 then report the moving average eg: nTot = 0; for(i=0;i<8;i++) nTot = nTot + aMagXArray; nMagXAveragedValue = nTot / 8; etc repeat for the other seven sensors ...a pretty standard moving average alogorithm, store the samples in a table with a rotating pointer and average the table when you want a value Unfortunately all this array referencing is taking a big chunk of code space, which I don't have due to all the other code in my PIC. Using pointers isn't much help either. So I'd like to re-write this in assembly. It's been a while since I've played with indirect addressing, hence this post. Is there a 18F4550 assembly algorithm that will sum integers (unsigned or signed) that are sitting in an array? The sum can then be divided to give the moving average. Presumably I'd have to allocate the arrays in a single page to simplify things. Does the Sourceboost compiler automatically ensure that arrays are allocated in one page? Thanks for any help. Phil
  19. I recently had the same problem, in my case I was buffering a 256 ints from the AD port which made 512 bytes....so it does happen sometimes. Got around it by being less fussy about precision but would have liked a big size array. Possibly one solution would be to write your own array access primitives (ie your own functions) using the FSR registers you've mentioned, although I haven't looked into it myself. I would suggest googling 16 bit addressing for PICs as I imagine this has been addressed before. Phil
  20. Hi, Before writing my own code, just thought I'd check to see if anyone has done this before. I'm after some routines that will take a memory image from external EEPROM and write to the program memory. The idea being that the application will receive a .hex file (or similiar), and write it to the EEPROM, do some checks to make sure its valid then write that EEPROM memory image to the flash program memory. This is so the application can be updated in the field. Note this is NOT a bootloader because: - the program data is first loaded onto onboard eeprom not directly to program memory - there is no facility in hardware for the application to start in either bootload or application mode by pressing a button or other action at powerup - bootloaders require a reasonably tech savvy person to operate, not good enough for end users - I want the code to be received in a certain protocol that gives feedback to the user if successful or not plus maybe some simple encryption. I don't want the routines to worry about receiving the data or be restricted to serial ports, io pins or whatever else people have come up with. Basically I'm only after routines that take the eeprom image and write to application memory...everything else I'll do. Has something like this been written? Don't want to re-invent the wheel if necessary. If not, then can I get a sanity check on the following requirements before I start: - the 'image writer' would have to be in a protected block as set by configuration bits - how do I get my updated application code to 'org' at a non protected block? Do I need to use -rb linker option? Is there an 'org' directive in Sourceboost? - if I use the first protected block for the image writer, how does this affect interrupt and reset vectors. Will they be fixed? - if I need to protect the first block, and the interrupt/reset vectors are fixed, how then do I make sure my interrupt routines always start at the same place? - will writing to a protected block result in a error that needs to be caught, or can I simply ignore it safe in the knowledge the image writer wont write over itself? Will I need to make it simply ignore writes to the protected block (in which case we wouldn't need protection I suppose). - when the app code gets the goahead to update, how can I make sure I can call the image writer code in the protected block at the pre-defined address? Phil
  21. Hi Ian, Thanks for writing this code.....some non Microchip (and associated licence issues) code that does CDC has been much waited for. I've been using a modified version of the Microchip CDC code for a while, and it's been quite aggravating I cannot release the code because of the Microchip license (search the forums for threads on this). I've managed to get the code to compile, though not run yet as my hardware is a little different from yours I think. If I may make some suggestions for the next release candidate to make it more general: - Have some way of turning off all serial port debug functions. In my system I don't have a RS232 serial port, and the pins are actually being used for something else. Debug functions should be able to be compiled or not compiled depending on a #define switch. - Implement the code as a library, so it can be included in the project as a block of code without complicating the actual implementation. In the library don't have any code specific to any other peripherals (like a serial port), specific interrupt timers or i/o pins unless #define switches are turned on. - Implement a USB receive buffer set of functions, including a cdc_kbhit to check if a character has been received, but not extracting it from the buffer (helps in user interfaces). - allow the option of non blocking and blocking cdc functions. For example when transmitting cdc data, the characters go out to a buffer and get sent automagically by interrupt. - bundle any debug, bus power, blocking etc #define switches plus the PID/VID codes into a seperate identifiable file. - If possible have the rx/tx buffers user definable in the above bundle...some applications need to transmit long blocks, sometimes dont - have flushing functions for the buffers, sometimes a user might abort a long download - as a stretch goal, use the usb interrupts rather than a timer based interrupt approach, freeing up timers for other uses. In a library based implementation may I suggest the following approach: 1) Have an error code function to return status of the USB, connection/disconnection etc instead of debug to serial port 2) Have an error code function to return the status of USB buffer rx/tx buffers, buffer overflow, empty, length of data etc instead of only serial debug 3) de-couple your interrupt based functions, so a simple call from an interrupt can do USB servicing. This allows the user to use their own interrupt functions, for example in my application I need some A/D operations to be done as a higher priority to the USB servicing....I'd prefer to setup my own interrupt functions. Some users may wish to call the cdc service function manually in a main loop, or turn on/off their usb activities depending on whether a cable is connected to save power (I sense the 5V pin via a resistor divider, and only turn on the USB stuff when needed). 4) some functions to turn off/on usb cleanly (my code is a bit iffy here) As a start I've attached a stripped down set of files. The files cdclib.c and cdclib.h are in the library and exposed to the user, based on the modified Microchip code mentioned above. The rx part of the code is a bit mangy sorry...but I hope you get the idea. The other two files show an example using the library. My library also has a bunch of functions for making menus and navigating them in hyperterminal which you might see in the attachment. The app can be controlled by hyperterminal, or by a custom host program but usually runs standalone (its a data logging application). If you like PM me and we can work on a library based implementation together. My goal is a black box library that hides all the complexities and lets me concentrate on the application only. I just need a CDC engine to drive it, preferably a non Microchip implementation so it can be shared. Phil cdclib.zip
  22. Hi, A long time ago Geoffrey Hopkins did an amazing job of porting the CDC code to Sourceboost and I helped in packaging it up. Unfortunately when we contacted Microchip they said we weren't allowed to publish the code....which is a real shame. You're allowed to modify the code and use it yourself or in your own products, but not publish any modified code apparently. Having said that the main problem was trying to simulate bit field structures in Sourceboost, to this day I still cannot figure out how Geoff did it, but it wasn't pretty. It also meant some version dependant code, as an example I've been unable to use Sourceboost compilers beyond version 6.60, and we're now at version 6.86! In order to do a reasonable port of the CDC code and all the other USB code (such as their newly released host stack) we'll need bit fields in structures. Structure bit fields are all over the Microchip C18 code. Currently according to the forum this is a low priority on Dave/Pavels list of things to do......can you guys bump it up the priority list please?? Once this has been done, we should be able to do a simple port and publish a conversion guide or diff file which would circumvent the Microchip licence limitations. Perhaps a bit of lobbying by Sourceboost users direct to Microchip and they'll lift the restriction. Alternatively although the Microchip CDC code looks complex, it is actually simple in operation, however does need someone more familiar with USB protocols than me to re-write it in Sourceboost, rather than a port. Phil
  23. Hi all, Just been doing some sleuthing about the net and reading up on the datasheets. Here is some code fragments that may be suitable for solving the page write and acknowledge polling problem. Note that this is set for the 24LC1024....don't know what other chips page sizes are. There is more generic code out there....this is just to show a rough solution. Firstly I looked at the i2c_driver.h code....I noticed that the hardware implementation for the i2c_write function returned the error condition of the i2c transmit buffer (ie any write collisions) and not the ACK status of the i2c device. The software implementation returned the ack status. This threw me for a bit. Rather than change the existing i2c_write code. I added a new function to the i2c_driver.h file. Firstly the helper function definition //////////////////////////////////////////////////////////////////////////// // Helpers that hide template arguments //////////////////////////////////////////////////////////////////////////// ....etc #define i2c_check_ack i2c_CHECK_ACK<i2c_ARGS> Then the new function //////////////////////////////////////////////////////////////////////////// // Generates the I2C Bus Write Condition //////////////////////////////////////////////////////////////////////////// _I2C_TEMPL unsigned char i2c_CHECK_ACK(unsigned char i2c_data) { volatile unsigned char i2c_SSPBUF@T_i2c_SSPBUF; volatile bit l_scl@T_SCL_PORT.T_SCL_BIT, l_sda@T_SDA_PORT.T_SDA_BIT; volatile bit l_scl_tris@T_SCL_TRIS.T_SCL_BIT, l_sda_tris@T_SDA_TRIS.T_SDA_BIT; volatile bit l_bf@T_i2c_SSPSTAT.i2c_BF, l_ackdt@T_i2c_SSPCON2.i2c_ACKDT; volatile bit l_sspif@T_i2c_SSPIF_PIR.T_i2c_SSPIF_BIT, l_bclif@T_i2c_BCLIF_PIR.T_i2c_BCLIF_BIT; volatile bit l_rw@T_i2c_SSPSTAT.i2c_RW,l_wcol@T_i2c_SSPCON1.i2c_WCOL; volatile bit l_rcen@T_i2c_SSPCON2.i2c_RCEN, l_pen@T_i2c_SSPCON2.i2c_PEN, l_sen@T_i2c_SSPCON2.i2c_SEN; volatile bit l_rsen@T_i2c_SSPCON2.i2c_RSEN, l_acken@T_i2c_SSPCON2.i2c_ACKEN; volatile bit l_ackstat@T_i2c_SSPCON2.i2c_ACKSTAT; //NOTE! This is added for checking ackstat char BitMask; bit local_ack; l_bclif = 0; // initialise the collision flag for this command l_sspif = 0; // clear the operation completed // Hardware I2C implementation while (l_acken || l_rcen || l_pen || l_rsen || l_sen || l_rw) if (T_MODE & i2c_reset_wdt) clear_wdt(); l_wcol = 0; // clear write collision flag i2c_SSPBUF = i2c_data; // test if a write collision occurred....note not sure how to handle this as well as return ackstat //if (l_wcol) // return (1); // error exit // wait until MSSP Tx register is empty while (l_acken || l_rcen || l_pen || l_rsen || l_sen || l_rw || !l_sspif) if (T_MODE & i2c_reset_wdt) clear_wdt(); return (l_ackstat); //return the ackstat bit //Software implementation omitted for clarity (also I didn't need it) } So calling i2c_check_ack will write the given data to the i2c device and return the ackstat bit. To use the function I had the following code. Again this isn't generic, and is custom to my app. It basically uses a global long variable addCurrentLog to keep track of the current address in a big log of data. The code writes a given number of bytes to this log, and sets this address to the next available address. void devWriteLogBytes(unsigned char *buf, unsigned char nBytes) { unsigned char nControlByte; unsigned char Addressh; unsigned int Addressl; unsigned char nBytesWritten; unsigned char nPagePosition; nControlByte = xee_slave; //the old favourite for eeprom devices, xee_slave = 0xa0 while(nBytes>0) { Addressh = addLogCurrent >> 16; //The use of Addressh, Addressl, nPagePosition is a bit clumsy here. Addressh is byte 2 of the three byte address Addressl = addLogCurrent & 0xffff; //addressl is bytes 1 and bytes 0 of the three byte address nPagePosition = Addressl & 0xff; //this is the position in the low 256 bytes if(test_bit(Addressh,0)) set_bit(nControlByte,3); //This is the block number if(test_bit(Addressh,1)) set_bit(nControlByte,1); //This is the device number, I'm using 4 24LC1025 devices on the same I2C bus if(test_bit(Addressh,2)) set_bit(nControlByte,2); //you put the 2 bits hardware address for the max 4 24LC1025 devices here i2c_start(); i2c_write(nControlByte); i2c_write(Addressl>>8); i2c_write(nPagePosition); //this forms the low byte address of the address word nBytesWritten = 0; while(nBytes>0) { i2c_write(*buf); buf++; nBytes--; nBytesWritten++; nPagePosition++; if ( (nPagePosition==0x80) || (nPagePosition==0x0) ) { break; //Only do byte writes within a page boundary...which is 0x80 bytes for the 24LC1024 } } i2c_stop(); //ACKNOWLEDGE POLLING //Now send the exact same control byte as the read operation and wait till the ack comes back while(1) { i2c_restart(); //Don't use i2c_start here if (!i2c_check_ack(nControlByte)) break; //stop polling when the ackstat bit goes low. Note you must use the same control byte as the write operation }; i2c_stop(); //delay_ms(5); ------Don't need to do this, Acknowledge Polling will delay whatever the device needs addLogCurrent = addLogCurrent + nBytesWritten; addLogCurrent = addLogCurrent & 0x07ffff; } } For some reason when I used i2c_start when doing the last bit there, it didn't work. Settling for i2c_restart made it all OK Hope this helps, perhaps the Sourceboost team can incoorporate this into a more generic library for all EEPROM i2c device users, not just the 24LC1025. I presume the single byte write operation could be re-written to use Acknowledge Polling rather than some arbitary delay. Still to come, making sure block reads are not done over device block boundaries (as opposed to page write boundaries). Phil
  24. Hi All, I've found an issue regarding talking to one or more I2C eeprom devices using multibyte read/writes. I'm using SB6.60 with a 18F4550 and talking to four 24LC1025 EEPROM devices connected to the I2C bus. According to the 24LC1025 datasheet you cannot write across page boundaries, nor more than 128 bytes in one block write. So if first you wrote 120 bytes starting at zero, fine, and then you wrote another 20 bytes, you would not perform a successful write because in the second operation you would be writing over a page boundary. You would need to make three operations. The first 120 bytes, then 8 bytes and then the remaining 12 bytes. Additionally you cannot read over device block boundaries. For the 24LC1025 it is divided into two blocks 0x00000...0x0FFFF and 0x10000...0x1FFFF. Thus a read of 16 adresses from 0x0FFF8 to 0x10007 will fail unless again it is split across the boundary, ie one read of 8 bytes from 0x0FFF8 to 0x0FFFF and another from 0x10000 to 0x10007. Note the sample i2C code that comes with Sourceboost doesn't take this into account, and in any case the block/page boundary problem will be different from different EEPROM devices. This little issue might be of interest to those having troubles writing logging software to one or more EEPROM devices.....check your datasheets! So some questions: 1) Has anyone written I2C EEPROM code that does take this page/block boundaries into account when doing block operations? I'll chugalug along and maybe post something....but would rather have some already tried and tested code if there is any about? 2) After a write operation, particularly a block write operation, the master needs to wait a time before performing another write. I'm currently adding delays (for 24LC1025 I'm using a delay of 5ms) worked out by trial an error. However an alternative faster method is to use Acknowledge Polling....where you send an additional identical write sequence (to the one used to start the block write) but check if the ACK comes back from the device. If the ACK doesn't come back then the device is busy. You keep repeating until you get an ACK then do the next write sequence. From what I can tell, this is different to checking if the i2C bus is idle. From a quick read of the I2C code in sourceboost it looks like the I2C code sits in an infinite loop waiting for an ACK from the device during a write....any ideas on modifying the library to perform a wait till write complete sort of function? This can avoid the need for arbitary delays....simplifying code and increasing performance. FYI info on Acknowledge Polling is on Section 7 of the 24LC1025 datasheet, page boundary stuff in Section 6 and Sequential Read limitations in Section 8.3. Phil
×
×
  • Create New...