Trying to add C support for the Arc32 plus Bap28/40

Nathan and others,

Note sure if this is the best place to discuss this, or on BM forum or email…

As mentioned in a different set of posts, I have been trying to build some C/C++ libraries for the different BAP processors. The current library is setup to build do different libs, one for the 3698 family (Bap28 maybe 24 if I find a reason to do so) and the 3687 family (Bap40 and hopefully arc32). I have most of the phoenix code ported to C and running on these libraries. My CHR-3 is currently using the Bap28 and I can control by PS2 and/or XBEE. I have also tested this code on BAP40 on a lab board and it appears like it is working. I have not yet tried to plug it in to the actual robot…

Now I would like to get it to work on the Arc32, which has a lot smaller footprint. I have a start on the pin table, but know that the early pin number text file does not match the current ARc32 PDF, which I can easily rectify. But the question is how best to handle the IO pins that are connected to the HC4052 (2x4) analog multiplex chips.

My first guesses of how this is hooked up is as follows:
The A and B select lines are connected up to standard H8 IO pins which I can guess the actual pins from previous info.
My guess is that the enable pins are connected to ground…
The X/Y pins are connected to the FTIO pins of the H8 so you can use TimerZ support (PWM?)

But since the document ion mentions you can get analog values for many(16) of these pins by HSERVOSTAT command and the FTIO pins are not analog pins, my assumption is that the X/Y pins of two of these chips is connected up to 4 analog input pins, probably the second group PB4-7) as I believe there are 4 other analog capable pins (p32-35) defined to read in VS1, VL, VS2 and a pin on Aux2 connector.

Do you have any suggestions on how best to do this, or should I punt and/or figure it out by trial and error.

Thanks
Kurt

HSERVOSTATE

However there is a very complexc interrupt hadnler that does all this work in Basic. It handles switching the muxes to the correct pins at the correct times. It handles starting the AD readings on 4 pins and then the next interrupt it reads those readings and moves on to the next set of 4 pins.

Post your pin tabel and I’ll correct it.

Hi Nathan, it is actually in the C files I uploaded in the C Phoenix posting, but here is the current version:

const PORTITEM g_PortTableArc32] = { {&IO.PDR5.BYTE, 0x10, (PUR_5 | PMR_5)}, // P0 servo0 (P54/WKP4) {&IO.PDR5.BYTE, 0x20, (PUR_5 | PMR_5)}, // P1 servo1 (P55/WKP5/ADTRG) {&IO.PDR1.BYTE, 0x1, (PUR_1 | PMR_1)}, // P2 servo2 (P10/TMOW) {&IO.PDR1.BYTE, 0x2, (PUR_1 )}, // P3 servo3 (P11/PWM) {&IO.PDR1.BYTE, 0x4, (PUR_1 | PMR_1)}, // P4 servo4 (P12) {&IO.PDR7.BYTE, 0x20, 0}, // P5 servo5 (P75/TMCIV) {&IO.PDR7.BYTE, 0x40, 0}, // P6 servo6 (P76/TMOV) {&IO.PDR2.BYTE, 0x8, 0}, // P7 servo7 (P23) {0,0,0}, // P8 {0,0,0}, // P9 {0,0,0}, // P10 {0,0,0}, // P11 {0,0,0}, // P12 {0,0,0}, // P13 {0,0,0}, // P14 {0,0,0}, // P15 {&IO.PDR5.BYTE, 0x8, (PUR_5 | PMR_5)}, // P16 servo16 (P53/WKP3) {&IO.PDR5.BYTE, 0x4, (PUR_5 | PMR_5)}, // P17 servo17 (P52/WKP2) {&IO.PDR5.BYTE, 0x2, (PUR_5 | PMR_5)}, // P18 servo18 (P51/WKP1) {&IO.PDR5.BYTE, 0x1, (PUR_5 | PMR_5)}, // P19 servo19 (P50/WKP0) {&IO.PDR3.BYTE, 0x1, 0}, // P20 servo20 (P30) {&IO.PDR3.BYTE, 0x2, 0}, // P21 servo21 (P31) {&IO.PDR3.BYTE, 0x4, 0}, // P22 servo22 (P32) {&IO.PDR3.BYTE, 0x8, 0}, // P23 servo23 (P33) {0,0,0}, // P24 {0,0,0}, // P25 {0,0,0}, // P26 {0,0,0}, // P27 {&IO.PDR1.BYTE, 0x80, (PUR_1 | PMR_1)}, // P28 servo28 (P17/IRQ3/TRGV) {&IO.PDR1.BYTE, 0x40, (PUR_1 | PMR_1)}, // P29 servo29 (P16/IRQ2) {&IO.PDR1.BYTE, 0x20, (PUR_1 | PMR_1)}, // P30 servo30 (P15/IRQ1/TMIB1) {&IO.PDR1.BYTE, 0x10, (PUR_1 | PMR_1)}, // P31 servo41 (P14/IRQ0) {0,0,0}, // P32 {0,0,0}, // P33 {&IO.PDR5.BYTE, 0x80, (PUR_5 | PMR_5)}, // P34 AUX1 pin1 (P57/SCL) {&IO.PDR5.BYTE, 0x40, (PUR_5 | PMR_5)}, // P35 AUX1 pin2 (P56/SDA) {&IO.PDR7.BYTE, 0x4, 0}, // P36 AUX1 pin3 (P72/TXD_2) {&IO.PDR7.BYTE, 0x1, 0}, // P37 AUX1 pin4 (P70/SCK3_2) {&IO.PDR7.BYTE, 0x2, 0}, // P38 AUX1 pin5 (P71/RXD_2) {&IO.PDR3.BYTE, 0x80, 0}, // P39 AUX1 pin6 (P37) and (PB3) {&IO.PDR2.BYTE, 0x10, 0}, // P40 AUX2 pin2 (P24) with pullup {&IO.PDR8.BYTE, 0x20, 0}, // P41 AUX2 pin4 (P85) with pullup {&IO.PDR8.BYTE, 0x40, 0}, // P42 AUX2 pin6 (P86) with pullup {&IO.PDR8.BYTE, 0x80, 0}, // P43 AUX2 pin8 (P87) with pullup {&IO.PDR3.BYTE, 0x40, 0}, // P44 Status LED (P36) {&IO.PDR7.BYTE, 0x10, 0}, // P45 Batt LED (P74/TMRIV) {&IO.PDR3.BYTE, 0x10, 0}, // P46 MUX A (P34) {&IO.PDR3.BYTE, 0x20, 0}, // P47 MUX B (P35) {&IO.PDR6.BYTE, 0x1, 0}, // P48 U24 X (P60/FTIOA0) {&IO.PDR6.BYTE, 0x2, 0}, // P49 U24 Y (P61/FTIOB0) {&IO.PDR6.BYTE, 0x4, 0}, // P50 U21 X (P62/FTIOC0) {&IO.PDR6.BYTE, 0x8, 0}, // P51 U21 Y (P63/FTIOD0) {&IO.PDR6.BYTE, 0x10, 0}, // P52 U22 X (P64/FTIOA1) {&IO.PDR6.BYTE, 0x20, 0}, // P53 U22 Y (P65/FTIOB1) {&IO.PDR6.BYTE, 0x40, 0}, // P54 U23 Y (P66/FTIOC1) {&IO.PDR6.BYTE, 0x80, 0}, // P55 U23 X (P67/FTIOD1) {&IO.PDRB.BYTE, 0x1, 0}, // P56 VS1 (PB0/AN0) {&IO.PDRB.BYTE, 0x2, 0}, // P57 VL (PB1/AN1) {&IO.PDRB.BYTE, 0x4, 0}, // P58 VS2 (PB2/AN2) {&IO.PDR2.BYTE, 0x2, 0}, // S_IN (P21) {&IO.PDR2.BYTE, 0x4, 0} // S_OUT TXD(P22) };
Thanks again
Kurt

As I mentioned in the other post, I have the beginnings of a HSERVO type code for C that has not been fully debugged yet. I hope to get back to it soon. I was thinking like trying to setup the HServo2 code to work something like:

If I leave TImerZ to increment on the 20mhz clock, that implies I should get an overflow every 3.2ms, which is longer than any pulse width needs to be. So every 6 overflows gets us near enough to 20ms for the servos (19.7ms). So I take one of these clocks for each of the 4 servo groups and have it set the appropriate A/B lines. Note: I will assume that I have already calculated each of the pulse widths in clocks for each of the servo or the value 65536-pw

Then at the start of a cycle, I would calculate when the next overflow will happen and set the two sets of registers (GRA-GRD) to 65536- for each servo that is active in that group. Not sure if I need interrupt on the match if I can have it automatically set the IO pin high at the match time. Then at the next overflow I would set all of those signal back low, and do any updates on calculations for changes in pulse with (group move), then do the processing for the next group (setting up GRA-GRD…) Not sure yet if need interrupts on GRx match. May? For example could get interrupt and then change the GRx match to something like: 0xffff to automatically set low and in that way maybe more precise wave form?

That leaves 2 overflow interrupts to do other things like maybe the hservostate processing you mentioned.

Does this approach sound reasonable?

Thanks
Kurt

Maybe we could get schematics for the ARC-32 under NDA?

Or a good block diagram?

Alan KM6VV

Just a quick update. I did receive confirmation from Nathan on which IO pins are connected to where. So I have updated my Arc32 pin table. It also answered my question to myself on how to handle S_IN/S_OUT as on previous platforms, they were 32/33, which was beyond the number of IO pins, but on Arc32 there are more. Answer leave as 32/33 and add the new ones after… Easy enough.

Also know which MUX pins are connected to which IO/Servo pins. So I have started writing code to do HSERVO like commands. Hopefully in a few days will have it limping along. Most of my thoughts for first pass are a few posts up. But I am hoping to see if I can simply use the compare match for the 8 channels (TZ0-1xGRA-D). Example is: Suppose I wish a pulse of 30000 clock pulses. I would first setup:
TZ0.GRA = 65535-30000 (may need some fudging)
Tell timer system to set the IO Pin high when we get the match and tell the system to generate an interrupt

When that time happens: IO will be set high, interrupt comes in.
Set TZ0.GRA = 0xffff - Just before it wraps and we get the overflow interrupt.
tell timer system to set the IO pin low when we get the match - Don’t need interrupt here…

When the timer overflow happens, all of the pulses for the previous channel should have already had their signals set low (Should be easy to setup very accurate pulses with mimimal overhead) … Ready to setup for the next channel with it’s 8 servos…

Issues to investigate:

  1. Some states of some of the different registers. Will need to figure out for example if I need to change the TOCR with each setting or can it be set once and done…

  2. The Zenta issue: Hitec digital servos - The above should work fine for those servos that do not have another IO pin (8-15, 20-23), that had their pull-up resistors removed. However what to do with those servos who have another IO pin associated with them. First thought was to simply set those IO pins to input, such that they won’t interfere. (What I am pretty sure the first implementation of HSERVO2 did). But this caused some digital servos to not work due to issues with pull-up and pull-down resistors… The question is what to do about it.
    a) Punt - I don’t have many digital servos anyway :stuck_out_tongue: (Only the couple of them on my Rover with Arm…)
    b) Could use the H8 IO lines connected to these IO ports to drive the signals high or low. - Should work, Question is about timing. That is with using the clock lines to go high and low, first off I don’t need as many interrupts, second off the timer makes it accurate as it drives the pins at the exact time I say, whereas I I use the IO pins, it depends on when the interrupt gets processed such that the interrupt driver can drive all of them low, so may have differences. Could waste time in handler to say I interrupt at time 0 but will spin and set the values at time 100, which should get more consistent timings, but waste CPU…

  3. How to have a different TimerZ interrupt handler for Bap40 and Arc32… Could have code in one timer that goes, if I am running on ARC32 do this else do that… Extra CPU cycles for each interrupt… Or would it be real/real bad to update the Interrupt vector at startup time? IE poke the address of the appropriate handler into memory addresses 0x34-35(z0) and 0x36-37 (Z1)… May try this…

That is all for now.
Kurt

I used the 6 cycles like you are looking at. In the first 4 cycles I set the pins of each servo high(that are active) and then I set the compare match to set them low when the pulse should be done for each servo. This gives me clock cycle accurate servo pulses. I also calculate the next positions for those same servo pins. I also start the A/D converters(on the A/D capable pins) using scan mode and read the result on the next of the 6 cycles. So in total I use 5 of the 6 cycles to do work and one cycle is just a do nothing interrupt.

  1. The pins without secondary I/Os on them have pull downs so those should not have a problem. The pins with secondary I/Os need to set the secondary I/Os to low when NOT driving the servo pulse. This has to be done for the previous 8 pins on the next of the 6 cycles. eg for pins 0 to 7 seconday I/Os need to be set as inputs and pulse is driven on first cycle and secondary I/Os for pins 0 to 7 must be set low on second cycle.

  2. The interrupt jump table can’t be modified at runtime. It is flash and the flash can’t be rewritten at runtime. Also note that your vector table is stored starting at 0x400. the bootloader has a redirecting vector table at 0x0. The code at 0x400 are actually just jmp commands, where a true vector table(like at 0x00) are just the addresses to jump to. So the 0x4000 vector table is in multiples of 4bytes and the true vector table at 0x0 is in multiples of 2 bytes.

Thanks Nathan,

I think I have a version of the HSERVO2 stuff somewhat working now. It appears to be driving all of the servos reasonably OK. I have the C Phoenix code ported over to it and it is at least walking. The code needs lots of cleanup/performance improvements. I have not done anything with my c code yet to support AtoD. I believe Bob - the other guy doing a C library has AtoD support. I will add it at some point soon.

I think I used the Interrupt, slightly different. That is, on the TimerZ0 overflow interrupt, I set the GRA-GrD for both Timerz0 and TimerZ1 (for those pins whose servos are active), to interrupt at something like: 65535-. I also set the flags to have it set IO state high at that point. On the interrupt, I reset the GRx value to 0xffff, turn off the interrupt and set it to set the pin level low when the timer happens. That way the high and low are both set by the hardware. When the next timerZ0 interrupt happens, before I change what the channels are pointing to I drive low any of those pins that have a packing H8 pin, I then set all of the TimerZ registers to not have the IO pins to not change (The underlying IO pins were setup as input pins so they float and not change the underlying values) and then set the the channel to the next group and start over…

If you are interested, the C code is all all up on the Multileg forum in the Porting the phoenix to C thread…

Kurt
Kurt

I am not sure how many are interested, but it might be nice to get some input…

As I mentioned in the other thread: viewtopic.php?f=8&t=6196&p=69514#p69514
I believe I now have both HSERVO and HSERVO2 type functionality working pretty well and I have my Biped brat working pretty well using the Bap28. I still need to drag out my Bap40 board and see if it is working or not. I will soon also make another pass through the Arc32 HServo code.

Yesterday I started playing with the Adin code. My earlier version of this was hard coded for the Bap28. That was it said if the passed in IO pin is 0 than it is channel 0… So I changed it to be driven by the IO table, so should not need to change this code for the different processors now… Tested so far with the Brat on Bap28…

But now trying to decide what to do with Arc32. Several options available, each with pluses and minus. So I am sort-of wondering how other people have been using the Analog capabilities on the Arc32? So far I know that some of you use the AtoD conversions to check their battery voltages.

Some options I see include:
a) Punt: The version of code that I have now should work (will test soon), to read VL, VS1, VS2 and 1 pin that is on one of the AUX connectors. Would not give you the capabilities of basic that allows you to get AtoD conversions on pins 8-15 and 24-31

b) Add some slower support for the pins I mentioned above. As I mentioned in previous posts there are 6 Timer cycles per pass through the servos, currently only using 4 of them. Could probably easily add support to read 1 analog reading during those cycles. Downside is that these reading would be slow, IE 1 20ms cycle to do a reading… Plus is that it would not take require another cache of data…

c) Fully implement the HServoStat code. Nathan mentioned in a previous post that he uses one of the TimerZ cycles to do the readings… We basically go through 6 TimerZ overflow interrupts per pass of the servos. 4 of these are needed to process the servos, which leaves 2 others. As HServoStat can read the Analog state of 16 servos, but we only have 4 Analog pins connected, this implies we have to do this in 4 groups of 4 (So need 5 parts of the code: setup first 4, read first-setup second,…,read fourth), so my guess is that I will need to setup 4 compare match interrupts as well during these cycles. Should have enough cycles to do this, as each AtoD conversion takes maybe 134 cycles, so a scan of 4 should take, 536 clocks, so I could setup the 4 GRA-GRD to be separated by maybe 5000 clocks or more and have it Reading in a batch and setup the next batch… May need to have a 5th pass for Analog pins 0-3 which are the VS pins mentioned above or we need to synchronize when we can call ADIN as to not interfere with eachother… The plus of this approach is that it gives you to flexibility to easily read all of those Analog states. The downside is the interrupt system is constantly interrupting the code to process these Analog pins and you are eating up maybe 20 words of RAM to cache these values, even if your code never uses them…

Ideas?

If possible, I would like to keep with 1 API like ADIN to read in Analog data regardless of the underlying implementation. That is if you move your code over from a BAP28 to an Arc32, you should not have to change which APIS you are using… May also be able to have the implementation that if you only call ADIN on the pins that have direct access, than stay with simple code, only if you call ADIN on one of the multiplexed pins do you switch to the hservostate type code… At least that is what I would hope for.

Again any suggestions or feedback would be great!
Kurt

As this is a experiment/hobby/exploring-card, full functionaly is allways the goal, even if it does “eat” both CPU-cykels and RAM…but I’m only a noob, but you are absolutely doing a very good jobb !

Thanks !

Kurt,

I’d fully implement the HServoStat code. I’d like to be able to read all of the Analog ports.

I did notice that I couldn’t read the full voltage of the Analog pins. Seems the input impedance is low?

Alan KM6VV

I’ve see what appeared to be an impedence issue on ARC32 when I was developing it but actually was my code not giving the AD hardware enough time to do a correct conversion.

Thats why I use scan mode on my adin for the hservo system. It gives me a full 5ms to do the AD conversions on 4 AD pins. once I impleneted that the low AD readins went away.

I can try that.

Now I need to remember where it was I was reading the sensors! But I think I was using the Hservo status command.

Alan KM6VV

As I have mentioned before, I have been playing around with the Analog to Digital capabilities of the Arc32. So I have been hacking on my Servo support code for the Arc32 to add Analog support (HServoState). I think I finally have it working reasonably well now, and it looks like it still generates servo pulses…

My implementation is a bit different than the version for Basic, in that I only have one numbering of pin numbers. That is I use P57 for VLIn in all cases and not depending on API… I know I may be confusing some here, but if you look at the Arc32 data sheet (b0010) at page 7, you will see for example two P34s defined… I did not want that.

Second - If you only ever call HSERVOSTATE on one of the 4 IO pins that are directly hooked up to Analog pin, than I never startup the HSERVO code to read the analog data, it simply goes through the normal ADIN code. If you however should do an HSERVOSTATE command on one of the pins indirectly connected to Analog pins through the MUX (9-15, 24-31), I startup the analog reading in the HSERVO code. Also if necessary I start up the HSERVO code.

There are still optimizations and the like that I may consider doing. Things like when the Analog HSERVO code is running, it adds something like 4 more interrupts to process the different groups of IO pins. Could optimize that if you never ask for any of the analog data associated with a group of for pins, than don’t have the analog code query these pins, which saves the interrupt overhead plus… The 5 logical groups are: (VS1, VL, VS2, Aux?), (10,15,28,25),(9,12, 31,26),(8, 14, 29, 27),(11,13, 30, 24)

Also I would like to make calling this more transparent. That is you should simply call ADIN for all platforms and it should do the right thing. In my CPhoenix_Arduino Project I simply defined

#ifdef BASICATOMPROARC32 #define adin(_bpin_) HServoState(_bpin_) #endif

Could probably simply move this define up into a public place.

I have now posted this code here as this is no longer specific code for Phoenix…

Kurt
CPhoenix with Arduinosh lib.zip (218 KB)