I'm working on an embedded (avr) project, and basically I want to print out a few different things based on how long a pin has been pressed down. I can't figure out what happens as the value passes through and satisfies if statements along the way (button is still pressed so counter increments).
The setup is as follows:
if overflows is between 7-48 (button pressed for 30ms-200ms), print out a '.'
if overflows is greater than 48 (button pressed for greater than 200ms), print out a '-'
if overflows is greater than 97 (button has not been pressed in over 400ms), print out a ' '
My current code is as follows:
static inline void isr(char type) {
static unsigned int overflows = 0;
static unsigned char idx = 0;
if (type == 'e') { // edge captured
if (TCCR1B & 0x40) { // rising edge
if (overflows < 7) {
// do nothing
} else if (overflows < 49) {
buffer[idx++] = '.';
size++;
} else {
buffer[idx++] = '-';
size++;
}
}
overflows = 0; // restart counting overflows at each edge
} else { // overflow occured
overflows++;
if (buffer[idx-1] != ' ' && !(TCCR1B & 0x40) && overflows > 97) {
buffer[idx++] = ' ';
size++;
}
}
I'm not sure if this is correct though, since it would seem that there would always be a '.' preceding a '-', since as the overflow value is incremented, it satisfies the <49 condition.
Any thoughts?
if you want to count no.of times switch pressed, you can use while loop.
for example,
if(sw==0) //sw is switch connected with I/O pin
{
while(sw==0)
{
led=1; //LED is output
delay(); // use delay function
led=0;
delay();
count++;
}
}
by using while loop, you can avoid from getting multiple times switch pressed. if you make one on and off switch, the count will be increased by one.
I'm not sure if this is correct though, since it would seem that there would always be a '.' preceding a '-', since as the overflow
value is incremented, it satisfies the <49 condition.
But the tests are performed only on the rising edge - when both type == 'e' and (TCCR1B & 0x40) != 0. So the overflow count is evaluated only when the button is released, so you will not see the intermediate . before -.
So to answer your question, a conditional statement or block is executed when the condition is true. Here you have nested conditionals, so all preceding conditions must be true for the inner condition to be evaluated.
According to your description I assume you have a button on your pin, so in this case the first thing what I would recommend before doing anything is to implement a debounce for the button signal.
I would solve the problem something like this:
#define BUTTON_UNKNOWN 0
#define BUTTON_DOWN 1
#define BUTTON_UP 2
#define FALSE 0
#define TRUE 1
static inline unsigned char debounce( unsigned char current_state)
{
static unsigned char ret_value;
unsigned char state_changed = FALSE;
// Counter for number of equal states
static unsigned char count = 0;
// Keeps track of current (de-bounced) state
static unsigned char button_state = 0;
// Check if button is high or low for the moment
if (current_state != button_state)
{
// Button state is about to be changed, increase counter
count++;
if (count >= 3)
{
// The button have not bounced for four checks, change state
button_state = current_state;
// If the button was pressed (not released), tell main so
count = 0;
state_changed = TRUE;
}
}
else
{
state_changed = FALSE;
// Reset counter
count = 0;
}
//if butten press or release detected
if (state_changed == TRUE)
{
//check for the current state of the button
if (current_state != 0)
{
ret_value = BUTTON_DOWN;
}
else
{
ret_value = BUTTON_UP;
}
}
return ret_value;
}
int main(void)
{
//perform proper initialization of your pin
unsigned char button = BUTTON_UNKNOWN;
unsigned char cycle_count = 0;
unsigned char idx = 0;
unsigned char buffer[255];
unsigned char current_state;
unsigned char no_activity;
//unsigned char current_state = (~BUTTON_PIN & BUTTON_MASK) != 0;
while(1)
{
//read the button state
current_state = (~BUTTON_PIN & BUTTON_MASK) != 0;
// Update button_state
button = debounce(current_state);
// Check if the button is pressed.
if (button == BUTTON_DOWN)
{
//count cycles in which the button was pressed
//one cycle is 10 ms - see delay below
cycle_count++;
}
else
{
//check if the button was pressed before
if ((button != BUTTON_UNKNOWN) && (cycle_count != 0))
{
//if button was pressed for 200ms
if (cycle_count <= 20)
{
buffer[idx] = '.';
idx++;
}
else
{
//if the button was pressed between 200 and 400 ms
if ((cycle_count > 20) && (cycle_count <= 40))
{
buffer[idx] = '-';
idx++;
}
//the button was pressed for more than 400ms, uncomment if you need it
/*else
{
buffer[idx] = ' ';
idx++;
}*/
}
//reset counting mechanism
cycle_count = 0;
no_activity = 0;
}
else
{
no_activity++;
if (no_activity >= 40)
{
buffer[idx] = ' ';
idx++;
no_activity = 0;
}
}
}
// Delay for a while so we don’t check to button too often
_delay_ms(10);
}
}
You can adapt this code according to your needs:
change the line current_state = (~BUTTON_PIN & BUTTON_MASK) != 0; so that to read your pin state, and change the declaration of the buffer to suite your needs unsigned char buffer[255];
Please note that due to the debounce the time measured is not exactly 200ms ( it is 200ms + 2*debounce_time = 260ms (debounce time is 3 cycles, each cycle is 10ms, see delay at the end), but you may compensate these errors by reducing the contants in the cycle_count comparisons in the end.
Hope this helps!
If you really stick to your solution, then to avoid the problem what you experience is to not evaluate the overflow value continuously, do not try to determine the length of the button push on the fly, try instead to measure the length of the push and after that evaluate it and put the char in the buffer. You need to wait for the 'release button' and then evaluate how much time the button was pressed. Something like this:
static inline void isr(char type) {
static unsigned int overflows = 0;
static unsigned char idx = 0;
unsiged char button_status;
if (type == 'e') { // edge captured
if (TCCR1B & 0x40) { // rising edge
//perform a debounce otherwise wont be good
button_status = 1;
}
overflows = 0; // restart counting overflows at each edge
} else { // overflow occured
overflows++;
//if button was pressed and its now released evaluate result
if (!(TCCR1B & 0x40) && (button_status == 1))
{
if (overflows < 7) {
// do nothing
} else if (overflows < 49) {
buffer[idx++] = '.';
size++;
} else {
buffer[idx++] = '-';
size++;
}
button_status = 0;
}
if (buffer[idx-1] != ' ' && !(TCCR1B & 0x40) && overflows > 97) {
buffer[idx++] = ' ';
size++;
}
}
Related
I want to interface a keypad in stm32 l053r8 with timer interrupt. I have a SysTick_Handler function which I am handling there the switch debouncing of push buttons and every time the push button counter gets equal to 10 I am using a flag in order to check when the button is pressed. So now I have all the rows of the keypad as input and all the columns of the keypad as output. My idea is
Every timer int, it activates a new column and reads the 4 row inputs
Check if a button flag is "1" and send the appropriate message to the screen.
I have an implementation but it is not working.For example in main function I have a condition that checks if the key1 (expect 1 as output in screen) is pressed but it doesn't do anything. Any advice would be great.
void TIM2_IRQHandler(void) {
if (TIM2->SR & TIM_SR_UIF ) {
//Clear Timer 2 Flag
CLEAR_BIT(TIM2->SR, TIM_SR_UIF);
if (column_counter==4)
column_counter=0;
column_counter++;
keypad_scanning(column_counter); // activate col=1 or 2 or 3 depends on counter.
}
}
void keypad_scanning(uint8_t column_pos) {
switch (column_pos) {
case 1:
SET_BIT(GPIOC->ODR,GPIO_ODR_OD15); // C1 HIGH
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD1); // C2 LOW
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD4); // C3 LOW
if (flag0.fl.f5 && !(flag0.fl.f6) && !(flag0.fl.f7)) {
key0.fl.f1 = 1; // key 1 is pressed.
}
if (!(flag0.fl.f5) && flag0.fl.f6 && !(flag0.fl.f7)) {
key0.fl.f4 = 1; // key 4 is pressed.
}
if (!(flag0.fl.f5) && !(flag0.fl.f6) && flag0.fl.f7) {
key0.fl.f7 = 1; // key 7 is pressed.
}
break;
case 2:
CLEAR_BIT(GPIOC->ODR,GPIO_ODR_OD15); // C1 LOW
SET_BIT(GPIOA->ODR,GPIO_ODR_OD1); // C2 HIGH
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD4); // C3 LOW
if (flag0.fl.f5 && !(flag0.fl.f6) && !(flag0.fl.f7)) {
key0.fl.f2 = 1; // key 2 is pressed.
}
if (!(flag0.fl.f5) && flag0.fl.f6 && !(flag0.fl.f7)) {
key0.fl.f5 = 1; // key 5 is pressed.
}
if (!(flag0.fl.f5) && !(flag0.fl.f6) && flag0.fl.f7) {
key0.fl.f8 = 1; // key 8 is pressed.
}
break;
case 3:
CLEAR_BIT(GPIOC->ODR,GPIO_ODR_OD15); // C1 LOW
CLEAR_BIT(GPIOA->ODR,GPIO_ODR_OD1); // C2 LOW
SET_BIT(GPIOA->ODR,GPIO_ODR_OD4); // C3 HIGH
if (flag0.fl.f5 && !(flag0.fl.f6) && !(flag0.fl.f7)) {
key0.fl.f3 = 1; // key 3 is pressed.
}
if (!(flag0.fl.f5) && flag0.fl.f6 && !(flag0.fl.f7)) {
key0.fl.f6 = 1; // key 6 is pressed.
}
if (!(flag0.fl.f5) && !(flag0.fl.f6) && flag0.fl.f7) {
key0.fl.f9 = 1; // key 9 is pressed.
}
break;
}
}
void SysTick_Handler(void) {
//------------------------------
//key 1 sampling ( ROW 1 FLAG)
if(!flag0.fl.f5) {
if(!KEY1_READ()) {
if(key1_counter == 10)
flag0.fl.f5 = 1; // R1 is HIGH
key1_counter = 0;
} else {
if(key1_counter < 10) //10 ms sampling
key1_counter ++;
}
}
//------------------------------
//------------------------------
//key 2 sampling (ROW 2 FLAG)
if(!flag0.fl.f6) {
if(!KEY2_READ()) {
if(key2_counter == 10)
flag0.fl.f6 = 1; // R2 is HIGH
key2_counter = 0;
} else {
if(key2_counter < 10) //10 ms sampling
key2_counter ++;
}
}
//------------------------------
//------------------------------
//key 3 sampling (ROW 3 FLAG)
if(!flag0.fl.f7) {
if(!KEY3_READ()) {
if(key3_counter == 10)
flag0.fl.f7 = 1; // R3 is HIGH
key3_counter = 0;
} else {
if(key3_counter < 10) //10 ms sampling
key3_counter ++;
}
}
//------------------------------
}
First, your title says 4x4 but the code appears to be for 3x3.
Next, an electrical signal can propagate through a key matrix almost instantaneously. You should do do every column one straight after the other, you don't need a timer to do that. Just do: column high, read row, column low, next column high, etc.
After that, you only read the state of the key if it is not already pressed. You try to debounce the rising edge, but once the key is pressed it seems to stay pressed for ever, you never look for the falling edge when the key is released. Falling edges can bounce too, so you need to have a count there too.
Also, you are checking for the state where only one row is active with if(x && !y && !z). What if the user presses more than one button at a time? For each flag you only need to check for one input.
These points won't fix everything, but hopefully will get you further along the way!
I suspect the following code to cause you some problems.
if(!KEY1_READ()){
if(key1_counter == 10)
What are the chances that you will release the button when counter is exactly 10?
You seem to want an on-release event, so I would write the scanning code like so:
if (KEY1_READ()) {
key1_counter++;
} else {
if (key1_counter > 10) {
flag0.fl.f5 = 1;
}
key1_counter = 0;
}
Don't forget to reset the flag after you're done processing it.
I found the solution for that. First of all thank all of you that gave me an advice and post a question. I used the timer (TIM2) and created an interrupt every 1ms. For each interrupt I was giving in the 3 column, which was my output ports, a sequence of binary numbers, while having all my input ports(rows) grounded. First sequence was 1 0 0, second 0 1 0 and third 0 0 1. After that sequence I was reading the inputs(rows) in order to detect which one was set at high("1"), with that I was able to detect which key was pressed by rasing a flag. All of that is similar to the code I 've posted but I used different register for make the outputs(columns) goes from high(logic "1") to ground(logic "0"), that is the BSRR register. For detection if a button is pressed I use the debounce method.
I am using MPLAB XC8 and I try to use the USART module. But I have abnormal problem. Here is my code, I explain after this.
#include <xc.h>
#include "conbits.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#define _XTAL_FREQ 8000000
void UART_RX_INIT(void);
void UART_TX(uint8_t *s);
void usart_send_str(uint8_t s[]);
uint8_t rx_buffer[];
uint8_t UART_Buffer = 0;
int rx_index=0;
bool rx_reg_ok = false;
uint8_t data;
void main(void) {
OSCCONbits.IRCF = 0X07;
OSCCONbits.SCS = 0X03;
while(OSCCONbits.HFIOFS !=1);
RCONbits.IPEN = 1;
ei();
UART_RX_INIT();
TRISD = 0X00;
LATD = 0X00;
while(1)
{
}
}
void UART_RX_INIT(){
TRISCbits.TRISC7 = 1;
TRISCbits.TRISC6 = 0;
SPBRG = 51;
RCSTAbits.CREN = 1;
RCSTAbits.SPEN = 1;
BAUDCON1bits.BRG16 = 0;
TXSTA1bits.SYNC = 0;
TXSTA1bits.BRGH = 1;
TXSTAbits.TXEN = 1;
IPR1bits.RCIP = 1;
PIE1bits.RCIE = 1;
IPR1bits.TXIP = 0;
PIE1bits.TXIE = 1;
}
void __interrupt(high_priority) ISR(void){
if(PIR1bits.RC1IF==1){
UART_Buffer = RCREG1;
if(UART_Buffer == 36 && rx_index==0)
{
memset(rx_buffer, 0, strlen(rx_buffer));
rx_buffer[0] = UART_Buffer;
rx_index++;
rx_reg_ok=true;
}
else if(rx_reg_ok && UART_Buffer != 36)
{
rx_buffer[rx_index] = UART_Buffer;
usart_send_str(rx_buffer);
usart_send_str("\r\n");
rx_index++;
}
else
{
usart_send_str("\r\nNot Registered\r\n");
}
if(UART_Buffer == 45)
{
usart_send_str("\r\n-----");
usart_send_str(rx_buffer);
usart_send_str("-----\r\n");
}
PIR1bits.RC1IF = 0;
}
}
void __interrupt(low_priority) low_isr(void){
INTCONbits.GIEH = 0;
if(PIR1bits.TXIF){
PIR1bits.TXIF=0;
}
INTCONbits.GIEH = 1;
}
///////////////////////////TRANSMIT
void UART_TX(uint8_t *s){
TXREG = *s;
while(TXSTA1bits.TRMT==0);
}
void usart_send_str(uint8_t s[]){
int i=0;
while(s[i]!='\0')
{
UART_TX(&s[i]);
i++;
}
}
Focus on inside the High Interrupt function block
First Unless I press '$' char send. PIC18 responses are just "Not Registered". After I press '$' char, It will add the next pressed chars to rx_buffer array.
My problem starts right there. Chars I pressed in order '1', '2' they send me 'Not Registered' and I press '$' they start adding to rx_buffer array and show me. Also for checking the buffer. If I send '-' char, It shows me rx_buffer. But If you look at the picture you can see. My problem after I press '$' second char is always 2 decimal higher. I press 'a' but it registered as 'c' to buffer. If I press '1' after '$', It register '3' this problem only happens after '$'. How can I fix this? Here is my simulation output.I press '1' - '2' - '$' - 'a' - 'b' - 'c' - '-'
I press '1' - '2' - '$' - 'a' - 'b' - 'c' - '-'
I press '1' - '2' - '$' - '1' - '2' - '3' - '-'
Is it possible to call a line number in embedded C that is outside of the function?
Simple goto label commands can't work because I am in the if statement loop: I want to restart the function once the error is greater than 200.
void followWall(void)
{
begin:
int desired = getADC(); //store the first ADC reading as the desired distance from the wall
int right = 200; //pre set all variables to be called in the drive function
int left = 200; //ints are needed here for bit masking later on to stop overflow
char rightH = 0;
char leftH = 0;
float k = 0.5; //constant to times the error to fine tune the response
drive(rightH,right,leftH,left); //drive the robot to the pre set variables
while( bumpFlag == 0 ) //while the robot has not been bumped or encounted a cliff
{
int actual = getADC(); //call a new ADC reading everytime at start of while as the current reading
rightH = 0;
leftH = 0; //set variables back to default to stop constant increase and decrease in variables
left = 200;
right = 200;
char error = abs(actual-desired); //calculate the error by minusing the actual distance to the desired
if(motorAngle < 180) //wall on left between 1st and 2nd quadrant
{
if(error > 200)
{
stop();
moveStepper(33,0);
goto begin;
}
if (actual > desired)
{
right -=((error)*k);
left += ((error)*k);
}
else if (actual < desired)
{
left -=((error)*k);
right +=((error)*k);
}
}
else if (motorAngle > 180)
{
if(error > 200)
{
stop();
moveStepper(33,1);
goto begin;
}
if (actual > desired)
{
left -=((error)*k);
right +=((error)*k);
}
else if (actual < desired)
{
right -=((error)*k);
left +=((error)*k);
}
}
drive(rightH,right,leftH,left); bumpSensor();
setLCDCursor(0x09);
writeLCDNumber(convert(actual)); //constantly write the converted AC value on the LCD
}
stop(); //stop the robot
}
You should restructure your code to avoid a GOTO. Another loop is needed. Something like this.
void followWall(void)
{
repeat = 0;
do // add this loop here
{
...
...
while( bumpFlag == 0 )
{
repeat = 0;
..
..
if(motorAngle < 180)
{
..
if(error > 200)
{
..
..
repeat = 1;
break;
}
}
else if (motorAngle > 180)
{
..
if(error > 200)
{
..
..
repeat = 1;
break;
}
}
}
} while (repeat == 1);
}
I am programming C on an Arduino and I am very much a beginner. My goal with this program is to display a binary number on LED's and then let the player guess the binary number. The player then presses a button equal to the decimal value of the displayer binary number. When the players starts pressing the button, they get two seconds for every press. If they don't click again in those two seconds they lose, or they guess the right amount and win.
The only thing my messy program doesn't do is give a two second period every button press. Even after extensive reading I am confused on milli();. Aside from the bad coding, could I change something in my program to give players a two second window when pressing the button? I am convinced that there is a just a minute detail that I am overlooking. Thanks for your time.
const int buttonPin = 2;
const int ledPinValue1 = 3;
const int ledPinValue2 = 4;
const int ledPinValue4 = 5;
const int ledPinValue8 = 6;
int teller = 0;
int buttonStatus = 0;
int lastButtonStatus;
unsigned long interval=5000; // the time we need to wait
unsigned long previousMillis = 0; // millis() returns an unsigned long.
unsigned long currentMillis = 0;
unsigned long elapsed;
unsigned long midElapsed;
unsigned long minus=5000;
int secondLoop = 0;
boolean allowTimer = false;
boolean allowMilliAfterFirstRound; //without this, when the second loop commences milli = milli - milli
void setup() {
Serial.begin(9600);
pinMode(ledPinValue1, OUTPUT);
pinMode(ledPinValue2, OUTPUT);
pinMode(ledPinValue4, OUTPUT);
pinMode(ledPinValue8, OUTPUT);
pinMode(buttonPin, INPUT);
}
void loop() {
boolean initiateLoss = true; // ++++
int randomNumber = 5; //Replace with randomcode
currentMillis = millis();
knopStatus = digitalRead(buttonPin);
if (randomNumber == 1 || randomNumber == 3 || randomNumber == 5 || randomNumber == 7 || randomNumber == 9 || randomNumber == 11 || randomNumber == 13 || randomNumber == 15){
digitalWrite(ledPinValue1, HIGH);
}
else {
digitalWrite(ledPinValue1, LOW);
}
if (randomNumber == 2 || randomNumber == 3 || randomNumber == 6 || randomNumber == 7 || randomNumber == 10 || randomNumber == 11 || randomNumber == 14 || randomNumber == 15){
digitalWrite(ledPinValue2, HIGH);
}
else {
digitalWrite(ledPinValue2, LOW);
}
if (randomNumber == 4 || randomNumber == 5 || randomNumber == 6 || randomNumber == 7 || randomNumber == 12 || randomNumber == 13 || randomNumber == 14 || randomNumber == 15){
digitalWrite(ledPinValue4, HIGH);
}
else {
digitalWrite(ledPinValue4, LOW);
}
if (randomNumber == 8 || randomNumber == 9 || randomNumber == 10 || randomNumber == 11 || randomNumber == 12 || randomNumber == 13 || randomNumber == 14 || randomNumber == 15){
digitalWrite(ledPinValue8, HIGH);
}
else {
digitalWrite(ledPinValue8, LOW);
}
//------------------------------------------------------------------------------------
if (knopStatus != lastButtonStatus) {
if (knopStatus == HIGH) {
teller++;
Serial.print("number of button pushes: ");
Serial.println(teller, DEC);
allowTimer = true;
}
delay(50);
}
//------------------
if (allowTimer == true){
if (allowMilliAfterFirstRound == true) {
currentMillis = currentMillis - currentMillis;
allowMilliAfterFirstRound = false;
}
if (teller == randomNumber){ // WIN
Serial.print("You won!");
Serial.println();
initiateLoss = false;
randomNumber = 3; // remove when randomnumber is added
teller = 0;
previousMillis = millis();
}
if ((unsigned long)(currentMillis - previousMillis) > interval && initiateLoss == true) { // LOST!
elapsed = currentMillis - previousMillis;
Serial.print("Time elapsed at time of failure: ");
Serial.print(elapsed);
Serial.println();
Serial.print("currentMillis is now: ");
Serial.print(currentMillis); // 5000, 10000, 15000, 20000 etc
Serial.println();
Serial.print("previousMillis is now: ");
Serial.print(previousMillis); // 5000, 10000, 15000, 20000 etc
Serial.println();
previousMillis = millis();
Serial.println();
Serial.print("You lost!");
Serial.println();
Serial.print("Begin!");
Serial.println();
teller = 0;
}
}
lastButtonStatus = knopStatus; // save the current state as the last state, for next time through the loop
}
I would do something like this:
void loop() {
if (not yet displayed) {
[all the code to display the number on leds]
}
if (button pressed) {
allowTimer = true; // Start!
}
if (allowTimer) /* If timer started */ {
while (millis() - previousMillis <= 2000) /* The famous 2 sec */ {
if (button pressed) {
previousMillis = millis(); /* Update timing */
teller++;
}
}
if(teller == randomNumber) {
[Hooray, you won or whatever message and stuff you like]
}
else {
[Sorry, you lost]
}
}
Of course you should replace my writings with your code.
TIP: For a single instruction in any statement you don't need parentheses:
if (condition) {
onlyOneInstruction;
}
is the same as
if (condition) onlyOneInstruction;
EDIT: Also, you try to compare knopStatus (undeclared, maybe you meant buttonStatus) with lastButtonStatus, which isn't initialised. This gives you an error.
There's bugs, for instance:
if (allowMilliAfterFirstRound == true) {
currentMillis = currentMillis - currentMillis;
allowMilliAfterFirstRound = false;
}
And allowMilliAfterFirstRound is not initialized, nor assigned elswhere...
More generally, the code shows that you understand millis correctly. What's missing is a management of the 2 states the program can be in:
Display number, then waiting for the player's answer: counting time until timeout
Displaying a win/lost message, just waiting for a button press to start another guess (?)
previousMillis should be initialized when going from state 2 to 1.
I am trying to get a rotary encoder to control the speed of a 7 segment display counting from 0-9 with the Atmel (ATmega328P Xplained mini) microprocessor. My problem is that whenever I run the program the display just counts faster and faster until you can just see an "8", sometimes it seems that I can keep the speed down by turning the rotary encoder CCW and sometimes no effect at all. As I am not that experienced in programming and especially not this stuff I hope someone is capable and willing to help.
Here is my code:
#include <avr/io.h>
void Display (uint8_t x)
{
static uint8_t tabel[] =
{0b11000000,0b11111001,0b10100100,0b10110000,0b10011001,0b10010010,0b10000010,0b11111000,0b10000000,0b10010000};
PORTD = tabel[x];
}
int GetInput (void)
{
uint8_t x = PINC&1;
uint8_t y = (PINC>>1)&1;
if (x == 0 && y == 0) {return 0; }
else if (x == 1 && y == 0) {return 1;}
else if (x == 0 && y == 1) {return 2;}
else {return 3;}
}
int main(void)
{
DDRD = 0xFF; // set PortD as an output
DDRC = 0x00; // set PortC as an input
PORTB = 0x03; // Activate Pull-up resistors
float d = 9000;
int tick = 0;
int i = 0;
int input, state = 0; // initial state
int oldInput = 0;
while (1)
{
input = GetInput();
if (oldInput == 0 && input == 1)
{
d = (d * 1.1);
//slower
}else if (oldInput == 0 && input == 2)
{
d = (d * 0.9);
//faster
}else if (oldInput == 1 && input == 0)
{
d = (d * 0.9);
//faster
}else if (oldInput == 1 && input == 3)
{
d = (d * 1.1);
//slower
}else if (oldInput == 2 && input == 0)
{
d = (d * 1.1);
//slower
}else if (oldInput == 2 && input == 3)
{
d = (d * 0.9);
//faster
}else if (oldInput == 3 && input == 1)
{
d = (d * 0.9);
//faster
}else if (oldInput == 3 && input == 2)
{
d = (d * 1.1);
//slower
}
oldInput = input;
switch (state)
{
case 0: //ini
Display(0);
state = 1;
break;
case 1: //count
if (i == 9)
{
i = 0;
Display(i);
}
else
{
i++;
Display(i);
}
state = 2;
break;
case 2: // delay
if (tick < d)
{
state = 2;
tick++;
}
else
{
state = 1;
tick = 0;
}
break;
case 3: //reset / destroy
break;
}
}
}
First try changing the GetInput function to return a more useful value. Note that bit 0 and bit 1 of PINC already combine to form the integer that you're reconstructing.
int GetInput (void)
{
// array to convert grey scale bit patterns to direction indicators.
// Rows indexed by lastValue, columns indexed by thisValue, and the
// content is -1 for CCW, +1 for CW, 0 for no motion. Note that 0 is
// also used for an invalid transition (2 bits changed at once), but a
// different value could be used for fault detection.
static const int tableGreyToDirection[4][4] =
{
0 , -1, 1 , 0 , // lastValue==0
1 , 0 , 0 , -1, // lastValue==1
-1, 0 , 0 , 1 , // lastValue==2
0 , 1 , -1, 0 // lastValue==3
};
static uint8_t lastValue = 0; // A valid default starting value
uint8_t thisValue = (PINC & 0b00000011); // Use the bottom two bits as a value from 0..3
int result = tableGreyToDirection[lastValue][thisValue];
lastValue = thisValue;
return result;
}
You can then simplify the test in the loop greatly.
while (1)
{
// Check the direction of the encoder: -1 = CCW, +1 = CW, anything else = no motion.
input = GetInput();
if(0 < input)
{
// Motion is CW, so increment the delay (within reasonable bounds).
if(8900 > d) d += 100;
}
else if(0 > input)
{
// Motion is CCW, so decrement the delay (within reasonable bounds).
if(100 < d) d -= 100;
}
// Keep the rest as it is...
}
It would be advisable to change d to be a uint16_t and tidy it up a little. Further tips include using #define to provide readable names for constants. E.g. in my table of directions you could use:
#define ENCODER_CW 1
#define ENCODER_CCW -1
#define ENCODER_NEITHER 0
...
static const int tableGreyToDirection[4][4] =
{
ENCODER_NEITHER, ENCODER_CCW, ENCODER_CW, ENCODER_NEITHER, // lastValue==0
...
I'm sure you can fill it out yourself.
I checked your SW, but I can't find big issue instantly.
You'd better check below part.
If you didn't touch the encoder but speed is faster and faster
: do you have a scope to check the encoder input port whether noise is input from the port or not.
If two input port is stable, please check your value also stable
: old input and new input value should be same
: check by log or output toggle unused port when the value is changed. you can debug your own code.
You'd better add amount tick value than multiply directly to prevent d value becomes 0.
your CPU has to run as fast as detect port status change in main loop. - I think it is possible if this code is all of your system.