MSP432 Interrupt With Encoder Count - c

I am using a MSP432 for a project and I would like to know how to generate a interrupt when a incremental encoder has reached a specified count. The interrupt should stop a motor from moving in a particular direction.
Background: I'm using a incremental encoder to control a brush motor. As the brush motor moves left or right, it is mechanically connected to a incremental encoder that counts pulses or "clicks". The incremental encoder is effectively controlling the limit of motion of the motor. Ie, if the encoder reads 20 pulses in the right direction, the motor should stop. My project has two modes of operation controlled by a switch-case statement. The first is a routine mode, and the second is a mode where a user can control the motor with a joystick. Regardless of whichever mode the program is in, the motor should stop when the limit of motion has been reached.
Psedo Code:
Case: Routine Button Mode
{
// Motor executes right, left, right movement routine
digitalWrite(directionMotor,RIGHT); // telling motor what direction to go
analogWrite(pwm_motor2,60); // telling motor to activate at 60% PWM
if(encoder_count == Motion_Limit)
analogWrite(pwm_motor,0); // tell motor to stop
// change direction
digitalWrite(directionMotor,LEFT); // telling motor what direction to go
analogWrite(pwm_motor2,60); // telling motor to activate at 60% PWM
}
Case: Joystick_Control
{
while(analogRead<10) // Joystick is pushed to the left
{
digitalWrite(directionMotor,LEFT); // telling motor what direction to go
analogWrite(pwm_motor2,60); // telling motor to activate at 60% PWM
if(encoder_count == Motion_Limit)
analogWrite(pwm_motor,0); // tell motor to stop
}
while(analogRead>1000) // Joystick is pushed to the right
{
digitalWrite(directionMotor,RIGHT); // telling motor what direction to go
analogWrite(pwm_motor2,60); // telling motor to activate at 60% PWM
if(encoder_count == Motion_Limit)
analogWrite(pwm_motor,0); // tell motor to stop
}
} // end case statement
Again, no matter what mode of operation the program is in, it should stop when the count has been reached. Even when the motion limit has been reached, the program should still allow the joystick control to drive the motor away from the motion limit. That is, if count == 20 on the right limit, I can still drive the motor left. Essentially, the encoder should be tracking the motor at all moments in operation.
Questions:
1. How do I declare a interrupt on a MSP432?
2. Can I use a incremental encoder for as a interrupt? Most examples I've found use a button that outputs a high or low signal as a flag for a interrupt. I'm not sure I can do the same thing with a encoder

Your code looks a lot like Arduino code, to attach an interrupt you should use the Arduino attachInterrupt() function. If you are using a different high level support
library then its documentation should include some interrupt examples.
As to the meat of your question.
Your incremental encoder should have two lines, one to indicate left motion, one to indicate right motion.
You will need to alter the global variable encoder_count up and down as indicated by these lines. This should definitely be done using an interrupt for each line. The interrupt should fire on the edge transition as specified in the encoder's datasheet. There is no difference between triggering on a button edge and an encoder edge (except that buttons are messy and need to be debounced, find a non-debounced example).
If it is very important to stop the motor exactly on the incremental count you can test the values in the incremental encoder increment/decrement interrupts and disable the motor if required.
However it is probably sufficient to do the test as part of your main loop. (Side note: remember you need to handle when the joystick is centered.)
I would also recommend creating a function to control the motor. This abstracts away the implementation details, allowing your main loop to focus on higher level functionality. Ensuring the motor control is always done by one function also allows you to guarantee that your limits are always applied.

Related

DC Motor speed and direction with PWM PSoC

I need to be able to change the direction and speed of a small dc motor using an PSoC LP5 and a L293D. The motor that was used is one of these: https://www.elecrow.com/dc-toy-hobby-motor-130-size-p-265.html. I was able to change the direction without PWM by changing the inputs, this gave me the following table.
But then I also had to change the speed. The schematics for this project can be found below. The PWM period has been set to 1000.
My code looks like this:
int main(void)
{
PWM_1_Start();
for(;;)
{
DIRECTION_Write(0);
PWM_1_WriteCompare(400);
CyDelay(2000);
// 2 seconds clockwise low speed
PWM_1_WriteCompare(0);
CyDelay(2000);
// 1 second nothing
DIRECTION_Write(1);
PWM_1_WriteCompare(400);
CyDelay(2000);
// 2 seconds counter-clockwise low speed
PWM_1_WriteCompare(0);
CyDelay(2000);
// 1 second nothing
}
}
The motor now only turns counter-clockwise, then stops for 5ish seconds en does the same again. I've tried other combinations such as also using SPEED_Write, but didn't get the result I wanted.
Any help is appreciated, thanks in advance :)
I've done exact thing with an L298, which I think is esentially the same as an L293. It's easiest to switch the EN pin and not the IN# pins. Move your PWM to EN1, and then control the direction with IN1 and IN2. When IN1 is 0 and IN2 is 1, the motor moves CCW. When the pins are reversed, it moves in the other direction.
With a PSoC5 you can control both IN# pins with 1 signal. Add a 1-bit Control Register to your schematic and connect it to 2 pin components Then put a NOT gate between the second Gpio and the control register. Then you can write to the Control Register, and both Gpios will be toggled at the same time, but will always be the opposite of each other.

Stm32 PWM sampling

I have a timer set up in the cube to make a PWM... Just setting ARR and CCR to different values.
I'm using the callback functions for both events that HAL set up for me, I think one is for the CCR compare event and the other is for the ARR compare event.
Anyway, in both of those I'm toggling a GPIO port like this:
GPIOC->ODR = foo;
Anyway, I want to sample the values of 3 ADC1 channels during the high pulse, but I'm not sure how to do that accurately. I'm using DMA in circular mode with the ADC1 right now.
I don't want to sample immediately after setting the pins, because there's propagation delay, noise, etc.
So, I want to:
Set the pins, wait a very short and constant amount of time, sample all the channels and then do some math on the results.
This is for a Bldc motor controller, by the way... Im trying to sample the BEMF on the positive side of the PWM drive signal, and I'm driving it at 18khz, 5% duty cycle, so my pulse lasts for 2.7us.
I'm not sure how to handle or debug this because it's so timing related and I need the motor to deliver the signal that I'm using... If the motor isn't spinning, then there's no feedback signal to work on. It sure seems like I'm trying to do too much in the timer ISRs, though.
Sorry I can't post any code right now... Im at the grocery store, but I'll put some up when I get back.

STM32 - TIM2_ETR pin, connected to pin PA0 (button), incrementing the timer in strange way

I am trying to implement PWM LED dimming in 6 stages, where each stage in more bright, based on clicking button, which increments external pin, which serves the value to timer.
I am facing a problem, that sometimes, value variable is too large than it should be and skips some levels of brightness. For example, value increments: 1,2,3, then jumps to 6,7, etc.
Can anybody pinpoint where is the mistake I am making.
Here is the code:
//EDIT: code removed, because it is a school assignment
This looks like contact bouncing. When input is processed by the CPU, a simple way to solve it is to disable input for a certain duration after an event is detected. Since you directly control timer input from a button, you may not have much control. However, I would experiment with the ETF field of the SMCR register (which in your case is likely set by the sClockSourceConfig.ClockFilter field) and the clock divisor CKD of the CR1 register (which seems like htim2.Init.ClockDivision in your code) (sorry, I am not familiar with STM libraries).

counting pulses from a Mechanical water meter with Two inputs

I want to count the number of pulses from a mechanical water meter using an STM32L Microcontroller. The outputs of the water meter are from TWO REED switches.
The operation of the Switches is explained as follows:
the two Reed switches would be operated "ON" OR "OFF" respectively by the magnet fitted to the pointer or gear during its running on the register, but never "ON" at the same time.
The two Reed switches operate two "ON" and two "OFF" in one round of the pointer/the gear means one signal output.
How can I read in the two inputs and be able to count the number of pulses in C? Note: 1 pulse = 100 liters.
I'm not sure what you're actually asking because what you seem to be asking is so simple whether you poll or use edge triggered interrupts. The main issue is debouncing the switch signals. For debouncing you should determine the maximum flow rate of your meter, which you don't really care about directly, but it will let you calculate the maximum switch period. Use some significant portion of the minimum on or off time to perform your debounce.
The point of having two switches 180 degree apart, such that only one switch can be made at a time, is that no switch debouncing is needed.
In practice the code would need to be more sophisticated, but the basic algorithm can be represented by this:
while(1) {
while(switchA() == 0); // wait for switch A to be made
litres += 100; // clock up unit volume
display(litres); // tell the user
while(switchB() == 0); // wait for switch B to be made
}
It does not matter how many pulses come from a reed switch, when it is near the magnet - all but the first pulse will be ignored, because the algorithm is then looking at the other switch.

Why doesn't this function properly toggle an LED on and off?

I am using the Atmel SAM3x8E micro-controller and trying to do a simple LED toggle when I press a button. I am using pull-up configuration button to trigger an interrupt routine.
This is the initialization for the interrupt:
// Set button pins as pull-up inputs
pio_set_input(PIOC, BUTTON_1, PIO_PULLUP);
pio_set_input(PIOC, BUTTON_2, PIO_PULLUP);
// Configure button input pin interrupt mode and handler (Rising Edge)
pio_handler_set(PIOC, ID_PIOC, BUTTON_1, PIO_IT_RISE_EDGE, button_press_handler);
pio_handler_set(PIOC, ID_PIOC, BUTTON_2, PIO_IT_RISE_EDGE, button_press_handler);
// Enable the interrupts
pio_enable_interrupt(PIOC, BUTTON_1);
pio_enable_interrupt(PIOC, BUTTON_2);
NVIC_EnableIRQ(PIOC_IRQn);
NVIC_EnableIRQ(PIOC_IRQn);
Then this is the interrupt routine:
// Interrupt handler for button press
void button_press_handler(uint32_t a, uint32_t b)
{
pio_toggle_pin_group(PIOC, BLUE_LED4); // NOT TOGGLING LED (ONLY TURNS IT ON)
}
Yet when I run it, I cannot get the LED to toggle. It simply turns on and stays on. The function that pio_toggle_pin_group calls is the following:
* \param p_pio Pointer to a PIO instance.
* \param ul_mask Bitmask of one or more pin(s) to configure.
*/
void pio_toggle_pin_group(Pio *p_pio, uint32_t ul_mask)
{
if (p_pio->PIO_ODSR & ul_mask) {
/* Value to be driven on the I/O line: 0. */
p_pio->PIO_CODR = ul_mask;
} else {
/* Value to be driven on the I/O line: 1. */
p_pio->PIO_SODR = ul_mask;
}
}
Any ideas as to why my LED is not toggling the way I want? I've refereed to the Atmel ASF documentation but I still cannot figure this out.
I cannot help you with the actual function calls, but suppose you use a edge interrupt. As far as I see, you call an interrupt handler for each rising edge. However, after the first rising edge, you need to trigger on button release, which would be a falling edge, so you need to change the edge within the interrupt handler.
But you must take into account that mechanical buttons do not generate a clean, single edge when pressed or released. It does instead bounce. For normal momentary contact buttons with pullup (or down) resistor, this results in multiple pulses for each event, so the LED may turn on/off multiple times and stay in an arbitrary state which might - by chance - be "on" far by most times. If available, check with an oscilloscope.
This can be circumvented in hardware by a capacitor or in software using a timer with a dead time after the relevant edge before reacting to any other button event.
The dead time depends on the type of button, but typical values are 5 to 20ms and should be mentioned in the datasheet of the button. If in doubt, use the highest acceptable value.
This is what ended up working for me:
// Interrupt handler for button press
void button_press_handler(uint32_t a, uint32_t b)
{
// Turn the LED's on or off
if (pio_get(PIOC, PIO_TYPE_PIO_OUTPUT_0, BLUE_LED4))
pio_clear(PIOC, BLUE_LED4);
else
pio_set(PIOC, BLUE_LED4);
}
and here is the called "set" function which worked to turn the LED on:
void pio_set(Pio *p_pio, const uint32_t ul_mask)
{
p_pio->PIO_SODR = ul_mask;
}
To avoid those random spikes generated by the bouncing button try to use a debouncing input filter. On the sam3x8e you can enable this by setting the register PIO?->PIO_DIFSR to BUTTON_1 or BUTTON_2 in your case.
Also clear the status register of the PIO interrupt by reading PIO?->PIO_ISR. This clears all input changes and enables the interrupt to be entered multiple times.

Resources