PIC18f452 port listening issue - microcontroller

Why doesn't this code work? All that I want is portb to toggle when I press a button.
main
trisb=0
trisa=0xff
while true
if ra0<>0 then
portb = not portb
end if
wend .end

I'm not sure what that is; is it pseudocode?
Anyway, you need to trigger on the CHANGE from RA0 == 0 to RA0 == 1. As written, as long as RA0 == 1 then your PORTB will toggle every time through the loop.
Here's an example in C:
int main(void)
{
unsigned char bButtonValue = 0;
TRISA = 0xFF;
TRISB = 0x00;
while (1)
{
if (RA0 != bButtonValue)
{
bButtonValue = RA0;
if (bButtonValue == 1)
{
PORTB= ~PORTB;
}
}
}
}
Keep in mind that for a real app, you would want to debounce the switch input (make sure it's stable for a few samples before triggering the change event.

Related

controlling motor speed in MSP430

#include <msp430.h>
int forward_cnt = 0;
int reverse_cnt = 0;
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
P1DIR |= BIT0;
P1OUT &= ~BIT0; //LED 1.0
P1REN |= BIT1;
P1OUT |= BIT1;
P2REN |= BIT1;
P2OUT |= BIT1; //switches
P1IE |= BIT1;
P1IES |= BIT1; //falling edge(when button is pushed)
P1IFG &= ~BIT1;
P2IE |= BIT1;
P2IES |= BIT1;
P2IFG &= ~BIT1;
//PWM Settings
P2DIR |= (BIT5 | BIT4);
P2SEL |= (BIT5 | BIT4); //SET TO 1
TA2CTL = TASSEL_2 + MC_1 + TACLR;
TA2CCR0 = 1000;
TA2CCTL2 = OUTMOD_6;
TA2CCR2 = 0;
TA2CCTL1 = OUTMOD_6;
TA2CCR1 = 0;
__bis_SR_register(GIE);
while(1)
{
}
}
#pragma vector = PORT2_VECTOR
__interrupt void Port_2_1(void)
{
forward_cnt++;
switch(forward_cnt % 3){
case 0 :
TA2CCR2 = 400;
TA2CCR1 = 0;
P1OUT |= BIT0;
break;
case 1 :
TA2CCR2 = 800;
TA2CCR1 = 0;
P1OUT |= BIT0;
break;
case 2 :
TA2CCR2 = 0;
TA2CCR1 = 0;
P1OUT &= ~BIT0;
break;
}
P2IFG &= ~BIT1; //interrupt end, so go back with IFG clear
}
#pragma vector = PORT1_VECTOR
__interrupt void Port_1_1(void)
{
TA2CCR2 = 0;
TA2CCR1 = 0;
forward_cnt = 0;
P1IFG &= ~BIT1;
}
I want to control motor speed by slow, fast, stop with clicking switch P2.1(external interrupt) and keep turned on LED when motor is working.
But some problem is coming out. It seems like forward_cnt value is changing while I release button sometimes. It works different while I keep hold on button sometimes.
For example, It should change its speed once when I click button once with my expectation but sometimes it changes its speed when I push, and also changes when I release button. I think interrupt is not working well. How can I fix this clearly?
The interrupt may be working as intended the issue is more likely with the button itself. It seems you are running into the classic switch bouncing problem. When a button is clicked it is not a clean transition from one logic state to the next. Referencing the picture posted below a single press of the button can cause multiple rising and falling edges. Which in the case of your code will cause multiple interrupts to occur changing your forward_cnt variable almost unpredictably.
There are many different solutions to this problem but the most straightforward is to add in a delay after the button is pressed to allow for the bouncing of the switch to settle. After the bouncing has settled you can then increment you forward_cnt variable.
scope capture of button when pressed
Push buttons are noisy by nature, so you need to deal with that. For example, if you were to sample the input pin from the push button, you would find it will toggle many times before settling down on the pressed value. To deal with the noise, you should add a debouce function to the button press event.
The easiest way to do this is as mmeadwell mentioned, which is to add some delay. Normally, a good design would use hardware timer for this, but in a pinch, you can just implement a software delay. For example, in your Port_2_1(void) interrupt routine, add this code:
#pragma vector = PORT2_VECTOR
__interrupt void Port_2_1(void)
{
// Variable for button state
int button_now = 0;
int button_last = 0;
// Implement a debounce delay for 150 ms
for (int delay = 0; delay < 150; delay++)
{
// Sample the current button state
button_now = (P2IN & BIT1) ? 1 : 0;
// Reset delay when state of button changes
if (button_now != button_last) delay = 0;
// delay 1 ms (must know your CPU clock rate)
// using MSP430 compiler intrinsic function
__delay_cycles(CPU_CLOCKS_PER_US * 1000);
button_last = button_now;
}
// Only continue if button is currently pressed
if (button_now == 0) return;
// now process the button press event
// remaining code is unchanged
forward_cnt++;
switch(forward_cnt % 3){
Since the button P1.1 is only used to stop the PWM, then no debounce is necessary. This noise will not change the outcome once stopped.

create a timed 3 state push button in arduino

Due to a shortage of pins on a esp8266 in arduino, I need a way to detect a button where;
momentary press runs snooze()
15 sec press runs conf_Desk()
30 sec press runs calibration()
the preconfig;
int buttonPin = D7;
pinMode( buttonPin , INPUT_PULLUP);
All while allowing the main loop to function.
If I trap an interrupt it stops cycling the loop(), a few millisec delays are OK but seconds of delay is too much.
The functions are already written I just can't seem to come up how to track and confirm the hold length to call the right function based on the right timing without stopping other process that must stay cycling.
Using an interrupt is, IMHO, overkill. Interrupts are made for when you need to reply to a stimulus quickly, and a button press is something slow. Unless your loop is blocking, thing that I highly discourage.
ADDITION: as Patrick pointed out in the comments, there is in fact another reason to use interrupts: sleep mode. In fact, if you want to go into sleep mode and wake with a button, you have to use interrupts to wake later. However usually you have to do something continuously and not only reply to the button inputs. If you can't go into sleep mode, using an interrupt for button detection is still overkill in my opinion.
So, if you properly designed your loop not to block, here is a brief part of code doing what I think you should implement:
uint8_t buttonState;
unsigned long lastPressTime;
void setup()
{
...
buttonState = digitalRead(buttonPin);
lastPressTime = 0;
}
void loop()
{
uint8_t currRead = digitalRead(buttonPin);
if (buttonState != currRead)
{ // Button transition
buttonState = currRead;
if (buttonState == LOW)
{ // Button pressed, start tracking
lastPressTime = millis();
}
else
{ // Button released, check which function to launch
if (lastPressTime < 100)
{} // Discard (it is just a bounce)
else if (lastPressTime < 15000)
snooze();
else if (lastPressTime < 30000)
conf_Desk();
else
calibration();
}
}
...
}
Since you made three very distant intervals though, I think that this part better suits your needs:
if ((lastPressTime > 100) && (lastPressTime < 7000))
snooze();
else if ((lastPressTime > 12000) && (lastPressTime < 20000))
conf_Desk();
else if ((lastPressTime > 26000) && (lastPressTime < 40000))
calibration();
So you define validity ranges, so if someone presses the button for 10 seconds nothing happens (this is useful because if someone presses the button for 14.9 seconds in the previous code it will trigger the snooze function).
i would use a simple state machine structure with two global vars to avoid complex nested logic:
int buttonDown = 0;
unsigned long buttonStart;
void loop(){
int snapshot = digitalRead(buttonPin);
if(!buttonDown && snapshot ){ //pressed, reset time
buttonDown = 1; // no longer unpressed
buttonStart = millis(); // when it was pressed
}
if(buttonDown && !snapshot ){ //released, count time
buttonDown = 0; // no longer pressed
int duration = millis() - buttonStart; // how long since pressed?
// now the "event part"
if(duration>30000) return calibration();
if(duration>15000) return conf_Desk();
snooze();
}
sleep(1); // or whatever
}
Interrupt service routines should be a short as possible. You don't have to wait inside the ISR and suspend your main loop for seconds.
Just use two different ISRs for a rising and a falling edge.
When the button is pressed, ISR1 starts a timer, when it is released ISR2 stop it and triggers whatever is necessary depending on the time passed.
Make sure your button is debounced.
https://www.arduino.cc/en/Reference/attachInterrupt
Another way to do this is with a pointer-to-function based state machine.
The advantage on this is that you can easily introduce more functionalities to your button (say, another function called at 45 seconds).
try this:
typedef void(*state)();
#define pressed (millis() - lastPressed)
void waitPress();
void momentPress();
void shortPress();
void longPress();
state State = waitPress;
unsigned long lastPressed;
int buttonState;
int buttonPin = 7;// or whathever pin you use
void snooze(){} // stubs for your functions
void conf_Desk(){}
void callibration(){}
void waitPress()
{
if (buttonState == HIGH)
{
lastPressed = millis();
State = momentPress;
return;
}
else
return;
}
void momentPress()
{
if (buttonState == LOW)
{
snooze();
State = waitPress;
return;
}
if (pressed > 15000)
State = shortPress;
return;
return;
}
void shortPress()
{
if (buttonState == LOW)
{
conf_Desk();
return;
}
if (pressed > 30000)
State = longPress;
return;
return;
}
void longPress()
{
if (buttonState == LOW)
{
callibration();
return;
}
return;
}
void loop()
{
buttonState = digitalRead(buttonPin);
State();
}

Reading input pin of pic microcontroller with MIKROC

I need to write a program that will check if an input pin of PIC has a voltage. If a voltage exists then it will give voltage to a selected output pin like PORTB.RB1=1;. Else it will give voltage to other selected output pin like PORTC.RC1=1;.
Is it possible? I have tried to do this, but it does not work .
void main() {
TRISB=0;
TRISA=1;
TRISC=0;
while(1){
delay_ms(500);
// PORTB=0;
if(PORTA==1){
PORTB.RB1 =1;
}
else{
PORTC.RC1 =1;
}
}
}
You have not turned off the other port output, and you have not isolated the input pin of PORTA. If it's bit 0 the mask is 1, if it's bit 1 the mask is 2, etc.
void main() {
TRISB=0;
TRISA=1;
TRISC=0;
while(1){
delay_ms(500);
if(PORTA & 1){
PORTB.RB1 =1;
PORTC.RC1 =0;
}
else{
PORTB.RB1 =0;
PORTC.RC1 =1;
}
}
}
The PORTA and the PORTE are analog ports. If you want to use them as Digital Input, you must Prevent PIC to use them as analog inputs.
You must add this instruction:
ADCON1=0x06;
before you set the PORTA or PORTE as input.
This code works successfully:
void main()
{
ADCON1=0x06;
TrisA=1;
TrisE=1;
TrisC=0;
PortC=0;
while (1)
{
if (PortA.B0==1)
PortC.B0=1;
else
PortC.B0=0;
if (PortA.B1==1)
PortC.B1=1;
else
PortC.B1=0;
if (PortA.B2==1)
PortC.B2=1;
else
PortC.B2=0;
if (PortA.B3==1)
PortC.B3=1;
else
PortC.B3=0;
if (PortA.B5==1)
PortC.B4=1;
else
PortC.B4=0;
if (PortE.B0==1)
PortC.B5=1;
else
PortC.B5=0;
if (PortE.B1==1)
PortC.B6=1;
else
PortC.B6=0;
if (PortE.B2==1)
PortC.B7=1;
else
PortC.B7=0;
}
}
hardware connection : wire input port's bit with 5v supply and switch.
after this connect it with pull down resistors.
void main(){
TRISB = 1; //set portB as input
TRISC = 0; //set portC as output
while(1){
if(PORTB.B0 == 0){ //if RB0 == 0 ?
PORTC.F0 = 1; //set RC0 = 1 ,(high)
}else PORTC.F0 = 0; //set RC0 = 0 ,(low)
if(PORTB.B1 == 0){ //if RB1 == 0 ?
PORTC.F1 = 1; //set RC1 = 1 ,(high)
}else PORTC.F1 = 0; //set RC1 = 0 ,(low)
//set if else block for numbers of bit as you want.....
}
}
note that pic port A is anlalog input default and if you want to use this port as digital port change the ADCON registers and follow datasheet.

Having trouble getting my stepper motor to respond to my button sensor in Arduino sketch

I'm using the AccelStepper library to control my stepper motor, and I'm having difficulty figuring out how to get my motor to stop when my button is pushed.
I can get the motor to stop once it completes its entire movement from the moveTo command, but I can't get it to stop before it finishes. I've tried using an if statement nested within the while loop I'm using to get the motor to run, but no dice.
The code as it stands is as follows:
#include <AccelStepper.h>
const int buttonPin=4; //number of the pushbutton pin
int buttonState=0;
int motorSpeed = 9600; //maximum steps per second (about 3rps / at 16 microsteps)
int motorAccel = 80000; //steps/second/second to accelerate
int motorDirPin = 8; //digital pin 8
int motorStepPin = 9; //digital pin 9
int state = 0;
int currentPosition=0;
//set up the accelStepper intance
//the "1" tells it we are using a driver
AccelStepper stepper(1, motorStepPin, motorDirPin);
void setup(){
pinMode(buttonPin,INPUT);
stepper.setMaxSpeed(motorSpeed);
stepper.setSpeed(motorSpeed);
stepper.setAcceleration(motorAccel);
stepper.moveTo(-12000); //move 2000 steps (gets close to the top)
}
void loop(){
while( stepper.currentPosition()!=-10000)
stepper.run();
// move to next state
buttonState = digitalRead(buttonPin);
currentPosition=stepper.currentPosition();
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
//if stepper is at desired location
if (buttonState == HIGH ){//need to find a way to alter current move to command
stepper.stop();
stepper.runToPosition();
stepper.moveTo(12000);
}
if(stepper.distanceToGo() == 0)
state=1;
if(state==1){
stepper.stop();
stepper.runToPosition();
stepper.moveTo(12000);
}
//these must be called as often as possible to ensure smooth operation
//any delay will cause jerky motion
stepper.run();
}
I don't know if you've figured it out already, but I stumbled upon this thread and noticed something which might or might not solve your problem. I'm working with accelstepper too at the moment.
I'm having the feeling that even though you use .stop to stop the motor, you're still assigning a new destination (stepper.moveTo(12000)), after which you still run the stepper.run() command at the bottom, causing the stepper to 'run towards 12000 steps'. Maybe try this?
uint8_t button_state = 0; // cf problem 1.
void loop() {
if (digitalRead(buttonPin) == HIGH) {
if (button_state == 0) {
stepper.stop();
stepper.moveTo(12000);
button_state = 1;
}
} else {
button_state = 0;
}
if (stepper.distanceToGo() == 0) {
stepper.stop();
stepper.moveTo(12000);
}
if(button_state = 0) {
stepper.run();
}
}
I don't know if this is going to work, but this way, if the button is pressed, the button_state variable should prevent the stepper from running when it's set on 1.
Hope this helps,
Just a passing by student
I see three problems in your code:
when you push the button, its state will be set to HIGH for the whole time you're pressing it, and it can be several loops. You'd better use a state variable that triggers what you want to do on button press only once.
looking at the documentation, you're using stepper.runToPosition(), which blocks until it reaches the destination. So the more you click, the more it could get blocked. You'd better use only the stepper.moveTo() and stepper.run() method that enables to use a few cycles for interactions.
at the beginning of your loop, you make your code block until stepper.currentPosition gets to -10000. So you surely are blocking until it gets there, removing all reactivity on every loop() iteration.
you may better work your loop() function out as follows:
uint8_t button_state = 0; // cf problem 1.
void loop() {
if (digitalRead(buttonPin) == HIGH) {
if (button_state == 0) {
stepper.stop();
stepper.moveTo(12000);
button_state = 1;
}
} else {
button_state = 0;
}
if (stepper.distanceToGo() == 0) {
stepper.stop();
stepper.moveTo(12000);
}
stepper.run();
}

cycle the LEDS IN ATMEGA8515

I want to read the switches next to the LEDS and cycle the LEDS from 0 to whichever switch is pressed
if none are pressed cycle through all of them with the delay.For this i have used timer0. Since I work on
atmega8515. I used INT0.
Here is my implementation:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BIT(n) (1 << n)
volatile uint8_t led, k, switches, i , j = 1;
/* uint8_t is the same as unsigned char */
int main(void)
{
DDRB = 0xff; /* use all pins on PortB for output */
led = 1; /* init variable representing the LED state */
PORTB = 0XFF;
cli( );
TCCR0|=(1<<CS02) |(1<<CS00);
//Enable Overflow Interrupt Enable
TIMSK|=(1<<TOIE0);
//Initialize Counter
TCNT0=0;
GICR = BIT(INT0);
MCUCR = BIT(ISC01) | BIT(ISC00);
sei( );
for ( ; ;);
}
ISR(TIMER0_OVF_vect)
{
if(switches == 0xff)
{
PORTB = ~led; /* invert the output since a zero means: LED on */
led <<= 1; /* move to next LED */
if (!led) /* overflow: start with Pin B0 again */
{
led = 1;
}
}
else
{
for (k = 0; k< 8;k++)
{
j = switches & (1 << k);
if(j == 0)
{
for(i=1;i<=(k +1);i++)
{
j = 1;
PORTB = ~j;
j = 1 << i;
_delay_ms(100); //without this delay it doesnt cycle the LEDS from to whichever switch is pressed
}
}
}
}
But using delay loops in ISR is a bad programming practice. How to use the same timer instead of the delay?
I think in the ISR, you should just update the LED status and in the main loop you could set the PORTB and read the switches values. In your code, it seems occupy so much time in ISR.

Resources