In this blog I'm using a scroll wheel from a defunct mouse to control a GaN power stage.
A Hercules LaunchPad has the duty to read the scroll wheel and drive the GaN. I'm controlling a humble 15 watt incandescent lamp here, but we wouldn't have to change a single line of code to control a 500 watt motor. |
Goal
I'm trying to control high powers with a jellybean scroll wheel. Scrolls wheel are common these days.
You find them in your mouse - between the left and right button. And in many other places like oscilloscopes and car entertainment systems.
The power is regulated by a Gallium Nitride half-bridge.
Setup
There are 8 items that play a role in my test configuration:
- An LMG5200 GaN half-bridge power stage.
Controls the amount of power going to the load
I received mine from TI after attending a GaN seminar and answering right on the quiz. - A Hercules TMS570LC43x LaunchPad - but this could be any controller of the
Hercules family that has one quadrature encoder (eQEP) and one PWM (etPWM) module.
This was donated by TI. - A DC power supply that can deliver a low current between 5.5 V and 6.5 V.
It will provide the LMG5200 bias voltage. It doesn't have to be very stable. - A DC power supply that can deliver a high current between 10 V to 60 V.
That's the one that will provide the power to the load, and that will be controlled by the LMG5200.
I use 20 V, 1 A. - A rotary (quadrature) encoder. I'm using one from a mouse scroll wheel.
- An optional DC current meter with an Amp range. We'll be burning approximately 1 A here.
Most DMMs have a 10 A range that will do just fine. - An optional DC volt meter. We're working in the range between 0 V and 20 V.
Again any DMM will do. - An optional oscilloscope to look at the signal going into the LMG5200. We'll be working at 1 MHz.
The scope is not neccesary, but it will enable you to see and measure the attributes of that input signal (frequency, duty cycle).
Design Decisions
All things are done for pragmatic reasons.
I'm using a Hercules LaunchPad and an LMG5200 GaN module because that's the components that I'm experimenting with.
I select a 1 MHz PWM signal because I want to see how the GaN half-bridge holds up at high frequencies
(and that's the actual reason for my setup - I didn't build it for this blog, but as a testbed).
You 'll see in the firmware that I'm defining a scroll range of 0 - 55. That's because I draw the maximum of 1 A from my power supply at that point.
The software is layered on a level that fits this exercise.
Abstraction level
I've applied a modest abstraction level in my design.
My only purpose is to have specific logic out of the main file, and to build modules with useful functions for this particular project only.
The Quadrature Encoder and PWM logic have their own source files with a few functions, but this isn't an abstraction lib that allows different peripheral or generic changes.
Module initialization and setup done in usual tools (HALCoGen in this case). I have no intention to take that any of that over in abstraction layer.
Firmware and Module Setup
Each module requires some setup. Activation and configuration of peripherals is done in Hercules HALCoGen.
Generic setup
We'll use SCI for data logging during debug. This is so simple - and only related to test code - that I haven't bothered to abstract it out.
#include "HL_sci.h" // ... void main(void) { // ... sciInit(); //Initializes the SCI (UART) module // ... }
Rotary Encoder
Quadrature information is decoded with the eQEP module. We're using eQEP instance #2 because that is broken out to connector J10.
At least EQEP2 Pin A and B need to be enabled.
rotary.h
#ifndef ROTARY_ROTARY_H_ #define ROTARY_ROTARY_H_ #include "HL_eqep.h" void rotaryInit(boolean wrap); uint32_t getRotaryPosition(); #endif /* ROTARY_ROTARY_H_ */
rotary.c
#include "rotary.h" #define EQEP_BORDER_CHECK (EQEP2_QPOSMAX_CONFIGVALUE / 4) uint32_t uRotary2LastVal = EQEP2_QPOSINIT_CONFIGVALUE; // no need to set volatile, only used in this function uint32_t uRotary2 = EQEP2_QPOSINIT_CONFIGVALUE; // no need to set volatile, only used in this function boolean bWrap; void rotaryInit(boolean wrap) { bWrap = wrap; QEPInit(); /* Enable Position Counter */ eqepEnableCounter(eqepREG2); /* Enable Unit Timer. */ eqepEnableUnitTimer(eqepREG2); /* Enable capture timer and capture period latch. */ eqepEnableCapture(eqepREG2); } uint32_t getRotaryPosition() { /* Status flag is set to indicate that a new value is latched in the QCPRD register*/ if((eqepREG2->QEPSTS & 0x80U) !=0U) { uRotary2 = eqepREG2->QPOSLAT; // address border conditions to avoid shootthrough around min or max if (!bWrap && (uRotary2 > EQEP2_QPOSMAX_CONFIGVALUE - EQEP_BORDER_CHECK) && (uRotary2LastVal < EQEP_BORDER_CHECK)) { uRotary2 = 0x0U; eqepREG2->QPOSCNT = 0x0U; eqepREG2->QPOSLAT = 0x0U; } else if (!bWrap && (uRotary2 < EQEP_BORDER_CHECK) && (uRotary2LastVal > EQEP2_QPOSMAX_CONFIGVALUE - EQEP_BORDER_CHECK)) { uRotary2 = EQEP2_QPOSMAX_CONFIGVALUE; eqepREG2->QPOSCNT = EQEP2_QPOSMAX_CONFIGVALUE; eqepREG2->QPOSLAT = EQEP2_QPOSMAX_CONFIGVALUE; } /* Clear the Status flag. */ eqepREG2->QEPSTS |= 0x80U; uRotary2LastVal = uRotary2; } return uRotary2; }
PWM
For pulse width modulation , the Hercules family has ePWM modules.
At least PWM1 A needs to be enabled. Don't forget to check the Special Muxing settings.
pwm.h
#ifndef PWM_PWM_H_ #define PWM_PWM_H_ #include "HL_etpwm.h" void pwmInit(); void setPwmDutyCycle(uint32_t dutyCycle); #endif /* PWM_PWM_H_ */
pwm.c
#include "pwm.h" #define DUTYCYCLE_STEP 1U void pwmInit() { etpwmInit(); } void setPwmDutyCycle(uint32_t dutyCycle) { etpwmSetCmpA(etpwmREG1, DUTYCYCLE_STEP * dutyCycle); }
Glue
The main file stitches everything together.
HL_sys_main.c
#include "HL_sys_common.h" #include "HL_sci.h" #include "HL_sys_core.h" #include <stdlib.h> #include "rotary.h" #include "pwm.h" unsigned char command[8]; void main(void) { uint32_t NumberOfChars; uint32_t uRotaryLastVal; uint32_t uRotary; sciInit(); //Initializes the SCI (UART) module rotaryInit(false); // no wrap around pwmInit(); _enable_interrupt_(); uRotary = getRotaryPosition(); uRotaryLastVal = uRotary; while(1) { uRotary = getRotaryPosition(); if (uRotary != uRotaryLastVal) { uRotaryLastVal = uRotary; NumberOfChars = ltoa(uRotary,(char *)command); sciSend(sciREG1, NumberOfChars, command); //Sends the eQEP data sciSend(sciREG1, 2, (unsigned char *)"\r\n"); //Sends new line character setPwmDutyCycle(uRotary); } } }
Results
The system will start up with the lamp fully dimmed. You can control the brightness by rotating the encoder.
Use the current meter to check the current coming from the power supply, and the current going to the lamp.
Check how it changes when rotating the scroll wheel.
Attach the voltmeter and check how the output voltage changes.
Attach your scope to the PWM signal. If you have a math function, you can see the duty cycle change and the frequency staying stable.
The CCS / HALCoGen projects are attached to the blog.
Top Comments