What happened before:
For almost a year I have a vintage turntable from Perpetuum Ebner at home. It's not mine. It belongs to someone that asked me to fix it. And it turned out that fixing the motor would cost too much. I asked the owner to collect the tt. That hasn't happened yet and the machine is collecting dust at my home.
So I'll take the freedom to attempt a non-intrusive repair with modern components. I'm also thinking about making it an Enchanted Objects Design Challenge. |
In post 1 I present the turntable, talk about my previous repair attempts and brainstorm on some modding ideas.
In post 2 I'm measuring up the different gears, pulleys and wheels, and I calculate the speed of the original motor.
Post 3 is the first post on motor control. I'm reviewing the operation of the Infineon DC Motor Control Shield.
In post number 4 I'm using the enhanced PWM module of my The specified item was not found. Hercules LaunchPad to test drive the motor.
The table is spinning for the first time in post 5.
In this article I'm building a speed adjuster.
You can regulate the DC motor's speed by varying the duty cycle of the signal you send to the motor driver.
And that's what I'm doing here. I react on button pushes to increase or decrease the duty cycle, and that speed up or slow down the motor.
I have documented the Hercules LaunchPad configuration in part 4, so I won't repeat it here. Follow the steps in that article if you want to replicate the exercise to get PWM out of the LaunchPad.
Here, I'll explain my steps to influence the speed.
And that's straightforward. Once you have your PWM working, you can change it by calling a simple API function:
etpwmSetCmpA(etpwmREG1, uDutyCycle)
(If you are more into direct registry manipulation, that's possible too. Just have a peek at what's happening with etpwm->CMPA in the file etpwm.c. That file was generated automatically if you followed the steps in post 4)
In the drawing below, you see that when you slow down the PWM signal's duty cycle, the same happens to the output of the motor shield. The percentages of the half H-bridge's output signal is in sync with the signal coming from our LaunchPad.
Enabling the GIO API library and the two LaunchPad User Buttons
Most pins on the RM46 microcontroller are multiplexed. And the pins for the two user buttons are no exception to that.
Button A is connected to pin 55 . The pin can be used as
- MIBSPI3NCS_0
- AD2EVT
- GIOB_2
- EQEP1I
Button B is pin 41.
- N2HET1_15
- MIBSPI1NCS_4
- ECAP1
For pin 55, the decision on the correct function is easy. The pin has a dedicated GIO port (GIOB pin 2), so we'll use that.
But you'll notice that when you go to the HALCoGen PINMUX tab, you can't select that particular pin:
That is because this particular pin has to be enabled via the special Pin Muxing options (I don't know why yet; I haven't read that part of the spec ).
When you select the 'Use GIOB_2' box, the pin is magically selected on the previous tab.
We'll be able to check if the button is down when the following call returns FALSE:
gioGetBit(gioPORTB, 2)
We MUX button B as NHET (NHET1 pin 15). This port/pin combination can be used in the GIO API as a generic GIO pin.
This one is checked by calling:
gioGetBit(hetPORT1, 15)
We have to enable two additional drivers in HALCoGen (we enabled the PWM driver in part 4):
GIO, for two reasons. When you selecting a driver, HALCoGen generates the API code. And we need that API code to poll the button state.
The second reason is that we're accessing one of the GIO pins (GIOB_2 for button A).
NHET1, because we are accessing the pin NHET1_15 for button B.
In our code, we'll have to call the xxxInit() function of both NHET and GIO, so that these two periferals are in their start state and correctly initialized.
gioInit(); // user button A is on the GIOB port, 2 hetInit(); // user button B is on the HET1 port, 15
The only thing left now is to check the button at regular times, and adjust the speed based on that.
I used a variable to keep the button state.
uint32 uBtn_Usr = 0U;
When you click and release button A, the bit on position 0 is set. A click and release of button B sets bit 1.
I used a very naive debounce mechanism. I've intentionally kept that part of the code simple for this proto setup.
You can find it in the full code at the end.
When the firmware has detected that a button was clicked, it adjusts the PWM duty cycle (in my example, 40U is 1% change).
void adjustDutyCycle() { if ((uBtn_Usr & BTN_USRA) && (uDutyCycle < 4000U)) { uDutyCycle += 40; } if ((uBtn_Usr & BTN_USRB) && (uDutyCycle > 0U)) { uDutyCycle -= 40; } etpwmSetCmpA(etpwmREG1, uDutyCycle); uBtn_Usr = 0; }
The effect of pushing the button B 2 times is shown on this scope capture:
The code:
#include "sys_common.h" #include "etpwm.h" #include "het.h" #include "gio.h" // buttons for speed up and down uint32 uBtn_Usr = 0U; #define BTN_USRA 1U #define BTN_USRB 2U uint16 uDutyCycle; bool buttonCheck(); void adjustDutyCycle(); void main(void) { uDutyCycle = 2000U; gioInit(); // user button A is on the GIOB port, 2 hetInit(); // user button B is on the HET1 port, 15 etpwmInit(); while (1) { if (buttonCheck()) { adjustDutyCycle(); } } } bool buttonCheck() { static uint32 uBtn_Usr_Bounce = 0U; if ((!(uBtn_Usr_Bounce & BTN_USRA)) && !gioGetBit(gioPORTB, 2)) { uBtn_Usr_Bounce += BTN_USRA; } if ((!(uBtn_Usr_Bounce & BTN_USRB)) && !gioGetBit(hetPORT1, 15)) { uBtn_Usr_Bounce += BTN_USRB; } if ((uBtn_Usr_Bounce & BTN_USRA) && gioGetBit(gioPORTB, 2)) { uBtn_Usr += BTN_USRA; uBtn_Usr_Bounce -= BTN_USRA; } if ((uBtn_Usr_Bounce & BTN_USRB) && gioGetBit(hetPORT1, 15)) { uBtn_Usr += BTN_USRB; uBtn_Usr_Bounce -= BTN_USRB; } return (uBtn_Usr > 0); } void adjustDutyCycle() { if ((uBtn_Usr & BTN_USRA) && (uDutyCycle < 4000U)) { uDutyCycle += 40; } if ((uBtn_Usr & BTN_USRB) && (uDutyCycle > 0U)) { uDutyCycle -= 40; } etpwmSetCmpA(etpwmREG1, uDutyCycle); uBtn_Usr = 0; }
disclaimers:
The trained eye will see that there is not much to learn in this code on proper state machine design and debouncing. So be it.
The variable for the button state doesn't have to be volatile, because I'm not setting it in an interrupt. It's only manipulated in the main processing thread.
Top Comments