PIC16F1937 and UART : keep led ON while receiving - c

I'm trying to keep a led ON while PIC is receiving a character (while a button is pressed), the led being OFF by default, but I can't seem to find a solution.
I'm 3 months in learning using microcontrollers (specifically PIC) coding with MPLABX IDE and started understanding UART communication. At first I tried turning a led ON/OFF when a charater is received, it went pretty good and worked, but right now I'm trying to keep a led active while a button is pressed but can't really get over this problem. I have my code in a interrupt function like this :
//#define LED RB5
void __interrupt () ISR (void)
{
if(RCIF) // data is received
{
data = RCREG; // get the value
if (data == '1') // if value received is '1'
LED = 1; //turn ON led
RCIF = 0;
}
LED = 0; // turn OFF led
}
Using the above code makes so the led turns on/off really fast while I keep pressing a button and isn't really what I wanted.I hope someone can help me understand what I need to do. Thanks !

The LED goes off quickly because you switch if off immediately after switching it on:
void __interrupt () ISR (void)
{
if(RCIF) // data is received
{
data = RCREG; // get the value
if (data == '1') // if value received is '1'
LED = 1; //turn ON led
RCIF = 0;
}
LED = 0; // turn OFF led <<=== This is executed unconditionally.
}
You might put LED = 0; in an else branch if you don't want it to be executed everytime.
Maybe like this:
void __interrupt () ISR (void)
{
if(RCIF) // data is received
{
data = RCREG; // get the value
if (data == '1') // if value received is '1'
LED = 1; //turn ON led
else
LED = 0; // turn OFF led
RCIF = 0;
}
}
Depending on your logic, the else might be placed after the end of the outer if block.

The LED goes off quickly after receiving any character.
RCIF flag will only set if UART data is received. So to turn off the LED you should start a timer of certain millisec and unless your reception is completed keep on restarting the timer.
void __interrupt () ISR (void)
{
if(RCIF) // data is received
{
data = RCREG; // get the value
if (data == '1') // if value received is '1'
LED = 1; //turn ON led
RCIF = 0;
start_timer_interrupt(x_ms); //set x_ms optimum timing so as to LED ON is visable with UART Rx
flag_rx_timer=1;
}
if(TIMER_OVERFLOW) //TIMER_OVERFLOW is an example keyword used here. Pleas add exact timer overflow flag
{
if(flag_rx_timer) //check if timer overflow is because of timer started during UART Rx
{
LED = 0;
flag_rx_timer=0;
}
}
}```

I suggest you create a state machine for this in app.c.
Define some states with an enum and a struct to hold the state and place them in app.h, include app.h in files where you wish to use this module (e.g. your system_interrupt.c file).
For example you could do this:
typedef enum{
IDLE,
START_RECEIVING,
STILL_RECEIVING,
}uart_state_t;
typedef struct{
uart_state_t current_state;
}uart_module_t
volatile uart_module_t uart_module = {0}; // initial state = IDLE, needs to be volatile because it will be updated via interrupt
Then create a function to service your state machine. This will handle the state that it is currently in and transition to other states as desired. For instance, the state machine starts in the IDLE state but once your interrupt has fired and RCIF bit is set then you'll transition in to the START_RECEIVING state which will turn the LED on then transition to the STILL_RECEIVING state where it will poll the RCIF bit until cleared. That would look something like this:
void uartFSM(void){
switch(uart_module.current_state){
case IDLE:
{
break;
}
case START_RECEIVING:
{
LED = 1; // Turn LED on
uart_module.current_state = STILL_RECEIVING; // state update
break;
}
case STILL_RECEIVING:
{
if(!RCIF){
// done receiving
LED = 0; // Turn LED off
uart_module.current_state = IDLE; // state update
}
break;
}
default:
{
// whoops
break;
}
}
}
Now your interrupt will look like this:
void __interrupt () ISR (void)
{
if(RCIF) // data is received
{
data = RCREG; // get the value
// if value received is '1'
if (data == '1') uart_module.current_state = START_RECEIVING; // state update
}
}
Now you just have to make sure that you call the uartFSM() some where in APP_Tasks so that the state machine gets serviced.

Related

Problem reading rotary encoder with atmega8

I am trying to read a rotary encoder as part of a larger project. I am using a Atmega8L and MPLab X with the XC8 compiler.
The issue I am having, is that the encoder does not work reliable and only seems to be able to count up and not down.
Due to some other aspects of the circuit the encoder pins could not be connected to interrupt-capable pins of the atmega8.
Here is my code, that reads the encoder.
#include <xc.h>
#include "pinmapping.h"
#include "main.h"
static inputEvent lastEvent;
static int aLastState;
static int aState;
//get the last Events data for use elsewhere
inputEvent getLastInputEvent(){
return lastEvent;
}
void initEncoder(){
//get our current button state and set rotation change to 0
if((PINB & ENCODER_SW) == 0){
//also save that to our Event
lastEvent.buttonPressed = 1;
}
else{
lastEvent.buttonPressed = 0;
}
lastEvent.rotationChange = 0;
//also save the current state of the A pin for the first read
aLastState = (PINB & ENCODER_A);
if(aLastState >= 1){
aLastState = 1;
}
}
//this should run regularly eg. on a timer interrupt
void readEncoder(){
//first get the state of both pins
aState = (PINB & ENCODER_A);
int bState = (PINB & ENCODER_B);
if(aState >= 1){
aState = 1;
}
if(bState >= 1){
bState = 1;
}
//check if the button is pressed
if((PINB & ENCODER_SW) == 0){
//save that to our Event
lastEvent.buttonPressed = 1;
}
else{
lastEvent.buttonPressed = 0;
}
//check if the state has changed since last time
if(aState != aLastState){
//check if A and B have the same state -> positive rotation
if(bState != aState){
//save the rotation change to our Event
lastEvent.rotationChange = 1;
}
else{
lastEvent.rotationChange = -1;
}
//save the current state of the A pin for next time
aLastState = aState;
}
else{
//if no rotation change happend, save that to our Event
lastEvent.rotationChange = 0;
}
}
The button press is working properly, but the rotation change is never -1 and always 1 or 0.
The ENCODER_SW, ENCODER_A and ENCODER_B are bitmasks, that are all 0s and a single 1 at the bit corresponding to the correct IO-pin.
In the main, first the initEncoder is called (after IO-setup) and then in the infinite loop a function is called, which in turn calls readEncoder() and then getLastInputEvent() and then does some more stuff before returning to the main.
I have also tried running the readEncoder() function on a timer interrupt roughly every ms. This however results in it not working at all.
What is the problem? Why isn't it working?
Edit: Updated the code after I made some changes.
I am using a ALPS EC12E with pushbutton.
The rotationChange is used to increment/decrement a number that is printed to an LCD.
Edit 2: I now think, it is a timing problem, since I tried running basically the same code, just without anything else on an Arduino and printed the values to the serial console and it worked basically perfectly.
I checked what else is done and realized that in my current code the readEncoder() function only runs about once every 98 or so ms.
When I run the function on a timer interrupt every 1 or 2 ms, it just doesn't work at all. I don't even get a reaction to the button push anymore, which is something I do not understand at all.
In fact the timer does not seem to run at all, or at least the interrupt service routine is never executed. (I tried turning on an LED in the setup and turning it off in the isr, but it stays on indefinitely.)
So I guess theres a new problem now...
Here is the timer initialization:
ASSR = 0x00; //make sure we are not in asynchronous mode
OCR2 = 3; //Output compare register
TCNT2 = 0; //Reset counter to 0
TCCR2 = 0b00001111; //Timer 2 CTC mode, clkio/1024
This is done inside of initDecoder(). The OCIE2 bit is already set earlier in the main setup, enabling the timer 2 interrupt.
The isr than just looks like this:
void __interrupt (TIMER2_COMP_vect_num) timer2_isr(){
//Just read the encoder
readEncoder();
}
The interrupt flag gets cleared by hardware when the isr is executed.
The TIMER2_COMP_vect_num is definitely correct as well, as MPLab recognizes it and I can even look at its definition.

RBIE interrupt works only once - PIC16F877A

In my code, I have two interruptions, one is coming from the overflow of the TMR0, and the other one is when a button is pressed.
this is the code in MikroC :
int compt = 0;
int seconds = 10 ;
int enable = 0;
void interrupt(){
if (INTCON.INTF) {
PORTD = 9;
enable = 1;
seconds = 10;
INTCON.INTF = 0;
}
if (INTCON.TMR0IF) {
compt++;
INTCON.TMR0IF = 0;
TMR0 = 0x06;
}
}
void main() {
TRISB = 0x01;
PORTB = 0;
PORTD = 0;
TRISD = 0x00;
INTCON = 0xB0;
OPTION_REG = 0x44;
TMR0 = 0x06;
while(1){
if (compt == 625){
if (enable) seconds--;
compt = 0;
}
if (seconds > 0 && enable == 1) {
PORTD = seconds;
PORTB.RB1 = 1;
} else {
enable = 0;
PORTB.RB1 = 0;
PORTD = 0;
}
}
}
what I am trying to achieve with my code is as shown in the following picture :
When I press one of the push buttons, the countdown starts and the LED illuminates until the countdown ends, and if the user pressed the button while the countdown still didn't hit 0, it starts over, until the countdown hits 0 again, then the LED should turn off.
What I'm facing here, is that the interruption from RBIE works only once, the second time I press the button, nothing happens.
I am not sure if the TMR0F has something to do with that or not, tried many things, but couldn't make it to work.
I Hope that you could see something i didn't notice, and help me.
The code as posted compiles without warnings or errors with MikroC.
The code runs using the simulator in MLPAB v8.92 and when using the simulator stimulus to assert the INT0 interrupt it is handled correctly each time.
Your circuit diagram looks like it was created using Proteus, perhaps there are issues with how that simulator works.
The only suspicious setting I can find is that the weak pull-ups are enabled for PORTB yet your circuit diagram has a 10K ohm pull-down in the INT0(RB0) pin.
I would suggest setting bit 8 of the OPTION_REG to one to turn off the PORTB pull-ups.
Sorry my answer is not more definite but I cannot reproduce your problem from the information posted.
Seem like this question was asked on StackExchange too.
You have enabled internal weak pull up resistor and also connected pull down resistor on pin RB0, external resistor is not needed, also you need to provide some amount of delay (about 300ms) after the button is pressed.

how to interrupt arduino when data recieved via rx pin

what am I doing wrong ? I have the RX of the Bluetooth HC-07 wired into pin2 looking for a change in voltage when data is received to start a interrupt.
I'm trying to cause an interrupt when data is received via the Bluetooth.
#include <SoftwareSerial.h>// import the serial library
#include <PinChangeInt.h>
SoftwareSerial Genotronex(10, 11); // RX, TX
int ledpin=13; // led on D13 will show blink on / off
int BluetoothData; // the data given from Computer
void doSomething(void);
void setup() {
// wil run once
Genotronex.begin(9600);// data rate for comunicating
Genotronex.println("Bluetooth On please press 1 or 0 blink LED ..");
pinMode(ledpin,OUTPUT);
interrupts();
pinMode(2,INPUT);
attachInterrupt(0,doSomething, CHANGE);
}
void loop() {
// Main body of code
}
void doSomething(){
Genotronex.println("INTERRUPT!!!!!!!!!!!!");
digitalWrite(ledpin,1);
delay(1000);
digitalWrite(ledpin,0);
delay(1000);
detachInterrupt(0);
}
////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
This is a repost of the code ,, I've taken out what the comments said and added what you recommended,, for the most part. The code runs but change state never changes back it only changes once? I've tried taking out DetachInterrupt but the actual interrupt stops working then ? Any more ideas? Thanks for the help btw
#include <SoftwareSerial.h>// import the serial library
SoftwareSerial Genotronex(10, 11); // RX, TX
int ledpin=13; // led on D13 will show blink on / off
int BluetoothData; // the data given from Computer
void doSomething(void);
volatile int state = LOW;
void setup() {
// wil run once
Genotronex.begin(9600);// data rate for comunicating
Genotronex.println("Bluetooth On please press 1 or 0 blink LED ..");
pinMode(ledpin,OUTPUT);
pinMode(2,INPUT);
attachInterrupt(0,doSomething, CHANGE);
}
void loop() {
// Main body of code
digitalWrite(ledpin, state);
}
void doSomething(){
Genotronex.println("INTERRUPT!!!!!!!!!!!!");
state = !state;
detachInterrupt(0);
}
delete:
detachInterrupt(0);
If you can change interrupt from CHANGE to HIGH/LOW and add something like 'debouncing'. My version here (HIGH state interrupts):
void interruptfunction() {
//your interrupt code here
while(1){
if (digitalRead(2)==LOW){
delay(50);
if (digitalRead(2)==LOW) break;
}
}
}
Or this: (not tested)
void interruptfunction() {
//your interrupt code here
if (digitalRead(2)==HIGH) state=true;
else state=false;
while(1){
if (digitalRead(2)!=state){
delay(50);
if (digitalRead(2)!=state) break;
}
}
}
If you have to react on CHANGE try leaving detachInterrupt(0); but reattach it somewhere in your main loop.

numTicks variable not incrementing

I have the following sketch, and the numTicks variable is not incrementing, the sketch compiles fine to the Arduino, but for whatever reason the variable "numTicks" is not incrementing.
/*
* kegboard-clone-4-KegCop
* This code is public domain
*
* This sketch sends a receives a multibyte String from the iPhone
* and performs functions on it.
*
* This Arduino sketch is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Public License
* along with this sketch. If not, see <http://www.gnu.org/licenses/>.
*
* Examples:
* http://arduino.cc/en/Tutorial/SerialEvent
* http://arduino.cc/en/Serial/read
* http://stackoverflow.com/questions/16532586/arduino-sketch-that-responds-to-certain-commands-how-is-it-done/
* http://davebmiller.wordpress.com/2011/01/18/arduino-flowmeter/
* http://forum.arduino.cc/index.php?topic=52003.0
* http://arduino.cc/en/Reference/AttachInterrupt
* https://github.com/just-kile/Zapfmaster2000/blob/master/src/zapfmaster2000-zapfkit-avr/draftkitAVR.ino
*
* TODO:
* - eventually get code working with the SF800 flow sensor / flowmeter
*
*/
// flow_A LED
int led = 4;
// relay_A
const int RELAY_A = A0;
// string / serial event variables
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
boolean valve_open = false;
// FLOWMETER SHIT
// flowmeter 0 pulse (input) = digital pin 2
// https://github.com/Kegbot/kegboard/blob/master/arduino/kegboard/kegboard_config.h
// which pin to use for reading the sensor? kegboard-mini shield has digital pin 2 allocated
// the SF800 outputs 5400 pulses per litre
// The hall-effect flow sensor (SF800) outputs approximately 5400 pulses per second per litre/minute of flow
// SF800 default (5400 ticks/Liter == 5.4 ticks/mL == 1/5.4 mL/tick)
int flowmeterInterrupt = 0; // changed from byte
int flowmeterPin = 2; // changed from byte
int flowmeterPinState = 0; // variable for storing state of sensor pin
// read RPM
int rpmcount = 0;
int rpm = 0;
unsigned long lastmillis = 0;
// NEW GLOBALS - 29JUL13
// initial ticks on flow meter
volatile unsigned int numTicks = 0;
// interval for flow meter frequency
int interval = 250;
volatile long previousMillis = 0;
void setup() {
// initialize serial
// Serial.flush(); // flush the serial buffer on setup.
Serial.begin(115200); // open serial port, sets data rate to 9600bps
Serial.println("Power on test");
inputString.reserve(200);
valve_open = false;
// relay for solenoid cut off valve
pinMode(RELAY_A, OUTPUT);
// flowmeter shit
pinMode(flowmeterPin, INPUT);
digitalWrite(flowmeterPin, HIGH); // Need to set these HIGH so they won't just tick away
// The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
// Configured to trigger on a RISING state change (transition from HIGH
// state to LOW state)
attachInterrupt(flowmeterInterrupt, count, RISING);
}
void open_valve() {
digitalWrite(RELAY_A, HIGH); // turn RELAY_A on
valve_open = true;
}
void close_valve() {
digitalWrite(RELAY_A, LOW); // turn RELAY_A off
valve_open = false;
}
void flow_A_blink() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for one second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
void flow_A_blink_stop() {
digitalWrite(led, LOW);
}
void flow_A_on() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
}
void flow_A_off() {
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
}
// flowmeter shit
void getFlow4() {
// Serial.println("im here");
// Serial.println(sensorPin);
flowmeterPinState = digitalRead(flowmeterPin);
// Serial.println(sensorPinState);
volatile unsigned long currentMillis = millis();
// if the predefined interval has passed
if(currentMillis - previousMillis > interval) { // Uptade every 1/4 second, this will be equal to reading frecuency (Hz).
// disconnect flow meter from interrupt
detachInterrupt(flowmeterInterrupt); // Disable interrupt when calculating
// check, whether any flow was detected
if (numTicks >= 0) {
// start message to computer with tick message symbol
Serial.print("Ticks:");
// send amount of ticks for last interval
Serial.print(numTicks);
}
// clean buffer
Serial.flush();
// reset amount of ticks
numTicks = 0;
// set new start value for interval counter
previousMillis = currentMillis;
// reattach interrupt
attachInterrupt(flowmeterInterrupt, count, RISING);
}
if(flowmeterPinState == LOW) {
flow_A_off();
// Serial.println("don't blink");
}
if(flowmeterPinState == HIGH) {
flow_A_on();
// Serial.println("blink damnit");
}
if(stringComplete) {
if(inputString.equals("{close_valve}\n")) {
// Serial.println("close vavle.");
close_valve();
}
return;
}
}
// flow meter interrupt function
void count(){
numTicks++;
}
/*
* Main program loop, runs over and over repeatedly
*/
void loop() {
if(stringComplete) {
// Serial.println(inputString);
if(inputString.equals("{open_valve}\n")) {
// Serial.println("inputString equates :)");
open_valve();
}
if(inputString.equals("{close_valve}\n")) {
// Serial.println("close vavle.");
close_valve();
}
if(valve_open) {
// Serial.println("valve_open = true");
inputString = "";
stringComplete = false;
while(numTicks <= 1000) {
getFlow4();
}
}
// clear the string:
inputString = "";
stringComplete = false;
}
//Serial.println("over and over");
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent() {
while(Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
}
// Serial.println(inputString.length());
}
}
The reason the numTicks variable is not changed is probably due to the interrupt not triggering. You should put a breakpoint in count() to confirm this. Then you need to figure out why the interrupt isn't triggering as it should, but that is another question.
Sorry for my prior answer. I did not completely understand the problem.
First of all, are you using an Arduino UNO, if so then check the board pin labelled digital pin 2 which is mapped as "int.0" and ensure that the interrupt line from the flowmeter is connected to this pin. (see: http://arduino.cc/en/Reference/AttachInterrupt).
Per Chris' comments above, the count() routine is interrupt driven code, and it appears to be coded correctly: numTicks is defined as volatile; and count() does NOT issue I/O commands such as printf; and it does NOT return any values.
The code sample that you provide does not isolate or highlight the problem. I would write a test sketch that is just a bare bones implementation of "opening" the sensor and then sensing an interrupt from the flow meter and reporting that back to the console from the main loop. If you can get code that detects one interrupt from the flow meter to work, then add more code to report on the number of interrupts in one second, then 1/2 second, etc.
Finally, in your provided code you have the fragment:
if(valve_open) {
// Serial.println("valve_open = true");
inputString = "";
stringComplete = false;
while(numTicks <= 1000) {
getFlow4();
}
}
Since numTicks is incremented by the interrupt routine count, as a matter of principal I would NOT test it unless some of kind of serialization was implemented. getFlow4() detaches the interrupt which is one way to serialize access to numTicks. Note. in theory code can update numTicks without serialization but any value returned is not necessarily accurate as the interrupt might have fired and increased numTicks.
It seems like your application is interested in knowing the number of ticks per second?? in which case you do NOT need to test numTicks prior to stopping the interrupts. All you might need is code that once every second checks numTicks and if you can live with dropping a count then zero out numTicks without even detaching the interrupt. However, sampling numTicks is more like polling which is what an interrupt is trying to avoid.
So, since the code is keeping track of intervals then divide the interval by numTicks to get a Hz value and don't zero out numTicks or reset the interval until they get close to rolling over.
The code ended up working, it was hardware issue with the connections from the flowmeter (>.>)
You are never actually calling your count() method. You should either insert numTicks++; into the code where you wish to increase the count (recommended way), or call your count() method where you wish to increase the count. Here the count() method is only defined and not called, but it would make more sense to just increment the counter in the code since that is the only thing your defined method is doing.

How can I create interrupts in C for Arduino?

So I did this code for Arduino in C. It is for controlling a stepper motor. But every time I have to wait until the microcontroller begins a new loop so that it can take the value for the new variables, how can I create an interrupt so it will do it in any time of the program?
#include <avr/io.h>
#define F_CPU 4000000UL
#include <util/delay.h>
#include "IO/ioconfig.h"
#include "leebotones/leebotonesA.h"
#include "leebotones/leebotonesB.h"
#include "rutina/avanza.h"
#include "rutina/retrocede.h"
char variableA = 0;
char variableB = 0;
int main(void){
ioconfig();
while(1) {
if (leebotonesA()==1) {
variableA++;
} //Fin de if leebotonesA.
if (leebotonesB()==1) {
if (variableA==0) {
variableB=1;
}
else {
variableB=0;
}
}
if (variableA==2) {
variableA=0;
PORTD=0x00;
_delay_ms(10000);
} //Fin de if variableA.
if (variableA==1 && variableB==0) {
avanza();
} //Fin de if leebotonesA.
if (variableA==1 && variableB==1) {
retrocede();
}
_delay_ms(25);
}//End of while
}// End of main
A hardware interrupt on the Arduino occurs when one of the interrupt pins receives a change of state. The function to use, if you have access to the Arduino library, is attachInterrupt.
Example code to listen for an interrupt (derived from the documentation I linked to, I added comments to help explain):
// The Arduino has an LED configured at pin 13
int pin = 13;
// Holds the current state of the LED to handle toggling
volatile int state = LOW;
void setup()
{
pinMode(pin, OUTPUT);
// First Parameter:
// 0 references the interrupt number. On the Duemilanove, interrupt 0
// corresponds to digital pin 2 and interrupt 1 corresponds to digital pin
// 3. There are only two interrupt pins for the Duemilanove and I believe
// the Uno too.
// Second Parameter:
// blink is the name of the function to call when an interrupt is detected
// Third Parameter:
// CHANGE is the event that occurs on that pin. CHANGE implies the pin
// changed values. There is also LOW, RISING, and FALLING.
attachInterrupt(0, blink, CHANGE);
}
void loop()
{
// Turns the LED on or off depending on the state
digitalWrite(pin, state);
}
void blink()
{
// Toggles the state
state = !state;
}
There is also a concept of Pin Change Interrupts that is supported on every pin. See the bottom part of introduction to interrupts for more info.
However, sometimes a hardware interrupt can be avoided by refactoring your code. For example, keep your loop() running quickly --- mostly just reading inputs, limit the use of delay() --- and in the loop, call a function when the targeted inputted value is detected.
MSTimer2 is an Arduino library function to let you set timer interrupts.
Another alternative to delay, rather than interrupts, is to set a timer and check it each time through the loop. http://arduino.cc/en/Tutorial/BlinkWithoutDelay explains the concepts. The Metro library http://arduino.cc/playground/Code/Metro implements this and is easy to use. I've used it instead of delay() so that I can check for a button push while my robot is moving.

Resources