Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 11/11/2019 in all areas

  1. 1 point
    energia

    Interrupts for TM4C123GXL

    I posted a library a while ago which could be of use. It can be found here: https://github.com/energia-libraries/RotaryEncoder
  2. 1 point
    maelli01

    3phase variable speed motor drive

    Hi there Here is my 3phase variable speed motor drive booster pack This has been in my mind for some years, but I always thought that a 3phase variable speed inverter drive is beyond my humble hobbyist scope. Too complicated for my old 8-bit mind ;-) Such a inverter contains: 6 high voltage FETs or IGBTs, 6 gatedrives, at least one DSP, a protection concept, all the software to create the 3-phase PWM, dead time control..... Still that was for quite some time on my long-term "to do" list, with no chance to actually materialize it, not enough time, too many other things to do. When playing around with the PWM module of the TM4C123 I found out that creating a 3phase PWM signal with this module is actually pretty easy. Combined that with an integrated Power Module such as the FSB50550 (Fairchild). So here it is: a booster pack for the Tiva Launchpad which drives big-ass 3phase motors. The booster pack contains the following: - the FSB50550 power module (6 FETs 500V 1.4Ohm, Gatedrivers, Bootstrap diodes, Temp sensor) - snubber capacitor Power supply: everything is powered from one DC source, 20V or (much) more. - 15V switchmode power supply from the high voltage side, built around a LNK304, for the FSB50550 - 3.3V switchmode power supply from the 15V to power the Launchpad, built around a LT1376 Measurement: - Passive voltage dividier to measure the input voltage - Sense resistor and LM339 comparator for overcurrent detection Display: - Nokia 5110 display Potentiometer for motor speed and direction The software is based on Energia using Tiva Ware function calls for all the PWM stuff. It is still work in progress, very basic and at the moment consists of: - calculate the sinwave lookup table at startup - PWM initialisation (PWM set to 15625 Hz, deadtime 1us, sync on) - a timer interrupt run every 10uSecs, do update the 3 PWD duty cycles - ADC measurement of temperature, voltage, current (moving average) - fault interrupt The main program is very short, the display is updated twice a second and the modulation factor is calculated out of the potentiometer speed setting and the applied DC voltage. Sudden changes in motor frequency are limited in the software, to prevent the motor to feed back energy and cause overvoltage. The motor on the picture is a 1/2hp, 900rpm, 6-pole motor, 12 kg of Italian steel and copper, probably 50 years old. For playing around, I apply about 50% of rated volt/hz, so current and maximum torque is reduced. Currently I use my dual 35V 4A lab supply, series connected, as a power source. here is the code: //simple 3phase frequency converter //27.9.2014 by maelli #define dots 192 //dots per halfhave, must be divisible with 3 #define period 5120 //80Mhz/5120 = 15625 switching frequency #define dt 80 //deadtime 80Mhz / 80 = 1uS #define PART_TM4C123GH6PM #include <stdint.h> #include <stdbool.h> #include "inc/hw_ints.h" #include "inc/hw_sysctl.h" #include "inc/hw_types.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/timer.h" #include "driverlib/pwm.h" #include "LCD_5110.h" #include "inc/tm4c123gh6pm.h" LCD_5110 myScreen (33,37,36,35,34,38,17); char celsius[3]={0x7f,'C',0x00}; uint16_t a,dire=0,modu,tensec; uint32_t timecount,sintable[dots]; volatile int32_t irqcount,timeset; volatile uint32_t temperature, voltage, current, poti; void setup(){ myScreen.begin(); myScreen.setBacklight(0); myScreen.text(0, 0, "3ph Converter"); for(int i=0;i<dots;i++) sintable[i]=sinf((i*3.14159)/dots)*(period/2-dt); unsigned long ulPeriod; unsigned int Hz = 10000; // interupt frequency in Hz ulPeriod = (SysCtlClockGet() / Hz); initTimer(); charge_gdu(); ROM_TimerLoadSet(TIMER0_BASE, TIMER_A,ulPeriod -1); initPWM(); } void loop(){ if (irqcount>499) { //20x per sec irqcount-=500; int32_t fsoll=732*(poti-16384); int32_t diff=fsoll-timeset; if (diff>0){ if (diff>150000) timeset+=150000; else timeset=fsoll; } else { if (diff<-150000) timeset-=150000; else timeset=fsoll; } modu=abs(timeset)/voltage/16; if (modu<(32000/voltage)) modu=32000/voltage; if (modu>256) modu=256; tensec++; if (tensec==10) { //2x per sec we display something tensec=0; myScreen.text(0, 1, mkstrg((temperature-325)/24,2)); myScreen.text(2, 1, celsius); myScreen.text(5, 1, mkstrg((voltage)/23,3)); myScreen.text(8, 1, "Volt"); myScreen.text(0, 2, mkstrg(abs(timeset)/322122,2)); myScreen.text(2, 2, "."); myScreen.text(3, 2, mkstrg(abs((timeset/32212)%10),1)); myScreen.text(4, 2, "Hz"); myScreen.text(7, 2, mkstrg(current,4)); myScreen.text(11, 2, "mA"); if (timeset<0) myScreen.text(0, 3, "links "); else myScreen.text(0, 3, "rechts"); } } } String mkstrg(int d,uint8_t l){ char display[l+1]; int q=1; display[l]=0; for (uint8_t a=l;a;a--){ display[a-1]=0x30+(d%(q*10))/q; q*=10; } return display; } void initTimer(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC); // 32 bits Timer TimerIntRegister(TIMER0_BASE, TIMER_A, Timer0Isr); // Registering isr ROM_TimerEnable(TIMER0_BASE, TIMER_A); ROM_IntEnable(INT_TIMER0A); ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); } void charge_gdu(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 ); ROM_GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_0,GPIO_STRENGTH_4MA,GPIO_PIN_TYPE_STD); GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7); //alle 3 oberen ausschalten HWREG(GPIO_PORTA_BASE + (GPIO_PIN_7 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_1); HWREG(GPIO_PORTD_BASE + (GPIO_PIN_1 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_3 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_0); //auch die 2 letzten aus HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = 0; GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6); //den ersten unteren ein HWREG(GPIO_PORTA_BASE + (GPIO_PIN_6 << 2)) = GPIO_PIN_6; delay(1); HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = GPIO_PIN_0; delay(1); HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = GPIO_PIN_2; delay(1); } void initPWM(){ ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); //The Tiva Launchpad has two PWM modules (0 and 1). We are using 1 ROM_GPIOPinConfigure(GPIO_PD0_M1PWM0); ROM_GPIOPinConfigure(GPIO_PD1_M1PWM1); ROM_GPIOPinConfigure(GPIO_PA6_M1PWM2); ROM_GPIOPinConfigure(GPIO_PA7_M1PWM3); ROM_GPIOPinConfigure(GPIO_PF2_M1PWM6); ROM_GPIOPinConfigure(GPIO_PF3_M1PWM7); ROM_GPIOPinConfigure(GPIO_PF4_M1FAULT0); ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 ); ROM_GPIOPinTypePWM(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7 ); ROM_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4); PWM1_0_FLTSEN_R =3; //PWM fault inverted see page 1169 GPIO_PORTF_PUR_R=0x10; //weak pullup for Pin 4 ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, period); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_1, period); ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_3, period); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_0, dt,dt); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_1, dt,dt); ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_3, dt,dt); ROM_PWMSyncTimeBase(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_0); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_1); ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_3); delay(1); PWMFaultIntRegister(PWM1_BASE, oh_shit); ROM_PWMIntEnable(PWM1_BASE,PWM_INT_FAULT0); ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, true); } void Timer0Isr(void) { //10000x per second ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); // Clear the timer interrupt irqcount++; timecount+=timeset; // 1 Hz is 192x256*256*256/10000=322122.5 if (timecount> 0xEFFFFFFF) timecount+=0xC0000000; if (timecount> 0xBFFFFFFF) timecount-=0xC0000000;; a=timecount>>16; a=a/(16384/(dots/3*2)); //a immer kleiner 2*dots: C000 *dots/3*2/ 4000= 12 *dots/3*2/4= 2*dots if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2-sintable[a-dots]*modu/256); a=a+dots*2/3; if (a>=2*dots) a-=2*dots; if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2-sintable[a-dots]*modu/256); a=a+dots*2/3; if (a>=2*dots) a-=2*dots; if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2+sintable[a]*modu/256); else ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2-sintable[a-dots]*modu/256); ROM_PWMSyncUpdate(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT); switch(irqcount%10){ case 0: temperature=(temperature*127+analogRead(26))/128; break; case 1: voltage=(voltage*31+analogRead(27)*3)/32; break; case 2: current=(current*127+analogRead(25)*8)/128; break; case 3: poti=(poti*127+analogRead(28)*8)/128; break; } } void oh_shit(void) { //in case of severe overcurrent we shut down! ROM_PWMFaultIntClearExt(PWM1_BASE,PWM_INT_FAULT0); ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, false); }
  3. 1 point
    I've spent some time recently experimenting with the Tiva timer PWM mode (not the PWM module). I have modified the wiring_analog.c such that duty cycle changes using PWMWrite() are now glitch free. Hence it is suitable for driving servos and other timing critical sources with frequent duty cycle updates. No interrupts are required. The changes are: - bypassing the timer disable/reset if it is already initialized to the PWM mode; - setting both MRSU and ILD bits in the Mode register such that both frequency and match register update are postponed to the next timer period; - the intermediate result of the duty cycle computation is increased so that large analog_res values do not cause overflow (useful for setting the duty cycle in microseconds for the servo) I tried to do a "minimally invasive" code modification to avoid compatibility issues. If the module interface change is acceptable then things can be done a bit cleaner/faster. Since I've already spend time learning the Tiva timer internals I can do further tweaks to the code if there is a community consensus on the optimal PWM interface (as a superset of Arduino). Try the code and see if you find any issues with it. If it does not cause any compatibility problems perhaps someone can help me integrate it into the next energia release. wiring_analog.c
  4. 1 point
    Ok: #include <wiring_private.h> void setup() { // put your setup code here, to run once: pinMode(PF_0, OUTPUT); } void loop() { // put your main code here, to run repeatedly: PWMWrite(PF_0, 256, 200, 15000); while(1) delay(100); } Saleae Logic16 shows a waveform with width=52.1uS, period=66.68uS, frequency = 14.997KHz. Pretty close. 52.1/66.68 = 0.781343, 256*0.781343 = 200.02 so that looks right. Running this: #include <wiring_private.h> void setup() { // put your setup code here, to run once: pinMode(PF_0, OUTPUT); } void loop() { // put your main code here, to run repeatedly: PWMWrite(PF_0, 256, 200, 15000); delay(100); } ...produces ALMOST the same thing, but every 100ms there's a single pulse whose width is 101.65uS instead of the typical 52.1uS. Running this: #include <wiring_private.h> void setup() { // put your setup code here, to run once: pinMode(PF_0, OUTPUT); } void loop() { // put your main code here, to run repeatedly: PWMWrite(PF_0, 256, 200, 15000); } Produces no waveform at all; the signal goes HIGH after Energia starts and then stays there with no transitions.
×
×
  • Create New...