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.
Related
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.
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.
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).
In my project, I have three buttons where each triggers a function.
This function have to do two things, one action when the button is pressed (actually working), but I want to add a second functionality, in which if the button is pressed for more then 3 seconds, it do something, like calling a function.
So far, i'm initializing the interruption:
attachInterrupt(0, footOne, Falling);
and the function is:
void footOne(){
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// Debounce
if (interrupt_time - last_interrupt_time > 200){
//Do things
if(debug==1){Serial.println("Button 1 pressed!");}
}
last_interrupt_time = interrupt_time;
}
Now I want to know how I can change the function to add the possibility if the button is pressed for longer then 3 seconds...
Remembering that this function is called from the interrupts.
Thank you!
With the way you have your program structured right now, you would need an external RTC to carry out this task. Once you initiate an interrupt, the internal clock on the arduino is compromised and therefore cannot be used reliably until leaving the interrupt. Is your implementation so time sensitive that an interrupt is required? Because if it isn't, polling the state of the button and then comparing the time elapsed would be a pretty easy implementation. If you want to stick with interrupts however, I'd take a look at volatile variables as a way to talk between your main loop and your interrupt functions.
Im using C language for a PIC18F to produce tones such that each of them plays at certain time-interval. I used PWM to produce a tone. But I don't know how to create the intervals. Here is my attempt.
#pragma code //
void main (void)
{
int i=0;
// set internal oscillator to 1MHz
//OSCCON = 0b10110110; // IRCFx = 101
//OSCTUNEbits.PLLEN = 0; //PLL disabled
TRISDbits.TRISD7 = 0;
T2CON = 0b00000101; //timer2 on
PR2 = 240;
CCPR1L = 0x78;
CCP1CON= 0b01001100;
LATDbits.LATD7=0;
Delay1KTCYx(1000);
while(1);
}
When I'm doing embedded programming, I find it extremely useful to add comments explaining exactly what I'm intending when I'm set configuration registers. That way I don't have to go back to the data sheets to figure out what 0x01001010 does when I'm trying to grok the code the next time I have to change it. (Just be sure to keep the comments in sync with the changes).
From what I can decode, it looks like you've got the PWM registers set up, but no way to change the frequency at your desired intervals. There are a few ways to do it, here are 2 ideas:
You could read a timer on startup, add the desired interval to get a target time, and poll the timer in the while loop. When the timer hits the target, set a new PWM duty cycle, and add the next interval to your target time. This will work fine, until you need to start doing other things in the background loop.
You could set timer0's count to 0xFFFF-interval, and set it to interrupt on rollover. In the ISR, set the new PWM duty cycle, and reset timer0 count to the next interval.
One common way of controlling timing in embedded processes looks like this:
int flag=0;
void main()
{
setup_interrupt(); //schedule interrupt for desired time.
while (1)
{
if (flag)
{
update_something();
flag = 0;
}
}
Where does flag get set? In the interrupt handler:
void InterruptHandler()
{
flag = 1;
acknowledge_interupt_reg = 0;
}
You've got all the pieces in your example, you just need to put them together in the right places. In your case, update_something() would update the PWM. The logic would look like: "If it's on, turn it off; else turn it on. Update the tone (duty cycle) if desired"
There should be no need for additional delays or pauses in the main while loop. The goal is that it just runs over and over again, waiting for something to do. If the program needs to do something else at a different rate, you can add another flag, which is triggered completely independently, and the timing of the two tasks won't interfere with each other.
EDIT:
I'm now confused about what you are trying to accomplish. Do you want a series of pulses of the same tone (on-off-on-off)? Or do you want a series of different notes without pauses (do-re-me-fa-...)? I had been assuming the latter, but now I'm not sure.
After seeing your updated code, I'm not sure exactly how your system is configured, so I'm just going to ask some questions I hope are helpful.
Is the PWM part working? Do you get the initial tone? I'm assuming yes.
Does your hardware have some sort of clock pulse hooked up to the RA4/T0CKI pin? If not, you need T0 to be clock mode, not counter mode.
Is the interrupt being called? You are setting INT0IE, which enables the external interrupt, not the timer interrupt
What interval do you want between tone updates? Right now, you are getting 0xFFFF / (clock_freq/8) You need to set the TMR0H/L registers if you want something different.
What's on LATD7? and why are you clearing it? Are you saying it enables PWM output?
Why the call to Delay()? The timer itself should be providing the needed delay. There seems to be a disconnect about how to do timing. I'll expand my other answer
Where are you trying to change the PWM frequency? Don't you need to write to PR2? You will need to give it a different value for every different tone.
"Halting build on first failure as requested."
This just says you have some syntax error, without showing us the error report, we can't really help.
In Windows you can use the Beep function in kernel32:
[DllImport("kernel32.dll")]
private static extern bool Beep(int frequency, int duration);