PWM Conversion to "________" <---- Insert data

Hi Kurte,

Yeah it does help,

I was thinking some sort of PWM --> 16 or 8 bit resolution value
0-255 or 0-65k’ish

Each one has its advantages and drawbacks "was wondering if anyone knew of a chip that could do this’

My goal is to wire up 4 channels off my 8 channel receiver and control the Phoenix with it "There are lots of features I don’t think I’ll need with Xan’s code’ and some I can leave on by default. The receiver I have is a R168DF Futaba… I wasn’t sure if the receiver you were using was a digital one as its been a while since I’m up to date on RC car goodies like every one of the spektrum modules with all these new and slick features…

square button = balance mode on or off … Probably keep that on as it works better

the triangle button = this would be pressed by default with the code starting …

raise and lower body = really don’t need this as the higher the body gets the worse the thing walks

R2 = for speedup 2x gait speed I think I would rather just make the analog stick control the speed so I don’t need an extra input to have 2x gait speed.

… and the translation ‘however cool looking not really necessary for my fiddling’

the only thing I would like to keep is the ability to adjust a couple of gait types there is around 2-3 of them i would use out of the 7 Xan has included in his code. "I have taken apart my 4 channel transmitter and it has some extra pins inside that would be interesting to probe around and see if i can get it to transmit a 5th channel I could rig up a switch how Xan did that on his transmitter for a 6 notch selection of sorts… "but that’s kinda beyond the scope of this post’

As for the things you have commented on the WTIMER/WKP interrupts – I’m not familiar enough with these coding objects to really say anything about them :slight_smile:

as for the pwm -> digital conversion I was thinking this would be handled by an external circuit powered off the battery I have made for the phoenix and as for speed, i think if it would be able to read pwm data and send out a value within 100ms that would be more then fast enough, with my idea of lobotomizing Xan’s code It might run faster with less features included in the code and more time to look all snazzy walking scaring the cats and all that crap.

I have no problem using assembly what so ever, I think its fantastic you wrote this since other people can use it and offer opinions and what not, and maybe find a way to speed it up if its possible, as for your comment on 2 full passes “to be stupid a second” why would 2 or x amount of passes make much of a difference? if the processor is fast enough, Id want it to be as fast as needed but not slow down the rest of Xan’s code to the point that it becomes visually apparent

side note* the only slowdown I have ever seen in xan’s code is the slight delays you see when changing gait types and raising and lowering the body around 1/4th of a second delay or the delay between things that cause the bb2 to beep when a key is pressed to change a feature.

so in terms of headroom I really have no idea how much there is left with xan’s code or my lobotomizing of his code will free up head room “not free code room on the atom pro but actual cpu cycles”

Do you have much documentation or a demo of just the assembly code I could paste into a new atom program and write some code to control some servo’s over the serial link to the SSC32

I saw on the RC forum you had it, but there was all sorts of extra stuff included in it and wasn’t sure where to begin or start snipping since I’m new to this stuff.

Hi again,

If it were me, I would start off with simple pulsin commands to get the 4 channels that you are interested in. When I talk about 2 cycles, what I mean is that the receiver sends out the servo pulses one after each other and then there is a pause and then it does this again. It does not depend on how many of the channels you are processing, you simply have to wait until the channel you are doing a pulsin on is low and then finally goes high and then low again. If you miss the start of it you have to wait for one complete cycle before you can try again. If you are doing a pulsin command, it will sit in this command until this happens. Here is an example coming off of the spektrum receiver:
http://i416.photobucket.com/albums/pp245/Kurts_Robots/Spectrumreceiver.png

Note, I should have captured one with multiple cycles, to show this better, but I now have it set up for PS2 testing… But you should notice that we have not even started the second pass in this complete trace so if you miss a signal you may need to wait awhile.

While the Spektrum type transmitter/receivers may use digital stuff, in the end it still generates RC servo pulses like the older analog ones… What would be nice is if they had a digital receiver, which you could simply interface to, through probably I2C or the like.

There are much better hardware experts up here, but my guess is the easiest hardware solution is with a processor, which would requires some code to be written for it.

It is great that we all have features or things that interest us and many of us try to share as much as we can. This allows all of us to to pick and choose where we want to spend our time.

As for the assembly code. This is my current test program for the receiver side. Note, it is not fully tested yet. It appeared to work with my default configuration of the first 7 pins. It would be nice now to reconfigure my bot with other pins, which free up some analog pins and don’t eat into the hardware serial port, etc…

[code];====================================================================
; DIY - Radio Receiver Test Program
;
; Currently this program assumes all 7 inputs are hooked up to P0-P6
; TBD - Make more general purpose, to allow different input pins.
; For P1-P7 change mask from 0x7f to 0xfe and make pulse_values word(8)
; For P8,10-15 change mask to 0xFD, PDR5 to PDR8, and pulse_values to word(8)
; BUGBUG - Need to check how PCR5 and PMR5 are used…

;----------------- Variables used in MPulsin function --------------------------
awPulsesIn var word(16)
wPulseError var word
wPulseMask var word

;----------------- Other variables --------------------------------------------
awPulsesPrev var word(16)
fPulseChanged var bit ; we had a channel change
fNoTransmitter var bit ; did we detect no transmitter…
fLostTransmitter var bit ; We lost contact with transmitter

i var byte
keypress var byte
pulse_slop con 2

;--------------------------------------------------------------------
; Init code
;====================================================================
;--------------------------- Init needed for MPulseIn -----------------
; Make sure all 7 pins are marked for input…
input p0
input p1
input p2
input p3
input p4
input p5
input p6

wPulseMask = 0x7f

;----------------------- other init used to show if transmitter was lost, etc -----------------------------------
; Specific to the DIY receiver code
fNoTransmitter = 0 ; assume we have a transmitter.
fLostTransmitter = 0

start:
gosub MPulseIn

; if we get a timeout and it is channel 0 then we now the receiver is not on...
if (wPulseError) then
	; we detected no tranmitter at startup...
	if fNoTransmitter = 0 then
		serout s_out, i9600, "No Transmitter at power up(",hex wPulseError, ")", 13 ]
		fNoTransmitter = 1
	endif
elseif awPulsesIn(0) < 950
	; On Bind we set channel 1 to about 800 normal is usually > 1000 so if under 950 
	; we probably lost our connection with the transmitter
	if fLostTransmitter = 0 then
		serout s_out, i9600, "Lost contact with transmitter", 13]
		fLostTransmitter = 1
	endif

else
	; Ok we hamve a message so reset our error codes
 	fNoTransmitter = 0		; assume we have a transmitter.
	fLostTransmitter = 0

	fPulseChanged = 0
	for i = 0 to 6
		if (awPulsesIn(i) > (awPulsesPrev(i)+pulse_slop)) or (awPulsesIn(i) < (awPulsesPrev(i)-pulse_slop)) then
			fPulseChanged = 1
			awPulsesPrev(i) = awPulsesIn(i)
		endif
	next
	
	if fPulseChanged or wPulseError  then
		if awPulsesIn(6) < 1135 then  
			keypress = 0xff
		else
			keypress = (awPulsesIn(6)+15 - 1150) / 50  ; add in some slope as we could be slight
		endif
  
		serout s_out, i9600, [hex wPulseError, ":", hex keypress, ":" ]
		for i = 0 to 6
			serout s_out, i9600, [dec awPulsesIn(i)," "]
		next
		serout s_out, i9600,[13] 

		if keypress = 0xA then 
		  low 12 
		else 
		  input p12 
		endif 
		
		if keypress = 0xB then 
		  low 13 
		else 
		  input p13 
		endif 
		
		if keypress = 0xC then 
		  low 14 
		else 
		  input p14 
		endif 
	endif
endif

goto start

;==============================================================================
; Read in Multiple pulse widths in one pass. This function will not return
; until all of the pulses requested have been returned or a timeout happens.
; If there is a timeout it will return the mask of those items that were not
; returned. Zero if all were returned.
; Parameters/Variables Used.
; wPulseMask - Input: the word mask of which bits to retrieve
; awPulsesIn - Output: Word Array to receive the pulses from the different IO pins.
; the size of this array must be at least the highes pin number
; used in the mask+1 as we are zero biased.
; wPulseError - Output: if zero all were returned else the bits set as one
; were the ones that were not returned due to timing out.
;
; BUGBUG: it is assumed that all of the IO bits that are set to be retrieved are
; already configured as input IO pins. Could add code in to set them properly…
;
;-------------------------------------------------------------------
MPulseIn:
; Make sure all 7 IOs are set to input.
; PMR5 = 0 ; all IO lines are general IO
; PCR5 = 0 ; All are input (may want to leave bit 7 alone…

; Ok now lets transisiton to assembly language.
;

; bMask = 0x7f ; set a mask of which bits we are needing…
;
mov.w @WPULSEMASK, r1 ; R1 contains the mask of bits left to be retrieved.

; wait until none of the IO lines are high... 

; while PDR5 & bMask
; ;
; wend
mov.l #250000,er2 ;(4) - setup timeout counter
_PI7_WAIT_FOR_ALL_LOW:
mov.b @PDR5:8, r0l ; R0l is the state of IO pins 0-7
mov.b @PDR8:8, r0h ; r0h is the state of IO pins 8-15

and.w	r1, r0			; see if any of the IO bits is still on...
beq		_PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:8	; all zero go to wait for one to go high...
dec.l	#1,er2							;(2)	
bne		_PI7_WAIT_FOR_ALL_LOW:8	; an IO pin is high and we have not timed out, keep looping until none are high
; We timed out waiting for all inputs to be low, so error out...
bra		_P17_RETURN_STATUS:16			; will return status that all timed out...

; while bMask

_PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:
mov.l #250000,er2 ;(4) - setup timeout counter

_PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH2:
; we still need some 1 or more of the pulses…
; while (PDR5 & bMask) = 0 ; waiting for a pulse to go high.
mov.b @PDR5:8, r0l ;(4)
mov.b @PDR8:8, r0h ;(4)
and.w r1, r0 ;(*2) see if any of the IO bits is still on…
bne _P17_IO_WENT_HIGH:8 ;(*4) One went high so go process
dec.l #1,er2 ;(2)
bne _PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH2:8 ; (4) Not a timeout go try again.
; we timed out…
bra _P17_RETURN_STATUS:16 ; will return status of what timed out…

; wend
; iPin = ???; TBD: convert which bit is high to IO line number 0-6
; see which bit is on in the mask
_P17_IO_WENT_HIGH:
xor.w r2,r2 ;(*2)
mov.w #1, r4 ;(*4)
mov.l #AWPULSESIN,er3 ;(*6)
_P17_WHICH_BIT_LOOP:
shlr.w r0 ;(@2)
bcs _P17_WHICH_BIT_LOOP_DONE:8 ;(@4)

shll.w	r4								;(@2)  - Set up our mask
inc.l	#2, er3							;(@2)  - point to the next place in the array.
add.w	#18,r2							;(@4)  - we do 18 clocks for each pass through this loop
bra		_P17_WHICH_BIT_LOOP:8			;(@4)

_P17_WHICH_BIT_LOOP_DONE:
; bMaskBit = 1 << iPin ; get the mask for which pin…
; ok R4 now has the mask for the bit that we are processing, clear it from our global flag.
xor.w r4, r1 ;(*2)

										; = (22) - count so far of clocks after went high Maybe 20 or 24

; iPinLoopCnt = 0 ; This may be replaced with time calculations…
; while (PDR5 & bMaskBit)
; iPinLoopCnt = iPinLoopCnt + 1 ; how long until it goes low again
; wend
_P17_WAIT_FOR_IO_GO_BACK_LOW:
mov.b @PDR5:8, r0l ;(#4)
mov.b @PDR8:8, r0h ;(#4)
and.w r4, r0 ;(#2)
beq _P17_IO_WENT_BACK_LOW:8 ;(#4)
add.w #22,r2 ;(#4) - number of cyles per loop
bcc _P17_WAIT_FOR_IO_GO_BACK_LOW:8 ;(#4)

; we had a timeout return the status.
xor.w	r4, r1							; Turn back on the one we timed out on.
bra		_P17_RETURN_STATUS:8			;

_P17_IO_WENT_BACK_LOW:
; need to calculate the pulse width in ms… need to divide calculated clocks by 16
add.w #22,r2 ; (4) ; need to add the rest of the overhead(*-1 loop above) in…
shlr.w r2 ; (2)
shlr.w r2 ; (2)
shlr.w r2 ; (2)
shlr.w r2 ; (2) / 16 (for clock speed)

; aPulses(iPin) = iPinLoopCnt ; convert loop count to pulse width…
mov.w r2,@er3 ; Save away the value

; bMask = bMask & ~bMaskBit ; turn off waiting for this one…
or r1,r1 ; (2) see if we are done or not
; wend
bne _PI7_WAIT_FOR_NEXT_IO_TO_GO_HIGH:16 ;(6)our mask has not gone to zero so wait for the next one.

_P17_RETURN_STATUS:
mov.w r1,@WPULSEERROR
; finally transisition back to basic and return.
return [/code]

In this code the main work horse is MPulsin. You simply do a gosub of it to read in the servo pulses. You also need the variables that are in the section marked as used by MPulsin. Also currently you need to do some initialization code, in particular you need to set the mask wPulseMask to which IO pins you wish to use, by setting the cooresponding bit in the mask. I only support pins 0-15 and have only tested 0-7 so far. The rest of the code in the test program simply prints out the results to the terminal and also has some specific code for our DIY spektrum to try to detect when the transmitter signal has been lost.

I hope that helps…

As for my next steps, I am torn between updating this code to use the same TimerW that we setup in the phoenix code to make sure that interrupts does not screw up our timings, or to integrate in the XBee intot he transmitter/receiver and get the first version of this up and running…

Kurt

humm that’s quite a bit of code to un mangle these signals! I’ll try and take it all in in a day or so once I have my servo wire adapters built to feed it back into the BB2.

As for that logic screen shot you have, it looks like it gets channel 5 then 7 then 6 ? was that the order you were talking about how the receiver puts out the signals?

I have a lot to learn here when I start to dive into this…

From what I know about coding here any time you want to grab the value that a channel is it you would put in your code a gosub to your assembly code then simply return to the point after the gosub with the return command? is this right?

I have an idea for your logic probe if you haven’t already done this clip 1 logic probe input on the output of your receiver then clip another logic probe on an output of the ssc32 to a servo then measure the delay between the two input pins to your logic analyzer and try new things with your code to see which code block has the least delay between the 2 input pins

I believe I will try your suggestion first and write something with the mplusin command and see where that goes! as for debugging… how do you run code on the bb2 and have the serial port from the computer still connected to the bb2 watching values??

First thing I would want to do is connect a signal channel have it process it and generate a value from it 750-2100 or whatever the servo max/min range is and watch these numbers change over a debug cable of sorts connected to the BB2 as i move the rc transmitter controls around… does this make sense to you?

–Aaron

Much thanks!

I was speaking with an electronics friend and he said at his job they use a circuit like this all the time

for this to work with the output of rc receivers the resistor or cap values would need to change, although this brings up a good point, atom code has a function called RCTIME Id imagine this would be a bit less complicated for the atom to process ? I could be wrong here.

The only problem I see is tolerance issues on the resistor/cap used so tweaking would have to be done if the values are skewed enough to not get the same voltage out on each output pin of that filter circuit.

So is it easy for the atom to read a voltage?

–Aaron

Yes it’s easy to read an analog voltage, but you’re wanting to read a PWM, pulse width modulation, the servos outputs are PPM, pulse position modulation. It’s not the same animal.

Robot dude Hi,

I guess your not familiar with this circuit, this circuit takes PWM and converts it into a voltage.

So as my thinking I’m guessing it would be easier for the atom to read a voltage rather then read PWM data with Xan or Kurt’s assembly code.

what do you think?

-Aaron

Ug… :unamused:

I understand the circuit perfectly. But it’s for reading PWM not PPM. PWM is pulse width modulation. It goes from 0 to 100%. PPM for a servo is 1-2mS on and 20mS off. So it’s never more than 5% to 10% on. This will give you a total range of 0vdc to .5vdc, not the 0vdc to 5vdc you are looking for.

Ahh ok so if that pwm signal did not have that 20ms off delay this circuit could probably be used. That 20ms off period would toss off the averaging of the Circuit.
As for what voltage you would get out, that would be adjusted with the resistor and the cap value to find a setup that works for you ???

that image was just to show the conversion from pwm to voltage it wasn’t to use those exact values…

I wasn’t sure where you got PPM from?

I’m probably missing something here.

–Aaron

Not trying to step on anyone’s feet here…

Aaactually servo control signals are PWM; they have a 20mS period with a standard pulse ranging from 1mS to 2ms, or an extended pulse ranging from 0.5mS to 2.5mS duration. This definition cmes from how the orriginal servo encoders worked where they could encode/decode up to 9 channels in a single 20mS frame. Here is a link to a popular data sheet for an LM1871 R/C encoder chip used back in the day, so to speak.

Here is where it gets confusing… The servos themselves are however forgiving enough that they allow a PPM signal with a constant 19 to 20mS off time and a pulse varying from 0.5 to 2.5mS to work.

The problem in using your R-C filter circuit is as described, the very short duty cycle combined with the low frequency makes it impractical to apply.

Ideally you can use a capture input of a microcontroller to measure the pulse duration in terms of counter pulses. I think some effort has already been spent in that direction in other threads on the forum.

An other option would be an integrator circuit where a capacitor is charged using a constant current source while the signal is high, then measured by an a/d converter when the signal goes back low. Once the voltage is read a transistor is used to discharge the capacitor until the next pulse arrives. the reason a constant current source is used is because the voltage across the cap increases linearly over time when a contant current is applied. The reason a transistor is used to discharge the cap in the off state is because leaving a pull-down resistor across the cap while charging makes the charge non-linear. A circuit like this is really easy to implement using a simple controller like the Atom but requires a dual channel op-amp, a couple transistors, and a capacitor be wired up to make the integrator. It also requires a multiplexer of some sort to select which r/c servo input you are measuring… something like a CD4051 would probably work just fine.

I agree Eddie. The term PPM really applies to the signal the old radios used to transmit to the receiver. The position of the pulse determined the position of the servo. I carried the term through the post just to differentiate it from PWM because the signals do not behave as, or are not used as, pure PWM (0% to 100%).

Eddie, Robot dude.

Thanks,

I’m not an electronics wiz but there has got to be a way of converting pwm signals from rc transmitters into a usable format that is somewhat easy to code with so we don’t run into situations where we have to use gobs of code to interpret the pwm signals…

It would be nice if there was a electronic circuit we could build that would do this, and convert it to something that the BB2 can get a numerical value from with the least amount of wasted cpu cycles.

–Aaron

Personally I’d go for a small CPLD with a 12-bit counter, 12-bit PI-SO shift register, and 8 channel mux to do the conversion. It would take maybe 4 wires from the controller to interface using an SPI style protocol. It’d be like 3 parts. :wink:

Sounds great :confused: , All you got to do is write the logic code for the CPLD to make it work?

Eddie … haha draw up a schematic and ill build it, but was thinking more of the lines of something really simple CPLD already sounds complicated… :slight_smile:

If I were to only have my stupid 50x servo extension wires I ordered off ebay about a month ago from china, I would be able to try all these methods to see if I can get away with using Kurte’s or Xans code on this issue, or at least make my own and see what happens with it.

By the way a bit off topic, how do you run code off the BB2 with the serial port linked to it to view live values or debug it?

Sure… straight forward enough in even the old HDL languages like CUPL or ABEL. Verilog and VHDL make my head hurt usually but then again I’m older so it’s probably the pain of thinking how to make the new fangled way of expressing logic do what I can do in only a few straight forward lines of the afore-mentioned HDLs. :smiley:

… OK you officially lost me…

Can ugh someone decode that for me ?

:open_mouth:

Wow , funny stuff…

ok a quick overview, although admittedly off topic so I’ll keep it sort of short.

Digital logic can be expressed as a form of mathematical equations. there are ICs called programmable logic devices (PLDs) that allow their output functions to be written in a programming language as a form of equations combining their input pins and feedback from their output pins. such a programming language is called a hardware description language (HDL). ABEL, CuPL, and PALASM are examples of early HDLs and their syntax resembled very closely the actual matematical equations used to represent the logic. Verilog and VHDL are examples of more modern HDLs and have a much more structured approach that resembles C more than straight equations. Verilog in partcular is used heavily both for coding devices as well as writing test procedures to validate the logic programmed to the devices.

The simplest PLDs were based on an array of fuses much like an EPROM and called PALs for programmable array logic. they had simple output functions like, for instance an or 8-input OR gate where each input was tied to an AND gate that could access every input pin to the device as well as a feedback from each output pin. the programmable part was which of the inputs to the AND gate were actually connected to a signal. there might be 8, 10, or 12 of these (OR + 8 AND) channels in a single package and upwards of 24 inputs depending on the device package size (i.e. number of pins available.) There were more options, the OR gate could drive a D-type flip-flop for making shift registers or state machines, or migh be replaced by an XOR gate to make building addition or CRC circuits simple. The next generation of these devices were called GALs, G for generic, as they replaced a single type of output with an output macrocell which had one or more registers and XOR gates or other specific logic functions which could be selected by programming… very flexible. The next generation is called complex programmable logic device, CPLD, and has multiple GAL devices in a single package which are, in addition to having a programmable array in each GAL, connected internally through yet another programmable fuse array that allows the different GALs to share input signals and pass outputs to other GALs. CPLDs can get quite large, I’ve used parts with as many as 16 GAL sections. Lattice and Xiinx are probably two of the biggest active CPLD developers but there are many others.

CPLD, GAL, PAL is implemented in silicon using EEPROM cell technology. therefore it has some of the same benefits as well as limitations. One of the biggest benefits is you turn on the power and it’s there… vs something called an FPGA (field programmable gate array) which is based on an SRAM cell and requires its arrays be re-loaded from a PROM device every time power is applied. This tends to make smaller CPLDs more cost effective than FPGAs with similar logic capability. Of the disadvantages of EEPROM cell are it’s slower and takes more transistors to implement when compared to SRAM… which is why very large FPGA devices are often comparable in cost to moderately large CPLDs. Many FPGA architectures are also considerably more flexible than the interconnected GAL sturcture of a CPLD. In the end you need to balance all of the factors of capability, complexity, re-programmability (FPGA are generally easier to manage in this regard), and cost when selecting a programmable logic device.

For the project you have been discussing, capturing lots of servo inputs efficiently into the BAP, I would probably lean towards a Lattice Mach 4 or Xilinx XC9500 series CPLD, with somewhere between 48 and 96 macrocells. This is like a $3-5 range part in USD.

Well… I said sort of short anyway. :unamused:

It has been a long long time for me since I had to look at a hardware description language. Probably back in the ice age at school. I think the language they used back then was AHPL, which was based on APL…

Thanks Eddie for the overview as you said you could probably make a cost effective way of offloading the work from the BAP. I am not sure that it would make it easier for non-hardware programmers to understand.
It would possibly have the added advantage of maybe using fewer of the BAPs IO pins.

quite right of course in it not being much of an at home type solution… really would need to be sold as a breakout PCB with programmed part installed.

ya know I wonder if there is an option there… one of those generic breakout footprint patterns like on a vectorboard or surfboard but on a SSC-32 / BB2 stackable footprint. Could run some through BatchPCB and find something similar for sale on sparkfun a couple months later. :stuck_out_tongue: :laughing: Sry, my bad. :smiling_imp: :smiley:

well 3-5 bucks aint bad!

Although I think programming one of these with the knowledge I have is probably going to be pretty challenging!

Although I’m kinda leaning more towards code :stuck_out_tongue:

how do you run code off the BB2 with the serial port linked to it to view live values off variables to debug??