And if your main loop doesn't complete in time to poll the "interrupt" pin???
The main loop handles non time sensitive processing, it doesn't "poll" the interrupt. It post processes data from the interrupt.
In my case the main loop was responsible for making the data useful to another machine and transmitting it. The UART could only do 57600, so the main loop was pretty much I/O bound. It would pull data from a few variables, put it in a structure, then transmit the structure. Once per second it would send a sync packet with information that is only useful once a second. I also bit packed the 10bit ADC data into 16 bit words IIRC, packing 80 bits into 5 words.
My point is that with an interrupt driven architecture, you have to make a lot of educated guesses, compromises, or rewrite large portions of code if either of those methods don't work.
With the prop, you simply write in a black box, without concern for any outside processes, it greatly simplifies real-time processing.
One way I save cores is to use counters where ever I can to monitor events that need me to do something right now but do need me to notice they happened. I have some light sensors I made with a 555 timer. Rather than eat up a core to sit and sample the frequency over and over I just hook that into the A and B timers and boom I get my count and as long as I know the frequency I check them at I can come up with the frequency of this sample. I implemented a seconds timer that way in my RTC object so again I can time events out to 4 billion seconds using the 1Hz output but don't eat a core watching that pin toggle.
My dumb question. Is there any way to detect a reboot without wasting a pin? What I'm referring to is this. On reset, the prop loads it's program from eeprom. I want to share these pins using a 4066. I can use the reset pin to detect a hardware reset, but what I'm looking for is a way to detect a software reset. Any ideas?
I've read through the the counter app note. Fantastic reading. And for the most part, it all makes sense to me. The diagram is simple enough and NCO/PWM mode looks extremely simple. So why do I feel like a retard? Here's my code that isn't working. It's being run on the Parallax QuickStart:
CON
_CLKMODE = XTAL1 + PLL16X
_CLKFREQ = 80_000_000
PUB Main | Apin, Bpin
Apin := 16
Bpin := 0
dira[Apin] := 1
repeat 8
!outa[Apin]
waitcnt(_CLKFREQ / 8 + cnt)
frqa := 54 'Set FRQA so PHSA[31] toggles every second
ctra := long[%00100 << 26 + Bpin << 9 + Apin] 'Establish mode and APIN (BPIN is ignored)
repeat
Can you explain what exactly was "confusing hub ram?
You lost me there. long[addr] reads a long from hub address addr & -4. In your case - with Bpin being 0 and the counter mode being out of range for addressing - you end up reading from address 16 (Apin) which is $00020040. Assigning that to counter A leaves you with a disabled counter. HTH
IOW, reg := expr and reg := long[expr] are two different things.
The counter's LED on pin 16 doesn't fire. I can get the counter to work in spin, but not PASM.
{{ blinky_asm.spin
Project: Learning
Author: David Zemon
Purpose: Blink two LEDs, one with the built-in counter,
another with an asembly loop }}
CON
_clkmode = xtal1 + PLL16X
_CLKFREQ = 80_000_000
_ToggleLED = 18
_Apin = 16
_Bpin = 17
PUB Start(parameterAdr) : okay
okay := cognew(@CtrToggle, parameterAdr)
DAT
ORG $0
CtrToggle mov dira, ToggleLED ' Set output pins
{ Setup for counter module }
or dira, A_GPIO
or dira, B_GPIO
mov frqa, Frequency
movs ctra, Apin
movd ctra, Bpin
movi ctra, CtrMode
{ Infinite loop toggling LED w/ software }
:Toggle mov Time, cnt
add Time, qrtr_second
:Loop xor outa, ToggleLED
waitcnt Time, qrtr_second
jmp #:Loop
{ Constants for hardcoding a blinking LED }
ToggleLED long |< _ToggleLED
half_second long _CLKFREQ/2/2 ' Length of blink
qrtr_second long _CLKFREQ/4/2
tenth_second long _CLKFREQ/10/2
custom_delay long 15
Time res 1
{ Constants for using the counter to blink an LED }
CtrMode long 100_000
Frequency long 54
Apin long _Apin
Bpin long _Bpin
A_GPIO long |< _Apin
B_GPIO long |< _Bpin
If you use res (undefined variables) then any of them (in general) has to come after any long (defined variables/constants). So just move Time to the end of the code block.
I've been stuck on this problem since April. Moving that one line fixed it. Thank you again for the help and I guess now I know for next time... wish I'd posted that code a long time ago.
Still reading through that AN001. Lots of big words (or small acronyms lol) that I haven't seen before and am looking up. But, overall very cool.
Here's another question:
Why didn't Parallax implement interrupts, watchdog timer, or extra peripherals?
I understand it's nice that we have 8 cores to utilize, and we can still do a lot without interrupts... but we could do SO MUCH more if we had them. It seems silly that I need to use an entire cog to poll a pin and wait on input to do something while a second cog runs foreground tasks. This is something that can easily be done with a single cog if it had interrupts, allowing the other 7 cogs to do other useful tasks.
WDT: self explanitory. Why no WDT?
Extra peripherals: As a relative newbie to the uC world, I'm always looking at specs for other uC that I hear about. It seems like every other chip out there has many many more peripherals on it than the prop (with the exclusion of the timers & video generators). Why no ADCs? No built-in serial communication modules? No built-in ____? Again, this chip would become even more versatile if it had these.
I know the prop2 prelim says it will have ADCs. I'm very grateful for that. But still not adding the rest of it?
Are there any other uC out there that have all of these? I know ARM makes some multi-core processors, but IIRC they are in the $50 range.
Thanks,
David
There is another document ===> The Propeller counters, a dummies guide by Graham Stabler. Try to Google by this title and look for other items he may have written.
WDT? Would one have been enough or do you want 8? I can't really say why, but I suspect that since there were no interrupts, that excluded the WDT feature as well. One can live without one.
Peripherals? Well the Propeller was the first of its kind and usually such items are rather bare bones. The Propeller II is going to have ADC and DAC. Other peripherals can easily be replicated in software. This keeps the inventory of chips down and allow more resources going into development rather than selling overstocked silicon.
In the assembly section, i've commented out 6 debugging lines (the lines i'm referring to all have the apostrophe as the first character). When those lines remain as comments, my assembly routine dies. Or goes into an infinite loop. Or something. I don't know - that's why I inserted the debugging lines.
It's a heisenbug. When I uncomment those six lines, the bug is gone, and it runs perfectly. Does anyone have any idea why? Or better yet, more generally, how do YOU go about debugging heisenbugs??? This is my first one.
The code is the beginning of a tachometer.
I am also still new to prop code. If you have formatting suggestions or you think my code is hard to read, I'd like to hear it.
{{ Tachometer.spin Author: David Zemon
Date Created: 10/9/11
Date Modified: 1/24/13
Write engine RPM to 16x2 character LCD
}}
CON
_CLKMODE = XTAL1 + PLL16X
_CLKFREQ = 80_000_000
_TACH_PIN = 18 ' Which pin is connected
RS = 0
RW = 1
E = 2
DB0 = 8
DB7 = 15
TEST_OUT = 17
TEST_OUT_2 = 16
TICKS_PER_REV = 2 ' Number of rising edges on tachometer signal per revolution
ARRAY_SIZE = 64 ' Number of ticks to count in the average
LOG_SIZE = 6 ' LOG_2(DEFAULT_AVG)
OBJ
lcd : "LCD_16x2_8Bit_spin"
Var
long divisor
long tachCount
PUB MAIN | asmCog, rpm
' 0123456789abcdef
asmCog := cognew(@entry, @tachCount)
lcd.start(RS, RW, E, DB0, DB7)
if (-1 == asmCog)
lcd.str(string("Error. Unable to"))
lcd.move(1, 2)
lcd.str(string("start PASM cog."))
else
lcd.str(string("Tachometer"))
lcd.move(1, 2)
lcd.str(string("initializing..."))
lcd.clear
lcd.str(string("RPM: "))
tachCount := 0
repeat
waitcnt(CLKFREQ/4 + cnt)
rpm := tachCount
rpm := calculateRPM(rpm)
'if (99999 < rpm) or (0 > rpm)
' rpm := 0
lcd.move(6, 1)
lcd.str(string(" "))
lcd.move(6, 1)
lcd.dec(rpm)
PUB calculateRPM(tickCount) : rpm
rpm := CLKFREQ / tickCount
rpm *= TICKS_PER_REV * 15
DAT
ORG 0
entry waitpeq tachPin, tachPin ' Ignore first period
waitpeq zero, tachPin
mov wPtr, @array ' Set pointer to first address in the array
mov sum, #0
mov old, cnt ' Initialize old cnt value
sub zero, #1 wc, nr ' Set the C flag
or dira, testLED
' or dira, testLED2
{{ Wait for input to go high, store the time delay }}
loop waitpeq tachPin, tachPin ' Wait until pin goes high
mov new, cnt ' Save system count register
waitpeq zero, tachPin ' Wait until pin goes low again
mov count, new
sub count, old ' Calculate time (in osc tick) between ticks
mov old, new
{{ Subtract the old value, add the new one (only if array full) }}
if_nc movs decArray, wPtr
add sum, count
decArray if_nc sub sum, 0-0
{{ Store a new value into the array }}
movd storeCnt, wPtr ' Store the current pointer address into the move instruction
add offset, #1 ' Increment the pointer
and offset, #(LOG_SIZE - 1) ' Mod wPtr with ARRAY_SIZE
mov wPtr, @array
add wPtr, offset
storeCnt mov 0-0, count ' Store the count into the array
xor outa, testLED
{{ Add more values to the array if not full }}
loopCnt if_c add loops, #1
if_c cmp loops, #ARRAY_SIZE wc
' if_c xor outa, testLED2
if_c jmp #loop
{{ Turn off testLED2 }}
' xor testLED2, invert
' and dira, testLED2
mov temp, sum
shr temp, #LOG_SIZE
wrlong count, par ' Write to HUB RAM
jmp #loop
tachPin long |< _TACH_PIN
testLED long |< TEST_OUT
'testLED2 long |< TEST_OUT_2
zero long 0
loops long 0
'invert long -1
sum res 1
old res 1
new res 1
count res 1
array res ARRAY_SIZE ' Rolling sum array
offset res 1 ' Pointer offset
wPtr res 1 ' Write pointer to array
temp res 1
This is a thought-experiment, as I'm sitting at work and don't have a prop at hand.
I never tried one thing before:
...
mov hubaddress, # @array
rd/wrlong hubaddress, array
...
array long 0
hubaddress res 1
or the functional equivalent, but the assignment to hubaddress is done during compile-time
...
rd/wrlong hubaddress, array
...
array long 0
hubaddress long @array
From my point of view this should be possible, as array is placed in a DAT section (thus it is unique over all object instances) and exists in HUB-RAM and in COG-RAM.
Where array is located is known during compile-time for both, COG-RAM and HUB-RAM.
Your first code fragment won't do what you expect. Remember that the source field is only 9 bits and @array (the hub address of array) may be larger than that. In any event, RD/WRLONG works backwards from other instructions in that the destination field has the cog address of the data while the source field has the cog address of the hub address so you need "RDLONG array,hubaddress".
The addresses of the cog array and the hub array may be known at compile time, but they're still in separate address spaces. The hub array is copied to the cog array only once ... when the cog is initialized by COGNEW or COGINIT. From that point onwards, the two copies of the array are separate. You can always keep the hub copy updated by explicitly writing any changes from cog to hub (if the cog is the only one changing the hub copy). It's a mess if other cogs can also change the hub copy. At that point, you should just keep a hub copy and access that directly.
A prof just said that an oscillator must have a small capacitor on each side of the crystal to work. Without the capacitance, there is no C in the RLC circuit to make an oscillation. I've never capacitors on my crystals on breadboards, yet my circuits work just fine (for basic stuff like the above example and blinky code). Why is this? Does the chip have built-in capacitors on those pins?
(I'll try the above suggestions for my code when I get home)
Yes, the oscillator on the chip has small capacitors built into it. Some of this capacitance is inherent to the CMOS gates that make up the oscillator. Other parts of this capacitance are switchable in and out of the circuit since some crystals require more or less capacitance to oscillate. Some chip oscillators don't provide enough capacitance for many crystals and their datasheets show how much has to be added externally. The printed circuit traces also add some capacitance that has to be taken into effect. Some real-time-clock chips' datasheets show a specific layout of the traces between the crystal and the chip that needs to be followed for best accuracy and minimal power consumption.
Is memory in cog RAM byte addressed or long addressed? I realized I'm only incrementing my offset by 1, but I want to be incrementing to the next long address.
I changed @array to #array and that fixed that bug at least. I also fixed the "mod offset" brain fart.
Battery died on my function generator so I think that's the world telling me to get some sleep. I'll test this again tomorrow.
Good example is that, if you're copying longs between hub and cog memory (like from a parameter list or control block, you have to increment the cog address by 1 and the hub address by 4.
Having trouble understanding the SPI Assembly module: http://obex.parallax.com/objects/431/. I'm attempting to port it to C/GAS and the methods SHIFTOUT and SHIFTIN seem broken to me. They contain numerous unused parameters, which I can only guess are there for compatibility with the Spin version of the driver. However, SHIFTIN sets the value of a local variable, "Flag" to 1, and then waits for that variable to be cleared... but the address of Flag is never passed to another cog, it is never referenced anywhere else... I have no idea what's happening.
The call has the address of Dpin passed in. Flag is the same as Dpin[5].
PUB SHIFTIN(Dpin, Cpin, Mode, Bits)|Value,Flag ''If SHIFTIN is called with 'Bits' set to Zero, then the COG will shut
''down. Another way to shut the COG down is to call 'stop' from Spin.
Flag := 1 ''Set Flag
setcommand(_SHIFTIN, [COLOR="#FF0000"]@Dpin[/COLOR])
...
PRI setcommand(cmd, [COLOR="#FF0000"]argptr[/COLOR])
command := cmd << 16 + [COLOR="#FF0000"]argptr[/COLOR] ''write command and pointer
repeat while command ''wait for command to be cleared, signifying receipt
...
loop rdlong [COLOR="#FF0000"]t1[/COLOR],par wz ''wait for command
...
mov [COLOR="#FF0000"]address[/COLOR],t1 ''preserve address location for passing
...
Update_SHIFTIN
mov [COLOR="#FF0000"]t1[/COLOR], address '' Write data back to Arg4
add [COLOR="#FF0000"]t1[/COLOR], #16 '' Arg0 = #0 ; Arg1 = #4 ; Arg2 = #8 ; Arg3 = #12 ; Arg4 = #16
wrlong t3, t1
add [COLOR="#FF0000"]t1[/COLOR], #4 '' Point t1 to Flag ... Arg4 + #4
wrlong zero, t1 '' Clear Flag ... indicates SHIFTIN data is ready
This may also be of interest [post=1061378]SPI_Asm vs. SPI_Spin[/post].
Took me a minute to understand this... but are you saying that Beau Schwabe has hardcoded this thing to be super-optimized for these parameters written in exactly this order for this specific compiler??? Using address offsets to access parameters without ever passing values or addresses... that's crazy.
Also... sounds like it isn't directly portable to C as I believe GCC pass parameters very differently - especially when there are more than 3. Do you think I'd be better off re-writing the assembly in SPI_Asm or porting SPI_Spin instead? I don't have a "desired clock rate" because I don't know what my requirements are, but I am running a real-time-system and gathering critical information through SPI.
Took me a minute to understand this... but are you saying that Beau Schwabe has hardcoded this thing to be super-optimized for these parameters written in exactly this order for this specific compiler??? Using address offsets to access parameters without ever passing values or addresses... that's crazy.
That's the way parameters are passed to a cog. IOW one base address (you only have par availableA) and elements down the line are accessed by base+offset. Usually a block of consecutive VARiables is used. Applying this to parameters and locals only works for SPIN as the defined order is result/parameters/locals (i.e. this wouldn't necessarily work for other languages).
Do you think I'd be better off re-writing the assembly in SPI_Asm or porting SPI_Spin instead? I don't have a "desired clock rate" because I don't know what my requirements are, but I am running a real-time-system and gathering critical information through SPI.
Up to you really, how fast does it have to be? As for parameters, create a block of consecutive variables (e.g. a structure) then pass its base address to the cog.
A in this specific example par isn't used for the parameter array, its address is part of the command but the principle is the same
No. The outputs from each cog are OR'd together before going to the pin. What this means, though, is that if one cog makes the pin high (1), it doesn't matter what any other cog is doing, that pin will be high.
That info might work for me. I think it will allow me to stay with an eight-bit pattern for my two steppers instead of switching to a four-bit pattern whenever I want my steppers to operate differentially.
The motors, which are in separate cogs, call an object in a 3rd cog which has a repeat loop that tests 'combined conditions'. I think I have a contention problem and I've been working on it for at least a week. (My program works perfectly as long as I write the values myself as opposed to return values from a Ping.)
Comments
The main loop handles non time sensitive processing, it doesn't "poll" the interrupt. It post processes data from the interrupt.
In my case the main loop was responsible for making the data useful to another machine and transmitting it. The UART could only do 57600, so the main loop was pretty much I/O bound. It would pull data from a few variables, put it in a structure, then transmit the structure. Once per second it would send a sync packet with information that is only useful once a second. I also bit packed the 10bit ADC data into 16 bit words IIRC, packing 80 bits into 5 words.
My point is that with an interrupt driven architecture, you have to make a lot of educated guesses, compromises, or rewrite large portions of code if either of those methods don't work.
With the prop, you simply write in a black box, without concern for any outside processes, it greatly simplifies real-time processing.
IOW, reg := expr and reg := long[expr] are two different things.
the.
....
I've been stuck on this problem since April. Moving that one line fixed it. Thank you again for the help and I guess now I know for next time... wish I'd posted that code a long time ago.
There is another document ===> The Propeller counters, a dummies guide by Graham Stabler. Try to Google by this title and look for other items he may have written.
WDT? Would one have been enough or do you want 8? I can't really say why, but I suspect that since there were no interrupts, that excluded the WDT feature as well. One can live without one.
Peripherals? Well the Propeller was the first of its kind and usually such items are rather bare bones. The Propeller II is going to have ADC and DAC. Other peripherals can easily be replicated in software. This keeps the inventory of chips down and allow more resources going into development rather than selling overstocked silicon.
It's a heisenbug. When I uncomment those six lines, the bug is gone, and it runs perfectly. Does anyone have any idea why? Or better yet, more generally, how do YOU go about debugging heisenbugs??? This is my first one.
The code is the beginning of a tachometer.
I am also still new to prop code. If you have formatting suggestions or you think my code is hard to read, I'd like to hear it.
And this only works for powers of two. LOG_SIZE - not being one - gives you any number of side effects. Once you reach nc condition you'll stay there. Probably not what you want. Can you elaborate?
This is a thought-experiment, as I'm sitting at work and don't have a prop at hand.
I never tried one thing before: or the functional equivalent, but the assignment to hubaddress is done during compile-time
From my point of view this should be possible, as array is placed in a DAT section (thus it is unique over all object instances) and exists in HUB-RAM and in COG-RAM.
Where array is located is known during compile-time for both, COG-RAM and HUB-RAM.
The addresses of the cog array and the hub array may be known at compile time, but they're still in separate address spaces. The hub array is copied to the cog array only once ... when the cog is initialized by COGNEW or COGINIT. From that point onwards, the two copies of the array are separate. You can always keep the hub copy updated by explicitly writing any changes from cog to hub (if the cog is the only one changing the hub copy). It's a mess if other cogs can also change the hub copy. At that point, you should just keep a hub copy and access that directly.
(I'll try the above suggestions for my code when I get home)
I changed @array to #array and that fixed that bug at least. I also fixed the "mod offset" brain fart.
Battery died on my function generator so I think that's the world telling me to get some sleep. I'll test this again tomorrow.
Bean
Thanks!
Paul
Thanks again for steering me in the right direction!
Also... sounds like it isn't directly portable to C as I believe GCC pass parameters very differently - especially when there are more than 3. Do you think I'd be better off re-writing the assembly in SPI_Asm or porting SPI_Spin instead? I don't have a "desired clock rate" because I don't know what my requirements are, but I am running a real-time-system and gathering critical information through SPI.
How would you have done the parameter access?
Up to you really, how fast does it have to be? As for parameters, create a block of consecutive variables (e.g. a structure) then pass its base address to the cog.
A in this specific example par isn't used for the parameter array, its address is part of the command but the principle is the same
The motors, which are in separate cogs, call an object in a 3rd cog which has a repeat loop that tests 'combined conditions'. I think I have a contention problem and I've been working on it for at least a week. (My program works perfectly as long as I write the values myself as opposed to return values from a Ping.)