Infineon / XMC-for-Arduino

Integration of Infineon's XMC microcontrollers into the Arduino IDE.
Other
106 stars 69 forks source link

Question on HardwarePWM aka analogWrite() #34

Closed odo2063 closed 6 years ago

odo2063 commented 6 years ago

Is it possible to use analogWrite() with the XMC2go?

techpaul commented 6 years ago

According to the pins_arduino.h for that board there is one PWM ouput on pin 8 (P0.5) so like Arduino Uno the analog o/p is actually a PWM output.

This is board PHYSICAL pin 16 (top right on pin out diagram on the wiki)

mhollfelder commented 6 years ago

Yes, there is one PWM output pin as already said. So this should work with analogWrite() as expected.

If you face any issues, please let me know.

Best regards,

Manuel

odo2063 commented 6 years ago

ok, so you maybe want to add the "~" in the pinoutdiagramm? ;-)

HMenger commented 6 years ago

Hi Manuel,

would it be possible to add the "analogWriteFrequency(float Hz)" -Funktion (like in teensyduino) in the next release?

Some time ago i played with Dave-App for PWM, looking for a possibility to create 1,5Hz blink-frequency, and with small modifications in the generated "pwm.h" and "pwm.c" - files , it was possible to generate (ultra) low pwm-frequency (down to 0,03Hz)

PCLK =64MHZ PRESCALER = 1 << 15 (2^15) PERIODVALUE = 65535

-->> lowest possible PWM frequency = 0,0298027771 Hz (with 65535 Resolution) with this mod :
change type of pwm_frq_hz from uint32 to float
;-)

/Sets the frequency for CCU4 slice. / PWM_STATUS_t PWM_lCCU4_SetFreq(PWM_t *const handle_ptr, float pwm_freq_hz) { PWM_STATUS_t status; uint32_t module_freq; uint8_t prescaler; uint32_t period_value; uint32_t compare;

regards Hubert

techpaul commented 6 years ago

Personally I try to avoid floats like the plague, as the overheads of floats is large.

Work with remainders or fixed point maths is ually faster and more accurate

HMenger commented 6 years ago

That was just a "quick&dirty" modification of the DAVE function , inspired by the arduino-teensy 3.x lib. the teensy 3.2 has a Cortex M4 MCU (MK20DX256)

same result could be reached by adding an additional parameter like "uint_8 precision" :

PWM_STATUS_t PWM_lCCU4_SetFreq(PWM_t *const handle_ptr, uint32_t pwm_freq_hz, uint8_t precision)

maybe something like this:

define PWM_PRECISION_0 1 // 1,2,3,....

define PWM_PRECISION_1 10 // 0.5, 12.4, 1234,7,.....

define PWM_PRECISION_2 100 // 0.17, 6.78, 1234,56

..... // set 1,23 Hz PWM_SetFreq(&_PULSE,123,PWM_PRECISION_2);

for Arduino-compatibility it should look like analogWriteFrequency( pmw_pin ,123 , PWM_PRECISION_2 );

But i think the Arduino-way is rather to have a quick result for an idea ,with easy to use functions, than to produce highly efficient code..

techpaul commented 6 years ago

Quite a lot of Arduino way is ignore errors, discard data on incoming data and many other issues.

I am trying to see if XMC-for-Arduino could be usable for educational projects with 17-18 year old school projects. Most of the things I am looking are for what the students would use and if some of my standard libraries could be ported to assist their projects.

Things like better LCD library, task scheduling, PS2 interfaces, DMX controller and device operations. Personally better timer handling and functions like micros and delaymicroseconds working properly and without issues from disabling interrupts are more important, than VLF PWM, as these are used in nearly all the projects I have seen over the years.

Other than proof of concept of a PART of a commercial project I would MIGHT use XMC-for-Arduino, having found the issues in these libraries I will be looking at what also exist in Dave for fuller projects,. That may be a way off as I need efficient code as some of my systems run continuously for years or do relatively high speed operations for the processor speed sometimes with dedicated FPGAs

odo2063 commented 6 years ago

"analogWriteFrequency(int32 Hz)" would also be ok XD....or is there another way to set the frequency?

techpaul commented 6 years ago

By not using Hz as the units but ms or us as the units as the time period for cycle is what the timers use, consider a 32 bit value for us and the range that has. However many of the timers on many platforms do not have 32 bit timer/counters.

What can be done depends on the limits of the platform not what a piece of software can processs. realistically if you really want VLF PWM you can also do that by GPIO toggling and delay() with ms timings. This assumes you are not doing anything else as the majority of Arduino style functions are linear and not good at doing much in parallel with hardware assist.

odo2063 commented 6 years ago

Can someone please give me an example on how to change the PWM frequency for xmc2go?

HMenger commented 6 years ago

Hi odo2063,

seems Manuel was very busy yesterday ;-))

i saw this morning that there is a new branch "XMCLib" https://github.com/Infineon/XMC-for-Arduino/blob/xmclib/arm/cores/wiring_analog.c if you take a look to the wiring_analog files, there is a new function that should do the job:

extern int16_t setanalogWriteFrequency( uint8_t pin, uint32_t frequency )

regards Hubert

mhollfelder commented 6 years ago

odo2063:

ok, so you maybe want to add the "~" in the pinoutdiagramm? ;-)

I added a line indicating a "~" to the pin out diagrams.

As already found out, PWM frequency change has been implemented, but we need to test it/rework it. For the PWM frequency changes, the pull requests as well as additional points, I need to work on it.

I will add a new issue tomorrow for proper discussion of the new release and reference related issues. By this, I hope to merge all requests and changes in a proper way to a new final release.

Thank you very much for all your feedback and work, very much appreciated!

Best regards,

Manuel

techpaul commented 6 years ago

What PWM frequency, resolution andaccuracy do you need?

Recent changes I have done to tone and wiring_time could easily do VVLF frequency toggle of GPIO, in 1 ms steps which means you could have a VERY VERY LOW frequency but specify the ON and OFF times in ms with limits of

Min ON or Min OFF 1 ms Max ON or Max OFF approx 50 years

1.23 Hz is 813 ms period so by specifying ON and OFF times on the fly you could have a 812 steps of PWM variation to 1ms accuracy, about 9.5 bits of resolution and accuracy Whilst still having lots of processing time available..

At even slower PWM frequency you have more bits of resolution and accuracy

Function like
LFPWM_mode( pin, uint8_t Start level ) LFPWM_time(uint32_t ON, uint32_t OFF )

If this would help I can see in betwen other paying work and other fixes if this can be added

HMenger commented 6 years ago

Hi Manuel, i made some test this evening with an XMC2Go and checked the frequency an dutycycle with an Osziloscop. there where some deviation in frequency and duty cycle. above 1000Hz it didnt work. image

i made some small modificatin in the function to calculate prescaler and timer period , with that it is working better, the deviation is now below 0,1% in the range from 100Hz to 100kHz. (according to the frecuency-counter on my scope)

extern int16_t setanalogWriteFrequency( uint8_t pin, uint32_t frequency ) { int16_t ret = -1; XMC_CCU4_SLICE_PRESCALER_t prescaler = (XMC_CCU4_SLICE_PRESCALER_t) 0; if(frequency < PCLK) { uint8_t exp = 0u; do { //if(frequency > PCLK/(1u<<exp)) //**** if(frequency >= ((PCLK >> exp)/65536)) //**** { break; } exp++; }while(exp <= 12u);

    if (digitalPinHasPWM4(pin))
    {
        uint8_t pwm4_num = digitalPinToPWM4Num(pin);
        XMC_PWM4_t *pwm4 = &mapping_pwm4[pwm4_num];

        pwm4->prescaler = exp;
        //pwm4->period_timer_val = PCLK/(frequency*(1u<<exp));

// pwm4->period_timer_val = ((PCLK >> exp)/frequency)-1; // //

        if(pwm4->enabled == ENABLED)
        {
            // Disable pwm output
            pwm4->enabled = DISABLED;
            XMC_CCU4_SLICE_StartTimer(pwm4->slice);
        }

        ret = 0;
    }

ifdef CCU8V2

    else if (digitalPinHasPWM8(pin))
    {
        uint8_t pwm8_num = digitalPinToPWM8Num(pin);
        XMC_PWM8_t *pwm8 = &mapping_pwm8[pwm8_num];

        pwm8->prescaler = exp;
        //pwm8->period_timer_val = PCLK/(frequency*(1u<<exp));
        pwm4->period_timer_val = ((PCLK >> exp)/frequency)-1;

        if(pwm8->enabled == ENABLED)
        {
            // Disable pwm output
            pwm8->enabled = DISABLED;
            XMC_CCU4_SLICE_StartTimer(pwm8->slice);
        }

        ret = 0;
    }

endif

}

return ret;

}

regards Hubert

HMenger commented 6 years ago

did not work in range 1-10Hz at 1,3,7 Hz

//**** if(frequency >= ((PCLK >> exp)/65536)) //**** removing the "=" here solved it.

if(frequency > ((PCLK >> exp)/65536))

HMenger commented 6 years ago

Hi Paul,

for current projekt i need low frequencies up to max 5 kHz for building a testequipment for aftermarkert motorcycle-speedo to simulate speed- (0..300kmh , ->0....83,33Hz) and rpm (0..18000 /min, -> 0...300Hz) -signals

the frequency also can be higher , depending on the pulses per meter ( 1... 64) and rpm (1..4), depending on the bike setup.

techpaul commented 6 years ago

Personally it is not the maximum frequency that is the problem it is resolution, as soon as you use a counter for PWM you lose resolution in frequency depending on numbers of bits need to handle the PWM.

Realistically whatever the frequency to maintain corect pulses (as if from hal effect or opto slotted wheel or similar) you need to maintain the mark/space ratio. to do that on XMC1xxxx CCU4 module and maintain the mark/space ACCURACY over the full range, you would probably need to use a cascaded PWM of 32 bit width. Then comes the problem of adjusting frequency and PWM values on cascaded units without glitches.

If on one configuration you need a 1:63 mark/space ratio, you need to clock 64 x faster at least if not 1024 times to maintain PWM range with frequency to avoid quantisation errors.

For this sort of thing I would borrow a thought from engine management micros and their Timing Pattern Units, where they change ONLY the frequency of update as the timing pattern is repeated often many bits wide giving parrallel shift registers many ouput serial bits wide, to keep things in sync.

By having an external 16 bit or more, Parallel In Serial Out shift register you can shift out a pattern until it recives the first one then reload the shift register, giving you a constant 1:16 (or however many bits wide), mark/space ratio. Then concentrate on using the micro to use 16 or 32 bit counter to produce an accurate frequency sweep over the range.

Timers have problems at low and high ends of range as to how much deviation is acheived per bit change. Top frequencies have large jumps in frequency, whilst at low end large count differences are required to make a change within your desired range. The difference between 1 Hz and 1.1 Hz can be large.

Changing prescalers adds other issues of needing hysterisis in when you change with overlapping calaculation ranges to allow for changes smoothly.

I would personally use external shift registers in a Timing Pattern Unit mode or build larger width TPU into a PLD/FPGA to be configurable and accurate. Concentrating on speed control from the micro by accurate clock over the range.

techpaul commented 6 years ago

This is an example circuit of what I mean by shift registers test-tpu

HMenger commented 6 years ago

Thanks for all the information provided here. i made some more tests with different boards,

in made a small modification in calulation for the timer period to increase the accuraty (a little) //*** // modified calulation of timer period uint16_t period = (PCLK >> prescaler ) / frequency // ;PCLK / ( ( 1U << prescaler ) * frequency) ; // - 1; //*** int16_t resource;
if ( ( resource = scan_map_table( mapping_pin_PWM4, pin ) ) >= 0 ) { uint8_t pwm4_num = resource; XMC_PWM4_t *pwm4 = &mapping_pwm4[pwm4_num];

        pwm4->prescaler = prescaler;
        pwm4->period_timer_val = period; //PCLK / ( ( 1U << prescaler )  * frequency) - 1;

        if(pwm4->enabled == ENABLED)
        {
            // Disable pwm output
            pwm4->enabled = DISABLED;
            XMC_CCU4_SLICE_StartTimer(pwm4->slice);
        }   
        ret = 0;
    }

with that it is very accurate on XMC4700 board. i tested a funtion to set frequency with precission e.g 100.00 wich work good for lower frequency up to 1kHz: // set frequency with 0.01 resolution extern int16_t setAnalogWriteFrequency100( uint8_t pin, uint32_t frequency ) { uint32_t freq = frequency/100; uint16_t period ;

int16_t ret = -1;
if(freq < PCLK)
{
uint16_t prescaler = 0U;
    do
    {
        if( freq > (PCLK / ( ( 1U << prescaler ) * 65536U )) )
        {
            break;
        }
  prescaler++;
    }while(prescaler < 16);

period = (PCLK >> prescaler )*100  / frequency ;

int16_t resource;       
if ( ( resource = scan_map_table( mapping_pin_PWM4, pin ) ) >= 0 )
    {
        uint8_t pwm4_num = resource;
        XMC_PWM4_t *pwm4 = &mapping_pwm4[pwm4_num];

        pwm4->prescaler = prescaler;
        pwm4->period_timer_val = period;      // (PCLK >> prescaler )*100  / frequency ;//- 1;

        if(pwm4->enabled == ENABLED)
        {
            // Disable pwm output
            pwm4->enabled = DISABLED;
            XMC_CCU4_SLICE_StartTimer(pwm4->slice);
        }   
        ret = 0;
    }

ifdef CCU8V2

. . . regards Hubert