Malfunctioning concurrent tick functions in my state machine - c

The purpose of the code I'm writing is that when my 0th bit of PINA is 1, the number displayed on PORTB is incremented. Likewise, if the 1st bit of PINA is 1, the number is decremented. If the button is held down, it will increment/decrement at a rate of once per second. If the button has been held down for at least 3 seconds, the rate is increased to one every 400 ms. If both buttons are pressed, the number displayed on PORTB is reset to 0. The timer period is set at 100 ms.
I split the state machine into 2 state machines running "concurrently". One SM adjusts the period that I'm incrementing/decrementing at, and the other does the transitions between waiting/adding/subtracting/resetting states.
The problem I'm having is that my state machine that modifies the period is not doing anything at all. I have a global variable to keep track of how many ticks (initialized to 10) I have to wait before incrementing/decrementing, and that this machine should modifying it when the number of ticks stayed in my Incr/Decr states exceeded 30 ticks (3 seconds). But this machine is not doing anything at all. I modified so that it will immediately and permanently change to 4 ticks when my concurrent SM starts, but that still doesn't do anything; incrementing/decrementing is still happening every 1 second rather than every 400 ms.
What's wrong with my code?
Code in question:
enum States {Init, ButPres, Incr, Wait, Decr, Reset} state;
enum other_States {Init1, change_tick_total} state1;
unsigned char button = 0x00;
unsigned char cnt = 0x00;
unsigned char tick_cnt = 0;
unsigned char tick_total = 0x0A;
void change_period_tick() {
switch (state1) {
case Init1:
state = change_tick_total;
break;
case change_tick_total:
state = change_tick_total;
break;
default:
state = Init1;
break;
}
switch (state1) {
case change_tick_total:
if (tick_cnt > 30)
tick_total = 0x04;
else
tick_total = 0x0A;
break;
default:
break;
}
}
void tick(){
switch(state){
case Init:
state = ButPres;
break;
case ButPres:
if(button == 0x01)
state = Incr;
else if(button == 0x02)
state = Decr;
else if(button == 0x03)
state = Reset;
else
state = ButPres;
break;
case Incr:
if(button == 0x01 && cnt < 9)
state = Incr;
else
state = Wait;
break;
case Decr:
if(button == 0x02 && cnt > 0){
state = Decr;
}
else
state = Wait;
break;
case Wait:
if(button == 0x03)
state = Reset;
else if(button)
state = Wait;
else
state = ButPres;
break;
case Reset:
if(button == 0x03)
state = Reset;
else
state = Init;
break;
default:
state = Init;
break;
}
switch(state){
case Init:
cnt = 0x00;
PORTB = cnt;
break;
case Incr:
if(cnt < 0x09){
++tick_cnt;
if (tick_cnt % tick_total == 1)
++cnt;
PORTB = cnt;
}
break;
case Decr:
if(cnt > 0x00){
++tick_cnt;
if (tick_cnt % tick_total == 1)
--cnt;
PORTB = cnt;
}
break;
case Reset:
cnt = 0;
PORTB = cnt;
default:
tick_cnt = 0; //once we're out of INCR/DECR, the tick_cnt resets, as we're on a new cycle of counting ticks
break;
}
}
int main(void)
{
state = Init;
state1 = Init1;
DDRA = 0x00; PORTA = 0xFF;
DDRB = 0xFF; PORTB = 0x00;
// Initializes the LCD display
TimerSet(100);
TimerOn();
tick_total = 0x0A;
while(1) {
button = ~PINA;
change_period_tick(); //this does no seem to be running at all
tick();
while(!TimerFlag){}
TimerFlag = 0;
}
}

Related

Debounce button in PIC

I newbie in PIC mcu. I use pic12f675 MPLAB and XC8 for make an LED multiple blink pattern.
and I have problem with push button (after review it call Bounce and Debounce).
Sometime when I press button it will in sequence ex. 1->2->3->4->5 but sometime it will jump ex. 1->3->4->6 etc.
Please advice me How to debounce in pic mcu or another way to solve my problem.
Thank you. everyone.
(PS.I connect push button with 10K resistor)
my code at below
#include <xc.h>
#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
#define _XTAL_FREQ 4000000
int cnt = 0;
int k = 0;
void __interrupt() MyISR(void){
if(INTCONbits.INTF) //If External Edge INT Interrupt
{
cnt++;
INTCONbits.GIE = 0;
INTCONbits.INTF = 0; // Clear the interrupt
INTCONbits.GPIF = 0;
if( cnt > 6 ){
cnt = 1;
}
}
INTCONbits.GIE = 1;
}
void main(void) {
ANSEL = 0;
CMCON = 0b00000111; //turns comparators off
TRISIO = 0b00000100;
GPIO = 0;
TRISIO2 = 1; // Make GP2 pin as input
INTCONbits.GIE = 1;
INTCONbits.INTE = 1;
INTCONbits.GPIF = 1;
INTCONbits.INTF = 0;
OPTION_REG = 0b01000000;
while(1){
if( cnt == 1 ){
GP0 = 1;
GP5 = 1;
}else if( cnt == 2 ){
for(k=0;k<30;k++){
GP5 = 1;
GP0 = 1;
}
k=0;
while(k<3){
GP5 = ~GP5;
__delay_ms(70);
GP0 = ~GP0;
__delay_ms(70);
k++;
}
}else if( cnt == 3 ){
for(k=0;k<5;k++){
GP5 = 1;
GP0 = 1;
__delay_ms(70);
GP5 = 0;
GP0 = 0;
__delay_ms(70);
}
GP5 = 0;
GP0 = 0;
__delay_ms(1200);
}else if( cnt == 4 ){
for(k=0;k<3;k++){
GP0 = 1;
__delay_ms(50);
GP0 = 0;
__delay_ms(50);
}
for(k=0;k<3;k++){
GP5 = 1;
__delay_ms(50);
GP5 = 0;
__delay_ms(50);
}
}else if( cnt == 5 ){
GP0 = 1;
GP5 = 1;
for(k=0;k<3;k++){
GP5 = 1;
__delay_ms(50);
GP5 = 0;
__delay_ms(50);
}
GP0 = 1;
GP5 = 1;
for(k=0;k<3;k++){
GP0 = 1;
__delay_ms(50);
GP0 = 0;
__delay_ms(50);
}
}else if( cnt == 6 ){
GP0 = 1;
GP5 = 1;
__delay_ms(20);
GP0 = 0;
GP5 = 0;
__delay_ms(3000);
}
}
return;
}
I rewrite your code and tested it in MPLAB simulation. It works as expected. It changes modes in ascending order, then runs in the selected mode until the change button pressed again, then it changes to the next mode. You can add more working modes if you want or you can modify the way the how GPIOs blinking. There is no __delay_ms(), that's why the delays run without consuming the CPU. Please test it in a real circuit and give me a feedback.
/*
* File: main.c
* Author: kozmotronik
*
*/
#define _XTAL_FREQ 4000000
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>
#pragma config FOSC=INTRCIO,WDTE=OFF,MCLRE=OFF,BOREN=OFF
// Work mode definitions
#define MODE_IDLE 0
#define MODE_OFF 1
#define MODE_ON 2
#define MODE_SLOW 3
#define MODE_FAST 4
#define MODE_CANCEL 5
#define LAST_MODE MODE_FAST
// Button states
#define BUTTON_IDLE 0
#define BUTTON_PRESS_DETECTED 1
#define BUTTON_DEBOUNCING 2
#define BUTTON_PRESS_CONFIRMED 3
#define SYSTEM_CLOCK_MS 1
#define SYSTEM_CLOCK_uS (SYSTEM_CLOCK_MS * 1000)
#define _XTAL_FREQ_MHZ (_XTAL_FREQ / 1000000) // Oscillator freq in MHz
#define TMR0_RELOAD_VALUE 256 - ( (SYSTEM_CLOCK_uS * _XTAL_FREQ_MHZ) / (8 * 4) ) // Result must be 131
#define MS_TO_TICKS(msTime) (msTime / SYSTEM_CLOCK_MS)
typedef struct{
unsigned int start;
unsigned int ticks;
} time_t;
char mode = MODE_IDLE;
char lastMode = MODE_OFF;
char buttonState = BUTTON_IDLE;
char k = 0;
unsigned int systemTick = 0; // Time value count by Timer0
void __interrupt() MyISR(void){
if(INTCONbits.INTF) //If External Edge INT Interrupt
{
INTCONbits.INTF = 0; // Clear the interrupt
buttonState = BUTTON_PRESS_DETECTED; // Signal the detected press
}
// Check for 1 ms periodic interrupt for system clock
else if(INTCONbits.T0IF){
INTCONbits.T0IF = 0; // clear flag
TMR0 = TMR0_RELOAD_VALUE; // Reload the calculated value for 1 ms
systemTick++;
}
}
// Setup Timer0 for 1ms interrupt
void setupTimer0(){
#define PRESCALER_VALUE 2
#define PRESCALER_MASK ~7
OPTION_REG &= PRESCALER_MASK; // Clear prescaler bits
OPTION_REG |= PRESCALER_VALUE; // Set prescaler value for 1:8
OPTION_REGbits.PSA = 0; // Assign prescaler to Tim0
OPTION_REGbits.T0CS = 0; // Set internal oscillator as clock source
TMR0 = TMR0_RELOAD_VALUE;
INTCONbits.T0IF = 0;
INTCONbits.T0IE = 1; // Enable Timer0 interrupt
}
// Get count atomically
unsigned int getTickCount(){
unsigned int count;
di(); // disable interrupts
count = systemTick;
ei(); // enable interrupts again
return count;
}
void performMode(){
static time_t modeDelay;
static char slowModeState = 1;
static char fastModeState = 1;
switch(mode){
case MODE_OFF:
// Always must save the current mode before put it into the IDLE
lastMode = mode; // We have to save the last mode first then put it into the IDLE state
mode = MODE_IDLE; // The rollover bug caused by here since we haven't save the last mode before put it into the IDLE state
GP0 = 0; GP5 = 0;
break;
case MODE_ON:
GP0 = 1; GP5 = 1;
break;
case MODE_SLOW:
if(slowModeState == 1){
GP0 = 1; GP5 = 1;
modeDelay.ticks = MS_TO_TICKS(100);
modeDelay.start = getTickCount();
slowModeState = 2; // Proceed the next step
}
else if(slowModeState == 2){
if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
// Delay not expired yet
return;
}
GP0 = ~GP0; GP5 = ~GP5; // Toggle
// Reload the start time
modeDelay.start = getTickCount();
}
break;
case MODE_FAST:
if(fastModeState == 1){
GP0 = 1; GP5 = 1;
modeDelay.ticks = MS_TO_TICKS(50);
modeDelay.start = getTickCount();
fastModeState = 2; // Proceed the next step
}
else if(fastModeState == 2){
if( !((getTickCount() - modeDelay.start) >= modeDelay.ticks) ){
// Delay not expired yet
return;
}
// Delay time expired, proceed toggle
GP0 = ~GP0; GP5 = ~GP5; // Toggle
// Reload the start time
modeDelay.start = getTickCount();
}
break;
case MODE_CANCEL:
// Cancel the current running mode, reset everything
modeDelay.start = 0;
modeDelay.ticks = 0;
slowModeState = 1;
fastModeState = 1;
// Also reset the outputs
GP0 = 0; GP5 = 0;
break;
default:
mode = MODE_IDLE;
}
}
void checkButton(){
#define DEBOUNCE_DELAY_MS 100u // Debounce delay is 100 ms
static time_t debounceTimer;
switch(buttonState){
case BUTTON_IDLE:
break;
case BUTTON_PRESS_DETECTED:
debounceTimer.ticks = MS_TO_TICKS(DEBOUNCE_DELAY_MS);
debounceTimer.start = getTickCount();
buttonState = BUTTON_DEBOUNCING;
break;
case BUTTON_DEBOUNCING:
if( !((getTickCount() - debounceTimer.start) >= debounceTimer.ticks) ){
// Debounce time has not expired yet
return;
}
// Debounce time has expired so check the button last time to confirm if it is still pressed
if(GPIObits.GP2 != 1){
// Not stable yet, debounce again
buttonState = BUTTON_PRESS_DETECTED;
}
// Button press is stable, confirm it
buttonState = BUTTON_PRESS_CONFIRMED;
break;
case BUTTON_PRESS_CONFIRMED:
buttonState = BUTTON_IDLE; // Change state so that it can process a new button press
if(mode != MODE_IDLE && mode != MODE_OFF){
// Cancel the running mode first
lastMode = mode; // save the last mode
mode = MODE_CANCEL; // purge the current one
performMode();
}
mode = lastMode + 1; // Switch to next mode
if(mode > LAST_MODE){
// Rewind mode to the beginning which is MODE_OFF
mode = MODE_OFF;
}
break;
default:
buttonState = BUTTON_IDLE;
}
}
void main(void) {
ANSEL = 0;
CMCON = 0b00000111; //turns comparators off
TRISIO = 0b00000100;
GPIO = 0;
TRISIO2 = 1; // Make GP2 pin as input
INTCONbits.INTF = 0;
INTCONbits.INTE = 1;
INTCONbits.GIE = 1;
OPTION_REG = 0b01000000; // Rising edge interrupt
setupTimer0();
// Super loop
while(1){
// Task 1: Button check
if(buttonState != BUTTON_IDLE){
checkButton();
}
// Task 2: Mode check
else if(mode != MODE_IDLE){
performMode();
}
}
return;
}

How to change LED flashing pattern by sw

I currently use PIC16F1827 to tact sw to led blinking pattern
We are creating a program to switch.
The input of sw is RA0, and when you press the button, it drops to Low.
In creating the program There is one problem.
For example, press sw while processing pat1 ();
There are times when I want to switch to the next lighting mode.
However, until the processing of pat1 (); is finished, the blinking pattern
It will not switch.
Is there a way to switch the flashing pattern at the moment you press sw?
Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 4000000
/*
* CONFIG will be omitted
*/
void internal_osc();
void io_int();
void tmr0_int();
void sw_scan();
void pat1();
void pat2();
void pat3();
void pat4();
char tmr0_cnt;
char sw_data;
char SwStatus;
char cnt;
void main(void){
internal_osc();
io_int();
tmr0_int();
while(1){
//sw_scan();
switch(sw_data){
case 1 :
pat1();
break;
case 2 :
pat2();
break;
case 3 :
pat3();
break;
case 4 :
pat4();
break;
default :
PORTB = 0x00;
break;
}
}
}
void interrupt ISR(void){
INTCONbits.TMR0IF = 0;
TMR0 = 130;
cnt++;
if(cnt>=15){
cnt=0;
sw_scan();
}
}
void internal_osc(void){
OSCCON = 0x6a;
}
void io_int(void){
TRISA = 0x01;
TRISB = 0x00;
ANSELA = 0x00;
ANSELB = 0x00;
}
void tmr0_int(void){
OPTION_REG = 0x82;
TMR0 = 130;
INTCONbits.TMR0IE = 1;
INTCONbits.GIE = 1;
}
void sw_scan(void){
if(PORTAbits.RA0 == 0){
__delay_ms(10);
if(PORTAbits.RA0 == 0){
if(SwStatus==0){
SwStatus = 1;
if(sw_data>4){
sw_data = 0;
}
sw_data++;
}
}
else{
SwStatus = 0;
}
}
else{
SwStatus = 0;
}
}
void pat1(void){
PORTB = 0x01;
__delay_ms(500);
PORTB = 0x02;
__delay_ms(500);
PORTB = 0x04;
__delay_ms(500);
PORTB = 0x08;
__delay_ms(500);
}
void pat2(void){
PORTB = 0x01;
__delay_ms(500);
PORTB = 0x03;
__delay_ms(500);
PORTB = 0x07;
__delay_ms(500);
PORTB = 0x0f;
__delay_ms(500);
}
void pat3(void){
PORTB = 0x0e;
__delay_ms(500);
PORTB = 0x0d;
__delay_ms(500);
PORTB = 0x0b;
__delay_ms(500);
PORTB = 0x07;
__delay_ms(500);
}
void pat4(void){
PORTB = 0x0e;
__delay_ms(500);
PORTB = 0x0c;
__delay_ms(500);
PORTB = 0x08;
__delay_ms(500);
PORTB = 0x00;
__delay_ms(500);
}
The problem with your design is that the CPU spends most of the time stuck in the __delay_ms() loops. It cannot respond to button pushes while it is busy counting through multiple calls to __delay_ms(500).
The solution is don't use a busy-loop to pass time. Instead, you have to allow the CPU to respond to other events while you monitor the passage of time. One way to do this is to record the timer tick count at the time of an event. And then repeatedly compare that previous tick value with the timer's current tick value within your while loop (while you continue to do other things such as checking whether the switch was pressed).
Since you have a __delay_ms() function, you probably have access to another function to get the timer's current tick value. In the example below I've assumed that this function is called __get_tick() but it may have a different name.
I've also taken the liberty of reducing all of your pattern functions into a multidimensional array. But don't get too distracted by that. The real solution I'm recommending is to use __get_tick() instead of __delay_ms().
#define NUM_PATTERNS 5
#define NUM_STEPS 4
uint8_t const patterns[NUM_PATTERNS][NUM_STEPS] = {
{0x00, 0x00, 0x00, 0x00}, // LEDs are off
{0x01, 0x02, 0x04, 0x08}, // pattern 1
{0x01, 0x03, 0x07, 0x0f}, // pattern 2
{0x0e, 0x0d, 0x0b, 0x07}, // pattern 3
{0x0e, 0x0c, 0x08, 0x00} // pattern 4
};
void main(void){
int current_pattern = 0;
int current_step = 0;
char prev_sw_data = 0;
uint32_t prev_tick = 0;
internal_osc();
io_int();
tmr0_int();
while(1){
bool was_switch_pressed = false;
bool has_500_ms_passed = false;
uint32_t current_tick = __get_tick();
// Check whether the switch has been pressed.
if (sw_data != prev_sw_data) {
was_switch_pressed = true;
// Remember the new sw_data setting.
prev_sw_data = sw_data;
// Change the pattern.
current_pattern = sw_data;
// Start at step 0 whenever the switch is pressed and
// the pattern is changed.
current_step = 0;
}
else {
// Check whether 500 ms has passed.
if ( (current_tick - prev_tick) > NUM_TICKS_IN_500_MS) )
{
has_500_ms_passed = true;
// Advance to the next step in the pattern
++current_step;
if (current_step >= NUM_STEPS) {
current_step = 0;
}
}
}
if (was_switched_pressed || has_500_ms_passed)
{
// Remember the new tick time.
prev_tick = current_tick;
PORTB = patterns[current_pattern][current_step];
}
}
}
The problem is that you are blocking the while(1) loop when you are in a pattern
One idea is to make the counter and give you a tact each 1ms and make the following change:
while(1){
check_if_50ms_passed{
switch(sw_data){
case 1 : pat1(); break;
case 2 : pat2(); break;
case 3 : pat3(); break;
case 4 : pat4(); break;
default : PORTB = 0x00;break;
}
}
}
You must keep a counter in each pattern:
void pat1(void){
couter_for_next_step++;
if(couter_for_next_step == 10)// 50ms * 10 steps = 500ms
{
couter_for_next_step = 0;
switch(port_change){
case 1 : PORTB = 0x01; break;
case 2 : PORTB = 0x02; break;
case 3 : PORTB = 0x04; break;
case 4 : PORTB = 0x08; break;
default: break;
}
}
and when you change the sw_data do not forget to:
couter_for_next_step = 10;
port_change = 1;
This will allow you to enter through the while(1) loop several times and also check the status of the pressed button
You can also add at a pressed button event to turn off the leds
you can use a ( high priority software ) interrupt for this
on a processor or OS that is able for multithreading you could use different threads, on a PIC you can use interrupts

If vs Case. If incrementing strangely

I'm learning embedded programming, and have a LED board and button board hooked up to the same PORT.
The commented out if statement under the chk_button function, certain buttons when activated will add to the num variable its intended increment + 1. IE: pushing button 1 will increment num by three. The current case statement works to spec.
I understand case statements when compiled effectively become look up tables, and thus have smaller footprint and run faster,but I am uncertain as to WHY im getting the bug that I am.
I'm also open to any other criticisms in my code.
// HARDWARE SETUP:
// PORTA is connected to the segments of the LED display. and to the pushbuttons.
// PORTA.0 corresponds to segment a, PORTA.1 corresponds to segement b, etc.
// PORTB bits 4-6 go to a,b,c inputs of the 74HC138.
// PORTB bit 7 goes to the PWM transistor base.
#include <avr/io.h>
#include <util/delay.h>
#define F_CPU 16000000
#define DIGIT_ONE 0x00
#define DIGIT_TWO 0x10
#define DIGIT_COLON 0x20
#define DIGIT_THREE 0x30
#define DIGIT_FOUR 0x40
//******************************************************************************
// debounce_switches
//Checks the state of the button number passed to it. It shifts in ones till
//the button is pushed. Function returns a 1 only once per debounced button
//push so a debounce and toggle function can be implemented at the same time.
//Adapted to check all buttons from Ganssel's "Guide to Debouncing"
//Expects active low pushbuttons on PINA port. Debounce time is determined by
//external loop delay times 12.
//Saves status of button (i) into a state array.
//******************************************************************************
int8_t debounce_switches(uint16_t *state, uint8_t i) {
state[i] = (state[i] << 1) | (! bit_is_clear(PINA, i)) | 0xE000;
if (state[i] == 0xF000) return 1;
return 0;
}
//******************************************************************************
// chk_buttons
//Checks the buttons. Calls debounce_switches in a loop passing both the state array
//and the current switch being checked.
//If debounce_switches returns a 1 for a switch, case statements determine which switch
//was activated, and increments count by the appropriate value.
//******************************************************************************
void chk_buttons(uint16_t *state, uint16_t *num)
{
uint8_t itr;
for( itr=0; itr<8; itr++)
{
if( debounce_switches(state, itr))
{
switch(itr)
{
case 0:
*num += 1;
break;
case 1:
*num += 2;
break;
case 2:
*num += 4;
break;
case 3:
*num += 8;
break;
case 4:
*num += 16;
break;
case 5:
*num += 32;
break;
case 6:
*num += 64;
break;
case 7:
*num +=128;
break;
}
/*
if (itr == 0) *num += 1;
else if(itr == 1) *num += 2;
else if(itr == 2) *num += 4;
else if(itr == 3) *num += 8;
else if(itr == 4) *num += 16;
else if(itr == 5) *num += 32;
else if(itr == 6) *num += 64;
else if(itr == 7) *num += 128;
*/
}
}
}
//******************************************************************************
// itoseven
// Accepts a number from 0 -9 Returns a hex value to display on the seven segment..
//******************************************************************************
uint8_t itoseven(uint8_t num)
{
uint8_t segnum;
switch(num)
{
case 0:
segnum = ~0x3F;
return segnum;
case 1:
segnum = ~0x06;
return segnum;
case 2:
segnum = ~0x5B;
return segnum;
case 3:
segnum = ~0x4F;
return segnum;
case 4:
segnum = ~0x66;
return segnum;
case 5:
segnum = ~0x6D;
return segnum;
case 6:
segnum = ~0x7D;
return segnum;
case 7:
segnum = ~0x07;
return segnum;
case 8:
segnum = ~0x7F;
return segnum;
case 9:
segnum = ~0x6F;
return segnum;
}
}
//***********************************************************************************
// segment_sum
//takes a 16-bit binary input value and displays the result to the LED board.
//***********************************************************************************
void segsum(uint16_t num) {
uint8_t ones;
uint8_t tens;
uint8_t hundreds;
uint8_t thousands;
//break up decimal sum into 4 digit-segments
ones = num % 10;
tens = (num/10)%10;
hundreds = (num/100)%10;
thousands = (num/1000)%10;
//determine how many digits there are
if(num < 10)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(num);
}
else if(num > 9 && num <100)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(ones);
_delay_ms(2);
PORTB = DIGIT_TWO;
PORTA = itoseven(tens);
}
else if(num > 99 && num < 1000)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(ones);
_delay_ms(2);
PORTB = DIGIT_TWO;
PORTA = itoseven(tens);
_delay_ms(2);
PORTB = DIGIT_THREE;
PORTA = itoseven(hundreds);
}
else if (num >999)
{
PORTB = DIGIT_ONE;
PORTA = itoseven(ones);
_delay_ms(2);
PORTB = DIGIT_TWO;
PORTA = itoseven(tens);
_delay_ms(2);
PORTB = DIGIT_THREE;
PORTA = itoseven(hundreds);
_delay_ms(2);
PORTB = DIGIT_FOUR;
PORTA = itoseven(thousands);
}
}
uint8_t main()
{
DDRB = 0xF0; //set port bits 4-7 B as outputs
uint16_t num = 0;
uint16_t state[8];
//initialize array values for debouncing
for(int i=0; i<8; i++)
{
state[i]= 0;
}
while(1)
{
//make PORTA an input port with pullups
DDRA = 0x00;
PORTA = 0xFF;
//enable tristate buffer for pushbutton switches
PORTB = 0x70;
//check the buttons. Increment by appropriate value if switch is active.
chk_buttons(&state, &num);
//rollover at 1023
if(num > 1023)
num = 1;
//switch PORTA to output
DDRA = 0xFF;
//Send num to display
segsum(num);
_delay_ms(2);
}
return 0;
}
I've update these functions to remove the switches as they are not needed at all. Note how the first uses an algorithm in place of a switch and the second uses a table that is easy to adjust in the future. Also, assert/error check your inputs and you'll be a happier camper when things go sideways.
void chk_buttons(uint16_t *state, uint16_t *num)
{
// Assert bad state and num here.
uint8_t itr;
for( itr=0; itr<8; itr++)
{
if( debounce_switches(state, itr))
{
*num += 1 << itr; // Could also use |= as that is what we are doing.
}
}
}
uint8_t itoseven(uint8_t num)
{
// assert num <= 9
const uint8_t kLookup[10] =
{~0x3f, ~0x06, ~0x5B, ~0x4F, ~0x66,
~0x6D, ~0x7D, ~0x07, ~0x7F, ~0x6F};
return kLookup[num];
}
For the last function, if you start at the 4-digit number check and work backwards to the single digit, it becomes a very simple loop to do your complex digit calculation. Just divide your mask from 10,000 to 10. I'll post that code a bit later as I have time.
Update
Here is something I coded up that demonstrates the idea. You will need to modify it for your own functions as right now it just outputs ASCII. It fails for negative numbers (as does your code), but that is very easy to add with a simple if check / ABS / prepend a minus character.
int main(void)
{
int num = 9876;
// Starting at the 5th digit. You could use 1000 instead if
// there were only 4 digits or less ever.
int digitMask = 10000;
char output[16];
int index = 0;
// Handle 0 case.
if(num== 0)
{
output[index++] = '0';
}
else
{
// Skip leading 0's
while((num / digitMask) == 0)
{
digitMask /= 10;
}
// While there may be digits to process...
while(digitMask)
{
// Grab MS Digit and mod it away from total.
int digit = num / digitMask;
num %= digitMask;
// Convert to ASCII (you would use your own function)
output[index++] = digit + '0';
// Update mask - note we could use a for/loop here just as easily.
digitMask /= 10;
}
}
// Terminate string and print it.
output[index] = 0;
printf("%s", output);
return 0;
}

Digits not displayed properly, Unable to control output at particular pin

I am trying to display my input at microcontroller ATmega16 to 7 segment displays.
I have found the following code on http://www.avr-tutorials.com/projects/atmega16-based-digital-clock but when I implimented it, it is not working. I am getting a fluctuating output i.e I am not able to control the output at particular pins.
I have two inputs temp1 and temp2, and I want to display each of them on three 7-segment displays. Also, I have not used Pins 2 & 3 as they are interrupt pins and I have used them somewhere else. Pin0,1,4,5,6,7 are used.
The code works fine when some delay is added, otherwise output is generated at random pins.
i.e. The output which I was suppose to display from PIND1 is displayed on all pins,
My code:
#include <avr/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define SegDataPort PORTC
#define SegDataPin PINC
#define SegDataDDR DDRC
#define SegCntrlPort PORTD
#define SegCntrlPin PIND
#define SegCntrlDDR DDRD
/*
* Function Description:
* Encode a Decimal Digit 0-9 to its Seven Segment Equivalent.
*
* Function Arguments:
* digit - Decimal Digit to be Encoded
* common - Common Anode (0), Common Cathode(1)
* SegVal - Encoded Seven Segment Value
*
* Connections:
* Encoded SegVal is return in the other G-F-E-D-C-B-A that is A is the least
* significant bit (bit 0) and G bit 6.
*/
unsigned char DigitTo7SegEncoder(int digit, unsigned char common)
{
unsigned char SegVal;
switch(digit)
{
case 0: if(common == 1) SegVal = 0b00111111;
else SegVal = ~0b00111111;
break;
case 1: if(common == 1) SegVal = 0b00000110;
else SegVal = ~0b00000110;
break;
case 2: if(common == 1) SegVal = 0b01011011;
else SegVal = ~0b01011011;
break;
case 3: if(common == 1) SegVal = 0b01001111;
else SegVal = ~0b01001111;
break;
case 4: if(common == 1) SegVal = 0b01100110;
else SegVal = ~0b01100110;
break;
case 5: if(common == 1) SegVal = 0b01101101;
else SegVal = ~0b01101101;
break;
case 6: if(common == 1) SegVal = 0b01111101;
else SegVal = ~0b01111101;
break;
case 7: if(common == 1) SegVal = 0b00000111;
else SegVal = ~0b00000111;
break;
case 8: if(common == 1) SegVal = 0b01111111;
else SegVal = ~0b01111111;
break;
case 9: if(common == 1) SegVal = 0b01101111;
else SegVal = ~0b01101111;
}
return SegVal;
}
int main(void)
{
int temp1,temp2;
//Suppose my input is 105 and 210, i.e. temp1=105 and temp2=210;
// it contains other information also, not required here
SegDataDDR = 0xFF;
SegCntrlDDR = 0xF3;
SegCntrlPort = 0xF3;
SegDataPort = 0x00;
while(1){
SegDataPort = DigitTo7SegEncoder(temp1%10,1);
SegCntrlPort = ~0x01;
SegDataPort = DigitTo7SegEncoder((temp1/10)%10,1);
SegCntrlPort = ~0x02;
SegDataPort = DigitTo7SegEncoder(temp1/100,1);
SegCntrlPort = ~0x10;
SegDataPort = DigitTo7SegEncoder(temp2%10,1);
SegCntrlPort = ~0x20;
SegDataPort = DigitTo7SegEncoder((temp2/10)%10,1);
SegCntrlPort = ~0x40;
SegDataPort = DigitTo7SegEncoder(temp2/100,1);
SegCntrlPort = ~0x80;
}}
You don't set the DDRs to output anywhere. The pins you want to use as outputs must have the corresponding bit in DDRx set to 1. Otherwise it remains an input pin.
An input pin will show an output, but with a lower current and a more slowly rising edge. Drawing on that current with an LCD may cause the voltage to drop.
Provided that these 7-segment components don't contain any sequential logic inside this is what actually happens:
SegDataPort = DigitTo7SegEncoder(temp1%10,1); // You write the data
SegCntrlPort = ~0x01; // You enable the first 7-segment component
// Then without any delay...
SegDataPort = DigitTo7SegEncoder((temp1/10)%10,1); // You write other data
SegCntrlPort = ~0x02; // You enable the second component
// The without any delay...
So actually the 7-segment component displays previous and next digits at once for a short time.
What about this? (just a pseudocode)
SegCntrlPort = 0xFF; // hide everything
SetDataPort = ... value1 ...;
SegCntrlPort = ~0x01; // show it
WaitForAWhile();
SegCntrlPort = 0xFF; // hide everything to avoid unwanted transitions
SetDataPort = ... value2 ...;
SegCntrlPort = ~0x02; // show it
WaitForAWhile();
And so on.

How do I properly reset buffer so next read is correct?

My project involves sending commands from PuTTY to a Dragon12-Plus2 development board(CodeWarrior 5.1) via an XBEE Wifi. I am using 4 commands to control the leds on the Dragon board {led_enable(), leds_on(integer in hex or decimal here), leds_off(), and led_disable()} from the LBE_DRAGON12_Plus project from LBEbooks.com. Here is my code:
// Final Project: Control leds via XBEE Wifi
#include <hidef.h> /* common defines and macros */
#include <mc9s12dg256.h> /* derivative information */
#include "queue.h"
#pragma LINK_INFO DERIVATIVE "mc9s12dg256b"
#include "main_asm.h" /* interface to the assembly module */
void main(void){
int x,y,z,parser;
int cmdLen = 0;
char c,d,e;
char cbuff[20];
PLL_init(); // set system clock frequency to 24 MHz
lcd_init(); // enable lcd
SCI1_init(9600); // initialize SCI1 at 9600 baud
SCI0_init(9600); // initialize SCI0 at 9600 baud
seg7_disable(); // disable 7 segment display
led_enable(); // enable 8 leds
while(1){
if(SCI1SR1_RDRF == 1){
c = inchar1();
cbuff[cmdLen] = c;
outchar0(cbuff[cmdLen]);
if(c == ';'){
if((cbuff[0] == 'l') && (cbuff[10] == ';')){
leds_off();
cmdLen = 0;
}
else if((cbuff[0] == 'l') && (cbuff[12] == ';')){
led_enable();
cmdLen = 0;
}
else if((cbuff[0] == 'l') && (cbuff[4] == 'd')){
led_disable();
cmdLen = 0;
}
else if((cbuff[0] == 'l') && (cbuff[13] == ';')){
d = cbuff[10];
e = cbuff[11];
switch(d){ // change first number to integer
case('0'):
x = 0x00;
break;
case('1'):
x = 0x10;
break;
case('2'):
x = 0x20;
break;
case('3'):
x = 0x30;
break;
case('4'):
x = 0x40;
break;
case('5'):
x = 0x50;
break;
case('6'):
x = 0x60;
break;
case('7'):
x = 0x70;
break;
case('8'):
x = 0x80;
break;
case('9'):
x = 0x90;
break;
case('a'):
x = 0xa0;
break;
case('b'):
x = 0xb0;
break;
case('c'):
x = 0xc0;
break;
case('d'):
x = 0xd0;
break;
case('e'):
x = 0xe0;
break;
case('f'):
x = 0xf0;
break;
default:
break;
}
switch(e){ // change second number to integer
case('0'):
y = 0x00;
break;
case('1'):
y = 0x01;
break;
case('2'):
y = 0x02;
break;
case('3'):
y = 0x03;
break;
case('4'):
y = 0x04;
break;
case('5'):
y = 0x05;
break;
case('6'):
y = 0x06;
break;
case('7'):
y = 0x07;
break;
case('8'):
y = 0x08;
break;
case('9'):
y = 0x09;
break;
case('a'):
y = 0x0a;
break;
case('b'):
y = 0x0b;
break;
case('c'):
y = 0x0c;
break;
case('d'):
y = 0x0d;
break;
case('e'):
y = 0x0e;
break;
case('f'):
y = 0x0f;
break;
default:
break;
}
z = (x + y); // add integers together
leds_on(z); // display hex number on leds
cmdLen = 0; // reset parser
}
for(parser = 0; parser < 20; parser++){
cbuff[parser] = '\0';
}
}
cmdLen++;
}
}
}
The first command i send works as expected (for instance if i type leds_on(0xaa); in PuTTY the 4 corresponding leds light up), but any command I send after does nothing. I am not sure how to properly reset cbuff so that it operates the same way each time, not just the first time.
EDITED TO SHOW SUGGESTED CHANGES!
If you want to clear all of the elements in the array, then memset would be a wise choice:
memset(cbuff, 0, 20);
That being said, I would strongly agree with #John Bollinger that separating the input and the interpretation functionality would be wise.
However, depending on the application memset might be overkill. A simple cleared array may also be achieved by simply setting the elements of the array to null character ('\0'). Simply call cbuff[0] = '\0' for each element in the array.
What an odd parser. I'd suggest you separate your input stage from your command interpretation stage. That is, read and count characters until you receive a semicolon (;) command terminator, and only then try to interpret the command.
Reset the parser variable (which would more accurately be named something like command_length) only after each attempt to interpret the command, whether successful or not, or if the buffer is about to overflow.

Resources