Buggfix Plus
Aktuellt datum och tid: 21.26 2019-11-11

Alla tidsangivelser är UTC + 1 timme




Svara på tråd  [ 20 inlägg ]  Gå till sida 1, 2  Nästa
Författare Meddelande
InläggPostat: 15.23 2017-05-22 

Blev medlem: 01.58 2011-03-03
Inlägg: 120
Efter åratal av inaktivitet och paus i många projekt har jag nyligen genomfört ett lite mindre projekt som precis avslutats. Tänkte jag kunde dela det här och få lite idéer på vidareutveckling eller andra tips inför framtiden.

Modulen är till för att kunna mäta tiden för att skjuta ner fällmål typ skidskytte. 5 st mindre mål ska träffas och när alla har fällts stoppas tiden. I skytteföreningen brukar skyttekvällar avslutas med att tävla om bästa tid samtidigt som Survivors låt "Eye of the tiger" spelas för att öka stressnivån lite. ;)

Fällmålen återställs med att dra i ett snöre, då åker en båge upp och fäller tillbaka målen. Sensorerna (tungelement) monterades på bågen och magneter limmades på varje enskilt litet mål. Så när alla tungelementen leder så kortsluts signalen till jord.

Använde en Arduino nano, en extern oscillator som bättre tidsreferens, en vanlig 16x2 LCD jag hade liggandes och lite fina knappar från farnell. Knapparnas inbyggda LED signalerar att modulen förväntar sig att man ska trycka på den specifika knappen, de två knappar som är märkta "Skytt X" är parallellkopplade med fällmålens sensorer.

"Företagsloggan" Sten hård vara med fyrkantvågen med ringningar är en ordlek då mitt smeknamn är Sten. Märker mina projekt med den för skoj skull och företaget finns inte (ännu i alla fall).

Oscillatorn har tyvärr inte riktigt tillräckligt hög noggrannhet för det som behövs (endast 100 ppm) för en upplösning på hundradelar (maxtiden är 10 minuter, 9:59.99). Helt enkelt för att skärmen inte har plats för fler tecken och mer behövs oftast inte. Trodde i min enfald att det fanns 5V, 4 MHz oscillatorer med ca 10-20 ppm i samma kapsel ( dip-8 ), men inte hittat nån...

Har använt PIC tidigare men tänkte det skulle bli lätt att bara slänga ihop något med en Arduino, det kom tillbaka och bet mig rätt ordentligt. I Arduino nano är tydligen inte BOR-spänningen anpassad för 5V matning utan 3,3V. Det betydde att ibland när man slog till strömmen så hamnade spänningen (under kort tid) under gränsen för definierat beteende hos processorn men inte under BOR-spänningen så allt hängde sig bara. Efter mycket om och men lyckades jag programmera om bootloadern och då skriva om Fuse-bitsen till en BOR-spänning på 4,3V.

Hårdvaran (och mjukvaran till viss del) är förberedd för att kunna mata ut tiden till en stor 7-seg LED display. En där varje siffra får ett 8-bit seriell till parallell shiftregister typ 74HC595. Därav att SPI hårdvaran i AVR:en skickar ut en del data, displayen är dock inte byggd ännu.

Lådan gjordes i PLA med en 3d-printer, frontpanelen gjordes i svart plexi med en laserskärare. Panelens text och logga "graverades" med låg effekt på lasern samtidigt som den skars ut med hög effekt. Sen m.h.a. vit akrylfärg och en pappersservett geggades det "graverade" igen med färgen så att det blev vitt och tydligt. Resultat blev inte perfekt med det blev definitivt tillräckligt snyggt.

Anledningen till att jag använde D-Sub 9 kontakter var att det fanns tillgängligt gratis för mig. Alla kablar internt i lådan är kontakterade med stiftlister så att det är möjligt att serva enkelt när något går sönder.

Kod: [Expandera/Minimera] [Hämta] (Untitled.txt)
// Tidtagare för "eye of the tiger" hos Chalmers skytteförening
// 2 fällmål
// Skrivet av: Sven "Sten" Åkersten


// PORTx set output value here if conf as output
// DDRx configure input/output, 0 = input, 1 = output
// PINx port input register, read here if conf as input

#include <avr/io.h>
#include <avr/interrupt.h>

#include <LiquidCrystal.h>

#include <avr/wdt.h>
 
#define STATE_INIT 0b00000000
#define STATE_CLEAR 0b00000111
#define STATE_WAIT_TO_START 0b00000001
#define STATE_RUNNING_BOTH 0b00000110
#define STATE_RUNNING_PL1 0b00000010
#define STATE_RUNNING_PL2 0b00000100

#define MAIN_BUTTON (0b00000001)
#define PLAYER_1_BUTTON (0b00000010)
#define PLAYER_2_BUTTON (0b00000100)
#define MAIN_LED (0b00001000)
#define PLAYER_1_LED (0b00010000)
#define PLAYER_2_LED (0b00100000)
#define BUTTON_PORT PINC
#define LED_PORT PORTC

#define MAXIMUM_TIME (300000)

#define DEBOUNCE_TIME 150 //testat till 150

#define EXT_OSC 0b00100000
#define SHIFT_REGISTER DDRB
#define LATCH_REGISTER DDRD
#define SHIFT_PORT PORTB
#define LATCH_PORT PORTD
#define DATA (1<<PB3)           //MOSI (SI)
#define LATCH (1<<PD4)          //
#define CLOCK (1<<PB5)          //SCK  (SCK)


LiquidCrystal lcd(2,3,7,8,9,10);

volatile unsigned long int timer_tick_vola = 0;  //timer value from interrupt
volatile unsigned long int last_tick_1_vola = 0; //time at last player 1 trigged from interrupt
volatile unsigned long int last_tick_2_vola = 0; //time at last player 2 trigged from interrupt
volatile unsigned short int holdoff = MAIN_BUTTON; // holdoff for buttons for interrupt

unsigned short int system_state = STATE_INIT;    // current system state
unsigned short int debounce_timer_holdoff = 0b00000000; // holdoff for statemachine, timer or internal state
unsigned long int actual_tick_1 = MAXIMUM_TIME;  //last player 1 time, non volatile
unsigned long int actual_tick_2 = MAXIMUM_TIME;  //last player 2 time, non volatile
unsigned long int time_to_display_1 = MAXIMUM_TIME;  //time to display player 1 time, non volatile
unsigned long int time_to_display_2 = MAXIMUM_TIME;  //time to display player 2, non volatile
unsigned long int start_tick_1 = 0;   //start time for player 1, non volatile
unsigned long int start_tick_2 = 0;   //start time for player 2, non volatile
unsigned long int timer_tick = 0;     //timer value copy to use in state machine, non volatile
unsigned long int last_tick_1 = 0;    //last time player 1 copy to use in state machine, non volatile
unsigned long int last_tick_2 = 0;    //last time player 2 copy to use in state machine, non volatile

void setup()
{

    delay(1000);
   
    // initialize pinsz
    DDRC = ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // inputs
    DDRC |= (PLAYER_1_LED | PLAYER_2_LED | MAIN_LED); // output
    PORTC = (PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // all inputs with internal pullup

    DDRB = 0b11111111; // all output,

    //initialize SPI module and pins
    SHIFT_REGISTER |= (DATA | CLOCK);     //Set control pins as outputs
    LATCH_REGISTER = (LATCH | 0b11111111) & ~EXT_OSC ;
   
    SHIFT_PORT &= ~(DATA | CLOCK);                //Set control pins low
    SPCR = (1<<SPE) | (1<<MSTR);  //Start SPI as Master

    //Pull LATCH low (Important: this is necessary to start the SPI transfer!)
    LATCH_PORT &= ~LATCH;

    // initialize Timer1
    cli();          // disable global interrupts
    TCCR1A = 0;     // set entire TCCR1A register to 0
    DDRD = 0b11011111;  // T1 as input
    TCCR1B = 0b00001111;     // turn on CTC mode and set external clock
    //TCCR1B = 0b00001001;     // turn on CTC mode and set prescaler as 1
    // set compare match register to desired timer count:
    OCR1A = 7999; // calculated to 7999 for 2 ms
    //OCR1A = 31999; // calculated to 31999 for 2 ms

    // enable timer compare interrupt:
    TIMSK1 |= (1 << OCIE1A);
    sei();          // enable global interrupts

    wdt_enable(WDTO_4S);

    lcd.begin(16,2);
    lcd.print("Skytt 1  Skytt 2");
   
    // Start serial com.
    Serial.begin(9600);

}

void loop() {
  // statemachine that runs over and over again
  wdt_reset();
 
  if (system_state == STATE_INIT) // inital state
  {
      //print_time_serial(1,actual_tick_1,actual_tick_2);  // display last time
      time_to_display_1 = actual_tick_1;
      time_to_display_2 = actual_tick_2;

      LED_PORT |= (PLAYER_1_LED | PLAYER_2_LED);  // turn on pla 1 and 2 led, to wait for them to be pressed
      LED_PORT &= ~MAIN_LED;  // turn off main button led
     
      if(holdoff & PLAYER_1_BUTTON)  // if player 1 is pressed
      {
          debounce_timer_holdoff |= PLAYER_1_BUTTON;
          // clear pla 1 led
          LED_PORT &= ~PLAYER_1_LED;
      }
      if(holdoff & PLAYER_2_BUTTON) // if player 2 is pressed
      {
          debounce_timer_holdoff |= PLAYER_2_BUTTON;
          // clear pla 1 led
          LED_PORT &= ~PLAYER_2_LED;
      }
      if( (debounce_timer_holdoff & PLAYER_1_BUTTON) && (debounce_timer_holdoff & PLAYER_2_BUTTON) ) // if both player have been pressed, go to next state
      {
         debounce_timer_holdoff &= ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON);
         system_state = STATE_CLEAR;
      }   
  }
 
  if(system_state == STATE_CLEAR)
  {
      //print_time_serial(2,actual_tick_1,actual_tick_2);  // display last time
      time_to_display_1 = actual_tick_1;
      time_to_display_2 = actual_tick_2;

      holdoff &= ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // clear all buttons
      delay(50);  // wait to see if any button is depressed so it will set it's corresponding holdoff
      system_state = STATE_WAIT_TO_START;   //change state
      debounce_timer_holdoff |= MAIN_BUTTON;   //set main button state machine holdoff
  }
 
  if (system_state == STATE_WAIT_TO_START)
  {   
      if( !( holdoff & (PLAYER_1_BUTTON | PLAYER_2_BUTTON) )  )  // if any player button has been pressed since cleared in last state, return to that state. Makes sure player buttons is low (targets reset)
      {       
          if(holdoff & MAIN_BUTTON & ~debounce_timer_holdoff)  // if statemachine holdoff is cleared and main button been pressed, start time and change state. If main button is clear, clear main button holdoff to enable start else go back to clearing state
          {
              LED_PORT &= ~MAIN_LED; //clear main button led
             
              cli();
              start_tick_1 = last_tick_1_vola;
              start_tick_2 = last_tick_2_vola;
              sei();
             
              system_state = STATE_RUNNING_BOTH;
          }
          else if (~holdoff & MAIN_BUTTON)
          {
               time_to_display_1 = 0;
               time_to_display_2 = 0;
               //print_time_serial(3,0,0);   // print zeros, system ready to start
               LED_PORT |= MAIN_LED;  //turn on main button led       
               debounce_timer_holdoff &= ~MAIN_BUTTON;           
          }
          else
          {
               //print_time_serial(3,actual_tick_1,actual_tick_2);  // display last measured time again, system NOT ready to start
               time_to_display_1 = actual_tick_1;
               time_to_display_2 = actual_tick_2;
               LED_PORT &= ~MAIN_LED; //clear main button led
               system_state = STATE_CLEAR;
          }
      }
      else
      {
           LED_PORT &= ~MAIN_LED; //clear main button led
           system_state = STATE_CLEAR;
      } 
  }
 
  cli();
  timer_tick = timer_tick_vola; // copy current timer tick from interrupt
  sei();

  if(system_state == STATE_RUNNING_PL1 || system_state == STATE_RUNNING_BOTH ) // if player time is running, recalculate time and display.
  {

      LED_PORT &= ~MAIN_LED;  //clear main button led, turn on pl 1
      LED_PORT |= PLAYER_1_LED;

      if( (actual_tick_1 = (timer_tick - start_tick_1)) < DEBOUNCE_TIME) // if time since start less then minimum time, set mimimum time
      {
          actual_tick_1 = 0;
      }   
      else // if time since start greater then minimum time
      {   
           time_to_display_1 = actual_tick_1 - DEBOUNCE_TIME;
          //print_time_serial(41,actual_tick_1,actual_tick_2); //display time pla 1
      }
     
      if(holdoff & PLAYER_1_BUTTON) // if player 1 holdoff
      {
          cli();
          last_tick_1 = last_tick_1_vola;
          sei();
         
          if( timer_tick - last_tick_1 > DEBOUNCE_TIME ) // Wait until debounce time have passed
          {
              if( ~BUTTON_PORT & PLAYER_1_BUTTON) // if button is still pressed, Calculate finish time and clear running state of that player, when both player been clear state return to init
              {
                  LED_PORT &= ~PLAYER_1_LED; //clear pla 1 led
                  debounce_timer_holdoff |=  MAIN_BUTTON;
                  time_to_display_1 = actual_tick_1 = last_tick_1 - start_tick_1;
                  system_state &= ~STATE_RUNNING_PL1;
              }
              else // else it was a button bounce, clear holdoff and do nothing
              {
                  holdoff &= ~PLAYER_1_BUTTON;
              }
          }   
      }
      else if(actual_tick_1 > MAXIMUM_TIME) // if time since start greater then maximum time and button not been pressed, terminate
      {   
          LED_PORT &= ~PLAYER_1_LED; //clear pla 1 led
          debounce_timer_holdoff |= MAIN_BUTTON;
          time_to_display_1 = actual_tick_1 = MAXIMUM_TIME;
          system_state &= ~STATE_RUNNING_PL1;
      }   
  }

  if(system_state == STATE_RUNNING_PL2 || system_state == STATE_RUNNING_BOTH ) // if player time is running, recalculate time and display.
  {

      LED_PORT &= ~MAIN_LED;  //clear main button led, turn on pl 2
      LED_PORT |= PLAYER_2_LED;

      if( (actual_tick_2 = (timer_tick - start_tick_2)) < DEBOUNCE_TIME) // if time since start less then minimum time, set mimimum time
      {
          time_to_display_2 = 0;
      }   
      else // if time since start greater then minimum time
      {   
          //print_time_serial(42,actual_tick_1,actual_tick_2); //display time pl 2
          time_to_display_2 = actual_tick_2 - DEBOUNCE_TIME;
      }
 
      if(holdoff & PLAYER_2_BUTTON) // if player 2 holdoff
      {
          cli();
          last_tick_2 = last_tick_2_vola;
          sei();
         
          if( timer_tick - last_tick_2 > DEBOUNCE_TIME ) // Wait until debounce time have passed
          {
              if( ~BUTTON_PORT & PLAYER_2_BUTTON) // if button is still pressed, Calculate finish time and clear running state of that player, when both player been clear state return to init
              {
                  LED_PORT &= ~PLAYER_2_LED; //clear pla 2 led
                  debounce_timer_holdoff |=  MAIN_BUTTON;
                  time_to_display_2 = actual_tick_2 = last_tick_2 - start_tick_2;
                  system_state &= ~STATE_RUNNING_PL2;
              }
              else // else it was a button bounce, clear holdoff and do nothing
              {
                  holdoff &= ~PLAYER_2_BUTTON;
              }
          }   
      }
      else if(actual_tick_2 > MAXIMUM_TIME) // if time since start greater then maximum time and button not been pressed, terminate
      {   
          LED_PORT &= ~PLAYER_2_LED; //clear pla 2 led
          debounce_timer_holdoff |= MAIN_BUTTON;
          time_to_display_2 = actual_tick_2 = MAXIMUM_TIME;
          system_state &= ~STATE_RUNNING_PL2;
      }   
  }

  print_time_lcd(time_to_display_1,time_to_display_2);
  print_time_serial(1,time_to_display_1,time_to_display_2);

}


void output_byte_spi(unsigned short data)
{
    //Shift in some data
    SPDR = data;
    //Wait for SPI process to finish
    while(!(SPSR & (1<<SPIF))) ;
   
    //Latch into output latch
    LATCH_PORT |= LATCH;
    delayMicroseconds(1);
    LATCH_PORT &= ~LATCH;
    delayMicroseconds(1);
}

void send_message_spi(char* data,unsigned short message_length)
{
    for(int i = 0; i < message_length ; i++)
    {   
        //Shift in some data
        SPDR = data[i];
        //Wait for SPI process to finish
        while(!(SPSR & (1<<SPIF))) ;
    }
   
    //Latch into output latch
    LATCH_PORT |= LATCH;
    delayMicroseconds(1);
    LATCH_PORT &= ~LATCH;
    delayMicroseconds(1);

}

void print_time_lcd(unsigned long time1, unsigned long time2)
{
    unsigned int t1_mm = time1/30000;
    unsigned int t2_mm = time2/30000;;
    unsigned int t1_ss = (time1/500)%60;
    unsigned int t2_ss = (time2/500)%60;
    unsigned short int t1_cc = (time1/5)%100;
    unsigned short int t2_cc = (time2/5)%100;
    char lcd_char_time[16] ={'-',':','-','-','.','-','-',' ',' ','-',':','-','-','.','-','-'};

    if(time1 != MAXIMUM_TIME)
    {
       lcd_char_time[3] = t1_ss%10 + 48;
       lcd_char_time[2] = (t1_ss/10)%10 + 48;
       lcd_char_time[0] = t1_mm + 48;

       lcd_char_time[6] = t1_cc%10 + 48;
       lcd_char_time[5] = (t1_cc/10)%10 + 48;
    }
    if(time2 != MAXIMUM_TIME)
    {
       lcd_char_time[12] = t2_ss%10 + 48;
       lcd_char_time[11] = (t2_ss/10)%10 + 48;
       lcd_char_time[9] = t2_mm + 48;

       lcd_char_time[15] = t2_cc%10 + 48;
       lcd_char_time[14] = (t2_cc/10)%10 + 48;
    }
    lcd.setCursor(0, 1);

    for(int i = 0; i < 16 ; i++)
        lcd.print(lcd_char_time[i]);

    send_message_spi(lcd_char_time,16);

}


void print_time_serial(unsigned short state, unsigned long time1, unsigned long time2) //debug printing of current state, volatile holdoff and the last measured time for each player
{
    unsigned int t1_ss = time1/500;
    unsigned int t2_ss = time2/500;
    unsigned short int t1_cc = (time1/5)%100;
    unsigned short int t2_cc = (time2/5)%100;
   
    // calculate time as seconds and hundreds of seconds before sending on serial port
   
    Serial.print("State: ");
    Serial.print(state);
    Serial.print(" Holdoff: ");
    Serial.print(holdoff);
    Serial.print(" Time 1: ");
    Serial.print(t1_ss);
    Serial.print(".");
    if(t1_cc < 10)
    {
        Serial.print("0");
    }
    Serial.print(t1_cc);
    Serial.print(" Time 1: ");
    Serial.print(t2_ss);
    Serial.print(".");
    if(t2_cc < 10)
    {
        Serial.print("0");
    }
    Serial.println(t2_cc);   
}
   

ISR(TIMER1_COMPA_vect) // interrupt runs once for every timer tick. If button holdoff is clear and button is pressed, store current time in volatile variable of corresponding player
{   
    unsigned short int new_button_state;
   
    timer_tick_vola++;  //increment main timer, one tick (2 ms) have passed
   
    new_button_state = (~BUTTON_PORT & ~holdoff) & (MAIN_BUTTON | PLAYER_1_BUTTON | PLAYER_2_BUTTON);  // if a button is pressed and holdoff is clear set high button state, buttons is active low

    if(MAIN_BUTTON & new_button_state) // store both player time if start (main) button is pressed. The start time ie.
    {
        holdoff |= MAIN_BUTTON;
        last_tick_1_vola = timer_tick_vola;
        last_tick_2_vola = timer_tick_vola;       
    }

    if(PLAYER_1_BUTTON & new_button_state)  // store player time if player 1 button is pressed. The finish time for that player ie.
    {
        holdoff |= PLAYER_1_BUTTON;
        last_tick_1_vola = timer_tick_vola;         
    }

    if(PLAYER_2_BUTTON & new_button_state) // store player time if player 2 button is pressed. The finish time for that player ie.
    {
        holdoff |= PLAYER_2_BUTTON;
        last_tick_2_vola = timer_tick_vola;         
    }

}


Logga in för att visa de filer som bifogats till detta inlägg.


Upp
 Profil  
 
InläggPostat: 15.55 2017-05-22 
Användarvisningsbild

Blev medlem: 18.09 2011-07-01
Inlägg: 9304
Ort: Helsingborg
Snyggt, enkelt och bra beskrivet! Tack.


Upp
 Profil  
 
InläggPostat: 14.52 2017-06-23 

Blev medlem: 01.58 2011-03-03
Inlägg: 120
En liten men stor uppdatering! Efter en generös donation av Icecap så har jag nu 20 fungerade stora LED displayer som jag ska använda för att presentera tiden på under skjutning.
Displayerna är 8*8 pixlar (20 cm * 20 cm), 3 gula leds per pixel (tänk skylt på en buss). De styrs genom att man shiftar in data i shiftregister och sen latchar ut till drivstegen.
Varje display har 2 dataingångar, en klocka, latch/strobe för att klocka in till drivstegen och en output enable som man kan PWM styra för ljusstyrka.

Varje dataingång har varsin halva (delat horisontellt) och man klockar in 32 bitar (en för varje pixel) i ett lite klurigt mönster. Det är där jag är just nu för att lösa mjukvaran.
För att hårdvaran ska vara enkel tänkte jag bara använda en enda dataingång och seriekoppla hela undre och övre halvan på den sammansatta displayen, då kan jag använda hårdvaran jag har och SPI hårdvaran i Arduinon som klockar ut en byte i taget. Kruxet blir att tillräckligt snabbt få ut hela displayen och räkna fram vilka bytes som ska klockas ut, ska uppdatera displayens tid med hundradelsupplösning live... Så hundradelssiffran måste "flimmra".

Mönstret man klockar in en en halva 4*8 (4 rader 8 kolumner) pixlar är uppbyggt som att man börjar i nedre högra hörnet. Sen klockar man in 4 bitar (halva bredden) för de 4 första. Sen börjar en ny rad på 4 pixlar vilket upprepas 3 gånger till, så man har byggt den nedre högra kvadraten på 4*4 pixlar.

Man kan helt enkelt se det som att varje byte man skiftar in delas upp i två nibbles som läggs ovanpå varandra. Som i sin tur bygger upp kvadrater på 4*4 pixlar. Två såna kvadrater i bredd (eller 4 byte) per data in.

Men hur gör jag mjukvaran så effektiv som möjligt? Jag måste ju dela upp varje tecken (vill även kunna skriva text) i massa nibbles som jag sen måste hantera och skifta hit och dit. Någon som har några smidiga idéer? Vilken "font" ska man göra? 5*7 pixlar? 4*6 fast dubbelt så stor?

Tanken är att ha ca 10-12 displayer (varje med 8*8 pixlar) i en lång rad och skriva på.


Upp
 Profil  
 
InläggPostat: 16.37 2017-06-23 
Användarvisningsbild

Blev medlem: 14.52 2005-01-10
Inlägg: 24148
Ort: Aabenraa, Danmark
JAG gör så att jag reserverar ett minnesarea som jag ritar i.
Den första rutin jag gör är en Set_Pixel(x, y) rutin som sedan sätter rätt bit i minnet så att utläsningen är rak och enkel.
Med de moduler blir det alltså 8*8*20 bits = 160 bytes.

Jag använder en 5×7(8) teckenmatris och i er system skulle jag rita ut nästa tiden och vänta med stroben till rätt tid.

Jag har även en rutin för att få data på hur lång en sträng är i pixlar (kom ihåg 1 tom pixel mellan tecken) då jag oftast använder olika teckenbredd (1, komma, punkt osv. är smala). I er fall vill jag dock rekommendera att hålla talen på fast bredd (5 pixlar) då det annars blir fladdrigt.

Med Set_pixel(x, y) kan man sedan scanna av teckentabellen och en '1' betyder att pixeln ska sättas, en '0' betyder att inget ska göras.

Allt detta görs i en "skriv ut text"-rutin och i mitt fall använder jag en flagga som anger huruvida texten ska skrives proportionellt eller inte (alltså med varierande teckenbredd eller fast). Då kan jag antingen centrera, högerställa eller vänsterställa texter.

När den nya tiden är latchad ut kan man rita den nya tiden och skicka datan och vänta med stroben osv.
Ska man skriva över de data som väntar är det ju bara att skriva dom utan att stroba innan.


Upp
 Profil  
 
InläggPostat: 13.07 2017-06-25 

Blev medlem: 01.58 2011-03-03
Inlägg: 120
Efter några timmars kodande har jag nu kommit en liten bit påvägen. Kan nu skriva ut en siffra per 8*8 display.

För att ens ha en chans att ha koll på hur jag ska få det att fungera har jag reserverat minne som representerar hur det ser ut på skärmen. Sen låter jag SPI enhetens interrupt hämta de två nibbles som skrivas ut. Lite mindre effektivt men det var hopplöst att hålla i huvudet annars.

Det som ska göras härnäst är att skapa datan som man vill ska ut på displayen, funderar egentligen hur man ska göra för att kunna skifta in delar av tecken på ett bra sätt. Varje panels "minnesskugga" består ju av 8 st bytes, en för varje rad. De ligger i sin tur i en array så det blir en 2 dimensionell sådan. Lite klurigt verkar det bli att kapa tecknen på ett smidigt sätt... Funderar på bektrakta de som 16 bitars tal som man skiftar och sen plockar ut varje byte som egna delar. Bra ide?


Upp
 Profil  
 
InläggPostat: 13.33 2017-06-25 
Användarvisningsbild

Blev medlem: 14.52 2005-01-10
Inlägg: 24148
Ort: Aabenraa, Danmark
Hur du än gör måste du ha rutiner som fyller i data i "bildminnet".
Mitt tips är att du gör det enkelt för dig:
* Reserver ett "bildminne" på 160 bytes (i detta fall).
* Skapa en rutin som kör ut dessa data vid att sända byte för byte.
* Skapa en rutin som setter en given pixel (x, y) på displayen. Jag har ofta gjort en 2-dimensionell uppslagstabell då de moduler ni har upprepar sig var 4*4 pixlar.

Med dessa rutiner kan du rakt av räkna med att om du kallar Set_Pixel (x, y) blir den visad rätt på displayen oavsett var den finns i bildminnet.

Tecken kan sedan läggas in i bildminnet vid en for (x) for (y) där teckentabellens bits testas.

Mellan varje tecken stegar man 1 tom pixelkolumn.

Jag anser att det sätt du än så länge har vald är mycket besvärlig och krånglig.


Upp
 Profil  
 
InläggPostat: 21.12 2017-06-25 

Blev medlem: 01.58 2011-03-03
Inlägg: 120
Det jag har är ju redan i princip:
Citera:
* Reserver ett "bildminne" på 160 bytes (i detta fall).
* Skapa en rutin som kör ut dessa data vid att sända byte för byte.

Skillnaden är att jag låter SPI-interruptet sköta utskriften och sen låter jag SPI-hårdvaran sköta utklockningen.

Det som jag vill göra nu är ju, fast på ett effektivare sätt:
Citera:
* Skapa en rutin som setter en given pixel (x, y) på displayen. Jag har ofta gjort en 2-dimensionell uppslagstabell då de moduler ni har upprepar sig var 4*4 pixlar.


Citera:
Hur du än gör måste du ha rutiner som fyller i data i "bildminnet".

Ja, precis. Frågan är hur jag kan göra det effektivt då det innebär en herrans massa bearbetning om jag inte är försiktig. Att hantera det pixel för pixel när datan går att få som bytes förstår jag inte poängen med mer än att det är lite enklare att koda. Det kommer bli otroligt ineffektivt med branch instruktioner och grejer för varje pixel.


Upp
 Profil  
 
InläggPostat: 21.50 2017-06-25 
Användarvisningsbild

Blev medlem: 14.52 2005-01-10
Inlägg: 24148
Ort: Aabenraa, Danmark
Att skicka ut 160 bytes 2 bit åt gången med bit-bang tar inte lång tid, jag är mycket tveksam på att det går snabbare med en ISR som ska servas då själva overheaden för en interrupt ska räknas med.

Jag har testat lite olika sätt att sätta upp bildminnet och skicka det och vad jag beskrev tidigare gav bäst resultat.

Samtidig kan jag placera text var jag vill utan att ha det på fasta positioner.


Upp
 Profil  
 
InläggPostat: 21.22 2017-06-27 

Blev medlem: 01.58 2011-03-03
Inlägg: 120
Efter mer kodande och lite trixande med felsökning har jag nu gjort grundläggande utskriftsrutiner. De kan skriva ut tecken var som helst på och ha en varierande bredd, tecken lagras i en tvådimensionell array som även lagrar teckenbredden i antal pixlar. För att kunna dela upp tecknen över flera 8*8 paneler så begränsas varje tecken till 8 pixlar brett. Gjorde min pekarmagi mycket enklare. För större "tecken" eller bilder måste man helt enkelt lagra flera man skriver ut bara.

Nästa steg blir att montera ihop en större display (experimenterat med 2 panel brett, 8*16 pixlar) och avlusa eventuella fel i koden jag inte hittat. Samt snygga upp den så jag kan lägga upp den här.

Eftersom hårvaran (det kortet jag etsat) inte har fler tillgängliga pinnar från Arduinon är det inte aktuellt att bitbanga 2 bitar i taget. Just nu har jag dragit ut för SPI-hårdvarans CLK, DATA och en pinne som latch/strobe. Då ser jag ingen anledning att inte låta hårdvaran klocka ut en byte istället för att bitbanga. Jag måste ju ändå p.g.a. mitt kretskort seriekoppla övre och undre halvan av displayen (endast en DATA tillgänglig).

Däremot är det möjligt att ISR overheaden käka upp allt positivt med att ha den och lite till (Interrupt 1 gång per byte). Ska när displayen är klar och stor jämföra för skoj skull och se hur mycket fortare det går att göra det i en rutin istället. Skillnaden i koden är ju nästintill ingen. Ska bara se till att koda den så att den förbereder nästa byte medans hårdvaran klockar ut en byte.


Upp
 Profil  
 
InläggPostat: 06.04 2017-06-28 
Användarvisningsbild

Blev medlem: 14.52 2005-01-10
Inlägg: 24148
Ort: Aabenraa, Danmark
Ja, finns det bara 1 databit är det ju inte mycket att välja på.
I övrigt kan man shifta in data med upp till 20MHz.

Jag har gjort en teckentabell i 2 dimensioner med en fast bredd om 5 pixlar. Varje tecken innehåller sedan information om mönstret men även bredd och offset så att samma tabell kan användas till fast bredd och proportionell bredd.

Det blir spännande att se slutresultatet av ditt arbete.


Upp
 Profil  
 
InläggPostat: 22.08 2017-07-03 

Blev medlem: 01.58 2011-03-03
Inlägg: 120
Har fått hela displayen att skriva ut tid nu. Dock blir det bara 10 paneler och inte 12, var enklare och det behövs inte mer eftersom jag har variabel teckenbredd ( ":" och "," blir "smala").
Testade för skoj skull att mäta hur lång tid det tog att uppdatera (och beräkna hela displayens bitar) med att använda interrupt och inte för att styra SPI hårdvaran. Tog ca 740 us med interrupt och 530 us utan. Tyckte skillnaden var lite för stor för att vara rimlig (bara interrupt-overheaden som är skillnaden i praktiken). Sen insåg jag hur galet jag mätt. Så får testa om...

Dessutom är varianten utan SPI-interrupt sämre kodad (långsammare) än den behöver vara just nu. Var så irriterad på ett löjligt fel att jag glömda att lägga själva beräkningen som hämtar nästa byte som ska klockas ut medans hårdvaran klockar ut nuvarande. Just nu står processorn bara och väntar medans hårdvaran klockar ut data.

Ful och ännu inte uppsnyggad kod:
Kod: [Expandera/Minimera] [Hämta] (Untitled.txt)
  1. // Tidtagare för "eye of the tiger" hos Chalmers skytteförening
  2. // 2 fällmål
  3. // Skrivet av: Sven "Sten" Åkersten
  4.  
  5.  
  6. // PORTx set output value here if conf as output
  7. // DDRx configure input/output, 0 = input, 1 = output
  8. // PINx port input register, read here if conf as input
  9.  
  10. #include <avr/io.h>
  11. #include <avr/interrupt.h>
  12.  
  13. #include <LiquidCrystal.h>
  14.  
  15. #include <avr/wdt.h>
  16.  
  17. #define STATE_INIT 0b00000000
  18. #define STATE_CLEAR 0b00000111
  19. #define STATE_WAIT_TO_START 0b00000001
  20. #define STATE_RUNNING_BOTH 0b00000110
  21. #define STATE_RUNNING_PL1 0b00000010
  22. #define STATE_RUNNING_PL2 0b00000100
  23.  
  24. #define MAIN_BUTTON (0b00000001)
  25. #define PLAYER_1_BUTTON (0b00000010)
  26. #define PLAYER_2_BUTTON (0b00000100)
  27. #define MAIN_LED (0b00001000)
  28. #define PLAYER_1_LED (0b00010000)
  29. #define PLAYER_2_LED (0b00100000)
  30. #define BUTTON_PORT PINC
  31. #define LED_PORT PORTC
  32.  
  33. #define MAXIMUM_TIME (300000)
  34.  
  35. #define DEBOUNCE_TIME 150 //testat till 150
  36.  
  37. #define EXT_OSC 0b00100000
  38. #define SHIFT_REGISTER DDRB
  39. #define LATCH_REGISTER DDRD
  40. #define SHIFT_PORT PORTB
  41. #define LATCH_PORT PORTD
  42. #define DATA (1<<PB3)           //MOSI (SI)
  43. #define LATCH (1<<PD4)          //
  44. #define CLOCK (1<<PB5)          //SCK  (SCK)
  45.  
  46. #define DISPLAY_SIZE 80
  47. #define CHARACTER_WIDTH 6
  48. #define LINE_CHAR_WIDTH 6
  49. #define COLON_CHAR_WIDTH 3
  50. #define DOT_CHAR_WIDTH 3
  51. #define LINE_CHAR 10
  52. #define COLON_CHAR 11
  53. #define DOT_CHAR 12
  54.  
  55.  
  56. LiquidCrystal lcd(2,3,7,8,9,10);
  57.  
  58. volatile unsigned long int timer_tick_vola = 0;  //timer value from interrupt
  59. volatile unsigned long int last_tick_1_vola = 0; //time at last player 1 trigged from interrupt
  60. volatile unsigned long int last_tick_2_vola = 0; //time at last player 2 trigged from interrupt
  61. volatile unsigned short int holdoff = MAIN_BUTTON; // holdoff for buttons for interrupt
  62.  
  63. unsigned short int system_state = STATE_INIT;    // current system state
  64. unsigned short int debounce_timer_holdoff = 0b00000000; // holdoff for statemachine, timer or internal state
  65. unsigned long int actual_tick_1 = MAXIMUM_TIME;  //last player 1 time, non volatile
  66. unsigned long int actual_tick_2 = MAXIMUM_TIME;  //last player 2 time, non volatile
  67. unsigned long int time_to_display_1 = MAXIMUM_TIME;  //time to display player 1 time, non volatile
  68. unsigned long int time_to_display_2 = MAXIMUM_TIME;  //time to display player 2, non volatile
  69. unsigned long int start_tick_1 = 0;   //start time for player 1, non volatile
  70. unsigned long int start_tick_2 = 0;   //start time for player 2, non volatile
  71. unsigned long int timer_tick = 0;     //timer value copy to use in state machine, non volatile
  72. unsigned long int last_tick_1 = 0;    //last time player 1 copy to use in state machine, non volatile
  73. unsigned long int last_tick_2 = 0;    //last time player 2 copy to use in state machine, non volatile
  74.  
  75. struct time_characters
  76. {
  77.   unsigned short int t1_mm;
  78.   unsigned short int t1_ss_tenth;
  79.   unsigned short int t1_ss_unit;
  80.   unsigned short int t1_cc_tenth;
  81.   unsigned short int t1_cc_unit;
  82.      
  83.   unsigned short int t2_mm;
  84.   unsigned short int t2_ss_tenth;
  85.   unsigned short int t2_ss_unit;
  86.   unsigned short int t2_cc_tenth;
  87.   unsigned short int t2_cc_unit;
  88. } ;
  89.  
  90. const char charTable[13][9] ={{0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,CHARACTER_WIDTH},  // Pixelmap for characters on LED display
  91.                               {0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00,CHARACTER_WIDTH},
  92.                               {0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00,CHARACTER_WIDTH},
  93.                               {0x70,0x88,0x08,0x10,0x08,0x88,0x70,0x00,CHARACTER_WIDTH},
  94.                               {0x10,0x30,0x50,0x90,0xf8,0x10,0x38,0x00,CHARACTER_WIDTH},
  95.                               {0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00,CHARACTER_WIDTH},
  96.                               {0x30,0x40,0x80,0xf0,0x88,0x88,0x70,0x00,CHARACTER_WIDTH},
  97.                               {0xf8,0x08,0x10,0x20,0x40,0x40,0x40,0x00,CHARACTER_WIDTH},
  98.                               {0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,CHARACTER_WIDTH},
  99.                               {0x70,0x88,0x88,0x78,0x08,0x10,0x60,0x00,CHARACTER_WIDTH},
  100.                               {0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,LINE_CHAR_WIDTH},
  101.                               {0x00,0xc0,0xc0,0x00,0xc0,0xc0,0x00,0x00,COLON_CHAR_WIDTH},
  102.                               {0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x00,DOT_CHAR_WIDTH}};
  103.  
  104. char display_shadow[DISPLAY_SIZE/8+1][8]; //LED display shadow
  105.  
  106. void setup()
  107. {
  108.  
  109.     delay(100);
  110.    
  111.     // initialize pinsz
  112.     DDRC = ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // inputs
  113.     DDRC |= (PLAYER_1_LED | PLAYER_2_LED | MAIN_LED); // output
  114.     PORTC = (PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // all inputs with internal pullup
  115.  
  116.     DDRB = 0b11111111; // all output,
  117.  
  118.     //initialize SPI module and pins
  119.     SHIFT_REGISTER |= (DATA | CLOCK);     //Set control pins as outputs
  120.     LATCH_REGISTER = (LATCH | 0b11111111) & ~EXT_OSC ;
  121.    
  122.     SHIFT_PORT &= ~(DATA | CLOCK);                //Set control pins low
  123.     SPCR = (1<<SPE) | (1<<MSTR);  //Start SPI as Master
  124.     //SPSR =  (1<<SPI2X);
  125.  
  126.     //Pull LATCH low (Important: this is necessary to start the SPI transfer!)
  127.     LATCH_PORT &= ~LATCH;
  128.  
  129.     // initialize Timer1
  130.     cli();          // disable global interrupts
  131.     TCCR1A = 0;     // set entire TCCR1A register to 0
  132.     DDRD = 0b11011111;  // T1 as input
  133.     TCCR1B = 0b00001111;     // turn on CTC mode and set external clock
  134.     //TCCR1B = 0b00001001;     // turn on CTC mode and set prescaler as 1
  135.     // set compare match register to desired timer count:
  136.     OCR1A = 7999; // calculated to 7999 for 2 ms
  137.     //OCR1A = 31999; // calculated to 31999 for 2 ms
  138.  
  139.     // enable timer compare interrupt:
  140.     TIMSK1 |= (1 << OCIE1A);
  141.     sei();          // enable global interrupts
  142.  
  143.     wdt_enable(WDTO_4S);
  144.  
  145.     lcd.begin(16,2);
  146.     lcd.print("Skytt 1  Skytt 2");
  147.    
  148.     // Start serial com.
  149.     Serial.begin(9600);
  150.  
  151. }
  152.  
  153. void loop() {
  154.   // statemachine that runs over and over again
  155.   wdt_reset();
  156.  
  157.   if (system_state == STATE_INIT) // inital state
  158.   {
  159.       //print_time_serial(1,actual_tick_1,actual_tick_2);  // display last time
  160.       time_to_display_1 = actual_tick_1;
  161.       time_to_display_2 = actual_tick_2;
  162.  
  163.       LED_PORT |= (PLAYER_1_LED | PLAYER_2_LED);  // turn on pla 1 and 2 led, to wait for them to be pressed
  164.       LED_PORT &= ~MAIN_LED;  // turn off main button led
  165.      
  166.       if(holdoff & PLAYER_1_BUTTON)  // if player 1 is pressed
  167.       {
  168.           debounce_timer_holdoff |= PLAYER_1_BUTTON;
  169.           // clear pla 1 led
  170.           LED_PORT &= ~PLAYER_1_LED;
  171.       }
  172.       if(holdoff & PLAYER_2_BUTTON) // if player 2 is pressed
  173.       {
  174.           debounce_timer_holdoff |= PLAYER_2_BUTTON;
  175.           // clear pla 1 led
  176.           LED_PORT &= ~PLAYER_2_LED;
  177.       }
  178.       if( (debounce_timer_holdoff & PLAYER_1_BUTTON) && (debounce_timer_holdoff & PLAYER_2_BUTTON) ) // if both player have been pressed, go to next state
  179.       {
  180.          debounce_timer_holdoff &= ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON);
  181.          system_state = STATE_CLEAR;
  182.       }    
  183.   }
  184.  
  185.   if(system_state == STATE_CLEAR)
  186.   {
  187.       //print_time_serial(2,actual_tick_1,actual_tick_2);  // display last time
  188.       time_to_display_1 = actual_tick_1;
  189.       time_to_display_2 = actual_tick_2;
  190.  
  191.       holdoff &= ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // clear all buttons
  192.       delay(50);  // wait to see if any button is depressed so it will set it's corresponding holdoff
  193.       system_state = STATE_WAIT_TO_START;   //change state
  194.       debounce_timer_holdoff |= MAIN_BUTTON;   //set main button state machine holdoff
  195.   }
  196.  
  197.   if (system_state == STATE_WAIT_TO_START)
  198.   {  
  199.       if( !( holdoff & (PLAYER_1_BUTTON | PLAYER_2_BUTTON) )  )  // if any player button has been pressed since cleared in last state, return to that state. Makes sure player buttons is low (targets reset)
  200.       {      
  201.           if(holdoff & MAIN_BUTTON & ~debounce_timer_holdoff)  // if statemachine holdoff is cleared and main button been pressed, start time and change state. If main button is clear, clear main button holdoff to enable start else go back to clearing state
  202.           {
  203.               LED_PORT &= ~MAIN_LED; //clear main button led
  204.              
  205.               cli();
  206.               start_tick_1 = last_tick_1_vola;
  207.               start_tick_2 = last_tick_2_vola;
  208.               sei();
  209.              
  210.               system_state = STATE_RUNNING_BOTH;
  211.           }
  212.           else if (~holdoff & MAIN_BUTTON)
  213.           {
  214.                time_to_display_1 = 0;
  215.                time_to_display_2 = 0;
  216.                //print_time_serial(3,0,0);   // print zeros, system ready to start
  217.                LED_PORT |= MAIN_LED;  //turn on main button led        
  218.                debounce_timer_holdoff &= ~MAIN_BUTTON;            
  219.           }
  220.           else
  221.           {
  222.                //print_time_serial(3,actual_tick_1,actual_tick_2);  // display last measured time again, system NOT ready to start
  223.                time_to_display_1 = actual_tick_1;
  224.                time_to_display_2 = actual_tick_2;
  225.                LED_PORT &= ~MAIN_LED; //clear main button led
  226.                system_state = STATE_CLEAR;
  227.           }
  228.       }
  229.       else
  230.       {
  231.            LED_PORT &= ~MAIN_LED; //clear main button led
  232.            system_state = STATE_CLEAR;
  233.       }  
  234.   }
  235.  
  236.   cli();
  237.   timer_tick = timer_tick_vola; // copy current timer tick from interrupt
  238.   sei();
  239.  
  240.   if(system_state == STATE_RUNNING_PL1 || system_state == STATE_RUNNING_BOTH ) // if player time is running, recalculate time and display.
  241.   {
  242.  
  243.       LED_PORT &= ~MAIN_LED;  //clear main button led, turn on pl 1
  244.       LED_PORT |= PLAYER_1_LED;
  245.  
  246.       if( (actual_tick_1 = (timer_tick - start_tick_1)) < DEBOUNCE_TIME) // if time since start less then minimum time, set mimimum time
  247.       {
  248.           actual_tick_1 = 0;
  249.       }    
  250.       else // if time since start greater then minimum time
  251.       {    
  252.            time_to_display_1 = actual_tick_1 - DEBOUNCE_TIME;
  253.           //print_time_serial(41,actual_tick_1,actual_tick_2); //display time pla 1
  254.       }
  255.      
  256.       if(holdoff & PLAYER_1_BUTTON) // if player 1 holdoff
  257.       {
  258.           cli();
  259.           last_tick_1 = last_tick_1_vola;
  260.           sei();
  261.          
  262.           if( timer_tick - last_tick_1 > DEBOUNCE_TIME ) // Wait until debounce time have passed
  263.           {
  264.               if( ~BUTTON_PORT & PLAYER_1_BUTTON) // if button is still pressed, Calculate finish time and clear running state of that player, when both player been clear state return to init
  265.               {
  266.                   LED_PORT &= ~PLAYER_1_LED; //clear pla 1 led
  267.                   debounce_timer_holdoff |=  MAIN_BUTTON;
  268.                   time_to_display_1 = actual_tick_1 = last_tick_1 - start_tick_1;
  269.                   system_state &= ~STATE_RUNNING_PL1;
  270.               }
  271.               else // else it was a button bounce, clear holdoff and do nothing
  272.               {
  273.                   holdoff &= ~PLAYER_1_BUTTON;
  274.               }
  275.           }  
  276.       }
  277.       else if(actual_tick_1 > MAXIMUM_TIME) // if time since start greater then maximum time and button not been pressed, terminate
  278.       {  
  279.           LED_PORT &= ~PLAYER_1_LED; //clear pla 1 led
  280.           debounce_timer_holdoff |= MAIN_BUTTON;
  281.           time_to_display_1 = actual_tick_1 = MAXIMUM_TIME;
  282.           system_state &= ~STATE_RUNNING_PL1;
  283.       }    
  284.   }
  285.  
  286.   if(system_state == STATE_RUNNING_PL2 || system_state == STATE_RUNNING_BOTH ) // if player time is running, recalculate time and display.
  287.   {
  288.  
  289.       LED_PORT &= ~MAIN_LED;  //clear main button led, turn on pl 2
  290.       LED_PORT |= PLAYER_2_LED;
  291.  
  292.       if( (actual_tick_2 = (timer_tick - start_tick_2)) < DEBOUNCE_TIME) // if time since start less then minimum time, set mimimum time
  293.       {
  294.           time_to_display_2 = 0;
  295.       }    
  296.       else // if time since start greater then minimum time
  297.       {    
  298.           //print_time_serial(42,actual_tick_1,actual_tick_2); //display time pl 2
  299.           time_to_display_2 = actual_tick_2 - DEBOUNCE_TIME;
  300.       }
  301.  
  302.       if(holdoff & PLAYER_2_BUTTON) // if player 2 holdoff
  303.       {
  304.           cli();
  305.           last_tick_2 = last_tick_2_vola;
  306.           sei();
  307.          
  308.           if( timer_tick - last_tick_2 > DEBOUNCE_TIME ) // Wait until debounce time have passed
  309.           {
  310.               if( ~BUTTON_PORT & PLAYER_2_BUTTON) // if button is still pressed, Calculate finish time and clear running state of that player, when both player been clear state return to init
  311.               {
  312.                   LED_PORT &= ~PLAYER_2_LED; //clear pla 2 led
  313.                   debounce_timer_holdoff |=  MAIN_BUTTON;
  314.                   time_to_display_2 = actual_tick_2 = last_tick_2 - start_tick_2;
  315.                   system_state &= ~STATE_RUNNING_PL2;
  316.               }
  317.               else // else it was a button bounce, clear holdoff and do nothing
  318.               {
  319.                   holdoff &= ~PLAYER_2_BUTTON;
  320.               }
  321.           }  
  322.       }
  323.       else if(actual_tick_2 > MAXIMUM_TIME) // if time since start greater then maximum time and button not been pressed, terminate
  324.       {  
  325.           LED_PORT &= ~PLAYER_2_LED; //clear pla 2 led
  326.           debounce_timer_holdoff |= MAIN_BUTTON;
  327.           time_to_display_2 = actual_tick_2 = MAXIMUM_TIME;
  328.           system_state &= ~STATE_RUNNING_PL2;
  329.       }    
  330.   }
  331.  
  332.   print_time(time_to_display_1,time_to_display_2);
  333.   //print_time_serial(1,time_to_display_1,time_to_display_2);
  334.  
  335. }
  336.  
  337.  
  338. void output_byte_spi(unsigned short data)
  339. {
  340.     //Shift in some data
  341.     SPDR = data;
  342.     //Wait for SPI process to finish
  343.     while(!(SPSR & (1<<SPIF))) ;
  344.    
  345.     //Latch into output latch
  346.     LATCH_PORT |= LATCH;
  347.     delayMicroseconds(1);
  348.     LATCH_PORT &= ~LATCH;
  349.     delayMicroseconds(1);
  350. }
  351.  
  352. void send_message_spi(char* data,unsigned short message_length)
  353. {
  354.     for(int i = 0; i < message_length ; i++)
  355.     {  
  356.         //Shift in some data
  357.         SPDR = data[i];
  358.         //Wait for SPI process to finish
  359.         while(!(SPSR & (1<<SPIF))) ;
  360.     }
  361.    
  362.     //Latch into output latch
  363.     LATCH_PORT |= LATCH;
  364.     delayMicroseconds(1);
  365.     LATCH_PORT &= ~LATCH;
  366.     delayMicroseconds(1);
  367.  
  368. }
  369.  
  370. void print_time(unsigned long time1, unsigned long time2)
  371. {
  372.     unsigned int t1_mm = time1/30000;
  373.     unsigned int t2_mm = time2/30000;;
  374.     unsigned int t1_ss = (time1/500)%60;
  375.     unsigned int t2_ss = (time2/500)%60;
  376.     unsigned short int t1_cc = (time1/5)%100;
  377.     unsigned short int t2_cc = (time2/5)%100;
  378.     char lcd_char_time[16] ={'-',':','-','-','.','-','-',' ',' ','-',':','-','-','.','-','-'};
  379.    
  380.     time_characters led_char_time = {LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR};
  381.  
  382.     if(time1 != MAXIMUM_TIME)
  383.     {
  384.      
  385.       lcd_char_time[3] = (led_char_time.t1_ss_unit = t1_ss%10) + 48;
  386.       lcd_char_time[2] = (led_char_time.t1_ss_tenth = (t1_ss/10)%10) + 48;
  387.       lcd_char_time[0] = (led_char_time.t1_mm = t1_mm) + 48;
  388.       lcd_char_time[6] = (led_char_time.t1_cc_unit = t1_cc%10) + 48;
  389.       lcd_char_time[5] = (led_char_time.t1_cc_tenth = (t1_cc/10)%10) + 48;
  390.     }
  391.     if(time2 != MAXIMUM_TIME)
  392.     {
  393.       lcd_char_time[12] = (led_char_time.t2_ss_unit = t2_ss%10) + 48;
  394.       lcd_char_time[11] = (led_char_time.t2_ss_tenth = (t2_ss/10)%10) + 48;
  395.       lcd_char_time[9] = (led_char_time.t2_mm = t2_mm) + 48;
  396.       lcd_char_time[15] = (led_char_time.t2_cc_unit = t2_cc%10) + 48;
  397.       lcd_char_time[14] = (led_char_time.t2_cc_tenth = (t2_cc/10)%10) + 48;
  398.     }
  399.     lcd.setCursor(0, 1);
  400.  
  401.     for(unsigned short int i = 0; i < 16 ; i++)
  402.         lcd.print(lcd_char_time[i]);
  403.  
  404.    unsigned short int col = 0;
  405.      
  406.    clear_all_display_shadow();   //Clear displayshodow memory
  407.  
  408.    //Fungerar denna på full storlek?+?
  409.    col +=2; // two blank pixel coloumns    
  410.    col = write_character_to_display_shadow(charTable[led_char_time.t1_mm],col);
  411.    col = write_character_to_display_shadow(charTable[COLON_CHAR],col);
  412.    col = write_character_to_display_shadow(charTable[led_char_time.t1_ss_tenth],col);
  413.    col = write_character_to_display_shadow(charTable[led_char_time.t1_ss_unit],col);
  414.    col = write_character_to_display_shadow(charTable[DOT_CHAR],col);
  415.    col = write_character_to_display_shadow(charTable[led_char_time.t1_cc_tenth],col);
  416.    col = write_character_to_display_shadow(charTable[led_char_time.t1_cc_unit],col);
  417.  
  418.    col += 5; // pixel columns in between the two times
  419.    col = write_character_to_display_shadow(charTable[led_char_time.t2_mm],col);
  420.    col = write_character_to_display_shadow(charTable[COLON_CHAR],col);
  421.    col = write_character_to_display_shadow(charTable[led_char_time.t2_ss_tenth],col);
  422.    col = write_character_to_display_shadow(charTable[led_char_time.t2_ss_unit],col);
  423.    col = write_character_to_display_shadow(charTable[DOT_CHAR],col);
  424.    col = write_character_to_display_shadow(charTable[led_char_time.t2_cc_tenth],col);
  425.    col = write_character_to_display_shadow(charTable[led_char_time.t2_cc_unit],col);
  426.  
  427.    update_display();  // send display shadow to LED screen
  428.      
  429.    //LATCH_PORT &= ~LATCH;
  430.  
  431.    delay(10);
  432.    //   SPCR |= (1<<SPIE); // SPI interrupt enable
  433.      
  434. }
  435.  
  436.  
  437. unsigned short int write_character_to_display_shadow(char* data, unsigned short col)
  438. {
  439.   unsigned int tmp;
  440.   unsigned short int i;
  441.   //uint16_t tmp[8];
  442.   for(i = 0; i < 8 ; i++) // for all rows, do pointer magic so it the character will be split between two bytes.  
  443.   {
  444.     *((char *)&tmp+1) = data[i];
  445.     *((char *)&tmp) = 0;
  446.     tmp = tmp>>col%8;
  447.  
  448.     display_shadow[col/8][i] |= *(((char *)&tmp)+1);
  449.     display_shadow[(col/8)+1][i] = *((char *)&tmp);  
  450.   }
  451.   return col+data[i]; // add the character width
  452. }
  453.  
  454. void clear_rest_display_shadow(unsigned short col)
  455. {
  456.   for(unsigned short panel = col/8 + 1 ; panel < DISPLAY_SIZE/8 ; panel++)
  457.   {
  458.     for(unsigned short i = 0; i < 8 ; i++)  
  459.     {
  460.       display_shadow[panel][i] = 0;  
  461.     }
  462.   }
  463. }
  464.  
  465. void clear_all_display_shadow(void)
  466. {
  467.   for(unsigned short panel = 0 ; panel < DISPLAY_SIZE/8 ; panel++)
  468.   {
  469.     for(unsigned short i = 0; i < 8 ; i++)  
  470.     {
  471.       display_shadow[panel][i] = 0;  
  472.     }
  473.   }
  474. }
  475.  
  476. void update_display(void) // fixa så att själva beräkningen för nästa utklockning sker medans hårdavaran sköter nuvarande. D.v.s. lägg till en en bytes temp buffer...
  477. {
  478.     SPDR = (display_shadow[0][0] & 0xF0) | (0x0F & display_shadow[0][1]>>4);
  479.    
  480.     for(unsigned short int i = 1; i < DISPLAY_SIZE ; i++)
  481.     {  
  482.        
  483.         while(!(SPSR & (1<<SPIF))) ; //Wait for SPI process to finish
  484.  
  485.         unsigned short int row = (i&1)<<1;
  486.         unsigned short int panel = i>>2;
  487.         if(i >= (DISPLAY_SIZE/2))
  488.         {
  489.           row = row+4;
  490.           panel -= (DISPLAY_SIZE/8);
  491.         }
  492.    
  493.         if(i & 0b00000010)
  494.           SPDR = (display_shadow[panel][row+1] & 0x0F) | (0xF0 & display_shadow[panel][row]<<4);
  495.         else
  496.           SPDR = (display_shadow[panel][row] & 0xF0) | (0x0F & display_shadow[panel][row+1]>>4);        
  497.     }
  498.  
  499.   while(!(SPSR & (1<<SPIF))) ; //Wait for SPI process to finish
  500.   LATCH_PORT |= LATCH; //Latch into output latch
  501.   delayMicroseconds(1);
  502.   LATCH_PORT &= ~LATCH;
  503. }
  504.  
  505.  
  506. void clear_first_panel_display_shadow(void)
  507. {
  508.   for(unsigned short i = 0; i < 8 ; i++)  
  509.   {
  510.     display_shadow[0][i] = 0;  
  511.   }
  512. }
  513.  
  514.  
  515. void print_time_serial(unsigned short state, unsigned long time1, unsigned long time2) //debug printing of current state, volatile holdoff and the last measured time for each player
  516. {
  517.     unsigned int t1_ss = time1/500;
  518.     unsigned int t2_ss = time2/500;
  519.     unsigned short int t1_cc = (time1/5)%100;
  520.     unsigned short int t2_cc = (time2/5)%100;
  521.     // calculate time as seconds and hundreds of seconds before sending on serial port
  522.     Serial.print("State: ");
  523.     Serial.print(state);
  524.     Serial.print(" Holdoff: ");
  525.     Serial.print(holdoff);
  526.     Serial.print(" Time 1: ");
  527.     Serial.print(t1_ss);
  528.     Serial.print(".");
  529.     if(t1_cc < 10)
  530.     {
  531.         Serial.print("0");
  532.     }
  533.     Serial.print(t1_cc);
  534.     Serial.print(" Time 1: ");
  535.     Serial.print(t2_ss);
  536.     Serial.print(".");
  537.     if(t2_cc < 10)
  538.     {
  539.         Serial.print("0");
  540.     }
  541.     Serial.println(t2_cc);
  542. }
  543.    
  544.  
  545. ISR(TIMER1_COMPA_vect) // interrupt runs once for every timer tick. If button holdoff is clear and button is pressed, store current time in volatile variable of corresponding player
  546. {  
  547.     unsigned short int new_button_state;
  548.    
  549.     timer_tick_vola++;  //increment main timer, one tick (2 ms) have passed
  550.    
  551.     new_button_state = (~BUTTON_PORT & ~holdoff) & (MAIN_BUTTON | PLAYER_1_BUTTON | PLAYER_2_BUTTON);  // if a button is pressed and holdoff is clear set high button state, buttons is active low
  552.  
  553.     if(MAIN_BUTTON & new_button_state) // store both player time if start (main) button is pressed. The start time ie.
  554.     {
  555.         holdoff |= MAIN_BUTTON;
  556.         last_tick_1_vola = timer_tick_vola;
  557.         last_tick_2_vola = timer_tick_vola;        
  558.     }
  559.  
  560.     if(PLAYER_1_BUTTON & new_button_state)  // store player time if player 1 button is pressed. The finish time for that player ie.
  561.     {
  562.         holdoff |= PLAYER_1_BUTTON;
  563.         last_tick_1_vola = timer_tick_vola;        
  564.     }
  565.  
  566.     if(PLAYER_2_BUTTON & new_button_state) // store player time if player 2 button is pressed. The finish time for that player ie.
  567.     {
  568.         holdoff |= PLAYER_2_BUTTON;
  569.         last_tick_2_vola = timer_tick_vola;        
  570.     }
  571. }
  572.  


Logga in för att visa de filer som bifogats till detta inlägg.


Upp
 Profil  
 
InläggPostat: 10.22 2017-07-04 
Användarvisningsbild

Blev medlem: 22.56 2008-11-27
Inlägg: 3317
Ort: Utanför Jönköping
Snyggt!!


Upp
 Profil  
 
InläggPostat: 10.52 2017-07-04 
Användarvisningsbild

Blev medlem: 14.52 2005-01-10
Inlägg: 24148
Ort: Aabenraa, Danmark
Ser bra ut.

Kul att se att det går framåt.


Upp
 Profil  
 
InläggPostat: 23.39 2017-07-09 

Blev medlem: 01.58 2011-03-03
Inlägg: 120
I helgen har projektet gått framåt en bit. Displayen är nu monterad till en enhet med hjälp av två långa plywood-remsor. Ännu finns inga väggfästen dock.

Strömkablar är fixade och anpassade men jag har inte löst strömförsörjningsfrågan ännu. Moddade ett datornätagg och drog ut bara 12V lina som kunde leverera 12A. När jag drog mellan 3-4 A och körde displayen på det så hörde jag ett högt vinande ljud, lät som om switchdelen ballade ur. Sen dog datornätagget, antagligen termiskt skydd som löste ut... Aja, gillade kanske inte att man drog så mycket ström från 12V men inget från nån av de andra linorna. Skulle behöva nåt är billigt och kan leverera ordentligt med ström. I praktiken drar den ca 4A när man visar tid, men tänder man alla pixlarna så drar displayen nästan 15A.

Mjukvaran är uppdaterad med en lite snyggare utskrift och sen har jag snabbat upp utklockningen av data en del. Jag sänkte SPI-enhetens klockhastighet till hälften utan att påverka den totala tiden för utklockning nämnvärt. Helt enkelt att eftersom hårdvaran sköter utklockningen så gör jag en del av beräkningen av nästa byte som ska ut.
Nu tar det ca 400 us, att klocka ut all data (i jämförelse med över 700 us för samma uppgift med SPI-interrupt). Det tar ca 5 ms att köra ett helt "varv", d.v.s. kolla om någon spelare stannat, omräkning av aktuell tid samt beräkning av displayer och utskrift på båda displayerna.

Koden i dess nuvarande form:
Kod: [Expandera/Minimera] [Hämta] (Untitled.txt)
  1.  
  2. // Tidtagare för "eye of the tiger" hos Chalmers skytteförening
  3. // 2 fällmål
  4. // Skrivet av: Sven "Sten" Åkersten
  5.  
  6.  
  7. // PORTx set output value here if conf as output
  8. // DDRx configure input/output, 0 = input, 1 = output
  9. // PINx port input register, read here if conf as input
  10.  
  11. #include <avr/io.h>
  12. #include <avr/interrupt.h>
  13.  
  14. #include <LiquidCrystal.h>
  15.  
  16. #include <avr/wdt.h>
  17.  
  18. #define STATE_INIT 0b00000000
  19. #define STATE_CLEAR 0b00000111
  20. #define STATE_WAIT_TO_START 0b00000001
  21. #define STATE_RUNNING_BOTH 0b00000110
  22. #define STATE_RUNNING_PL1 0b00000010
  23. #define STATE_RUNNING_PL2 0b00000100
  24.  
  25. #define MAIN_BUTTON (0b00000001)
  26. #define PLAYER_1_BUTTON (0b00000010)
  27. #define PLAYER_2_BUTTON (0b00000100)
  28. #define MAIN_LED (0b00001000)
  29. #define PLAYER_1_LED (0b00010000)
  30. #define PLAYER_2_LED (0b00100000)
  31. #define BUTTON_PORT PINC
  32. #define LED_PORT PORTC
  33.  
  34. #define MAXIMUM_TIME (300000)
  35.  
  36. #define DEBOUNCE_TIME 150 //testat till 150
  37.  
  38. #define EXT_OSC 0b00100000
  39. #define SHIFT_REGISTER DDRB
  40. #define LATCH_REGISTER DDRD
  41. #define SHIFT_PORT PORTB
  42. #define LATCH_PORT PORTD
  43. #define DATA (1<<PB3)           //MOSI (SI)
  44. #define LATCH (1<<PD4)          //
  45. #define CLOCK (1<<PB5)          //SCK  (SCK)
  46.  
  47. #define DISPLAY_SIZE 80
  48. #define CHARACTER_WIDTH 6
  49. #define LINE_CHAR_WIDTH 6
  50. #define COLON_CHAR_WIDTH 2
  51. #define DOT_CHAR_WIDTH 2
  52. #define LINE_CHAR 10
  53. #define COLON_CHAR 11
  54. #define DOT_CHAR 12
  55.  
  56.  
  57. LiquidCrystal lcd(2,3,7,8,9,10);
  58.  
  59. volatile unsigned long int timer_tick_vola = 0;  //timer value from interrupt
  60. volatile unsigned long int last_tick_1_vola = 0; //time at last player 1 trigged from interrupt
  61. volatile unsigned long int last_tick_2_vola = 0; //time at last player 2 trigged from interrupt
  62. volatile unsigned short int holdoff = MAIN_BUTTON; // holdoff for buttons for interrupt
  63.  
  64. unsigned short int system_state = STATE_INIT;    // current system state
  65. unsigned short int debounce_timer_holdoff = 0b00000000; // holdoff for statemachine, timer or internal state
  66. unsigned long int actual_tick_1 = MAXIMUM_TIME;  //last player 1 time, non volatile
  67. unsigned long int actual_tick_2 = MAXIMUM_TIME;  //last player 2 time, non volatile
  68. unsigned long int time_to_display_1 = MAXIMUM_TIME;  //time to display player 1 time, non volatile
  69. unsigned long int time_to_display_2 = MAXIMUM_TIME;  //time to display player 2, non volatile
  70. unsigned long int start_tick_1 = 0;   //start time for player 1, non volatile
  71. unsigned long int start_tick_2 = 0;   //start time for player 2, non volatile
  72. unsigned long int timer_tick = 0;     //timer value copy to use in state machine, non volatile
  73. unsigned long int last_tick_1 = 0;    //last time player 1 copy to use in state machine, non volatile
  74. unsigned long int last_tick_2 = 0;    //last time player 2 copy to use in state machine, non volatile
  75.  
  76. struct time_characters
  77. {
  78.   unsigned short int t1_mm;
  79.   unsigned short int t1_ss_tenth;
  80.   unsigned short int t1_ss_unit;
  81.   unsigned short int t1_cc_tenth;
  82.   unsigned short int t1_cc_unit;
  83.      
  84.   unsigned short int t2_mm;
  85.   unsigned short int t2_ss_tenth;
  86.   unsigned short int t2_ss_unit;
  87.   unsigned short int t2_cc_tenth;
  88.   unsigned short int t2_cc_unit;
  89. } ;
  90.  
  91. const char charTable[13][9] ={{0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,CHARACTER_WIDTH},  // Pixelmap for characters on LED display
  92.                               {0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00,CHARACTER_WIDTH},
  93.                               {0x70,0x88,0x08,0x10,0x20,0x40,0xf8,0x00,CHARACTER_WIDTH},
  94.                               {0x70,0x88,0x08,0x10,0x08,0x88,0x70,0x00,CHARACTER_WIDTH},
  95.                               {0x10,0x30,0x50,0x90,0xf8,0x10,0x38,0x00,CHARACTER_WIDTH},
  96.                               {0xf8,0x80,0xf0,0x08,0x08,0x88,0x70,0x00,CHARACTER_WIDTH},
  97.                               {0x30,0x40,0x80,0xf0,0x88,0x88,0x70,0x00,CHARACTER_WIDTH},
  98.                               {0xf8,0x08,0x10,0x20,0x40,0x40,0x40,0x00,CHARACTER_WIDTH},
  99.                               {0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,CHARACTER_WIDTH},
  100.                               {0x70,0x88,0x88,0x78,0x08,0x10,0x60,0x00,CHARACTER_WIDTH},
  101.                               {0x00,0x00,0x00,0xf8,0x00,0x00,0x00,0x00,LINE_CHAR_WIDTH},
  102.                               {0x00,0x80,0x80,0x00,0x80,0x80,0x00,0x00,COLON_CHAR_WIDTH},
  103.                               {0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,DOT_CHAR_WIDTH}};
  104.  
  105. char display_shadow[DISPLAY_SIZE/8+1][8]; //LED display shadow
  106.  
  107. void setup()
  108. {
  109.  
  110.     delay(100);
  111.    
  112.     // initialize pinsz
  113.     DDRC = ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // inputs
  114.     DDRC |= (PLAYER_1_LED | PLAYER_2_LED | MAIN_LED); // output
  115.     PORTC = (PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // all inputs with internal pullup
  116.  
  117.     DDRB = 0b11111111; // all output,
  118.  
  119.     //initialize SPI module and pins
  120.     SHIFT_REGISTER |= (DATA | CLOCK);     //Set control pins as outputs
  121.     LATCH_REGISTER = (LATCH | 0b11111111) & ~EXT_OSC ;
  122.    
  123.     SHIFT_PORT &= ~(DATA | CLOCK);                //Set control pins low
  124.     SPCR = (1<<SPE) | (1<<MSTR)| (1<<SPR0);  //Start SPI as Master
  125.     SPSR =  (1<<SPI2X);
  126.  
  127.     //Pull LATCH low (Important: this is necessary to start the SPI transfer!)
  128.     LATCH_PORT &= ~LATCH;
  129.  
  130.     // initialize Timer1
  131.     cli();          // disable global interrupts
  132.     TCCR1A = 0;     // set entire TCCR1A register to 0
  133.     DDRD = 0b11011111;  // T1 as input
  134.     TCCR1B = 0b00001111;     // turn on CTC mode and set external clock
  135.     //TCCR1B = 0b00001001;     // turn on CTC mode and set prescaler as 1
  136.     // set compare match register to desired timer count:
  137.     OCR1A = 7999; // calculated to 7999 for 2 ms
  138.     //OCR1A = 31999; // calculated to 31999 for 2 ms
  139.  
  140.     // enable timer compare interrupt:
  141.     TIMSK1 |= (1 << OCIE1A);
  142.     sei();          // enable global interrupts
  143.  
  144.     wdt_enable(WDTO_4S);
  145.  
  146.     lcd.begin(16,2);
  147.     lcd.print("Skytt 1  Skytt 2");
  148.    
  149.     // Start serial com.
  150.     Serial.begin(9600);
  151.  
  152. }
  153.  
  154. void loop() {
  155.   // statemachine that runs over and over again
  156.   wdt_reset();
  157.  
  158.   if (system_state == STATE_INIT) // inital state
  159.   {
  160.       //print_time_serial(1,actual_tick_1,actual_tick_2);  // display last time
  161.       time_to_display_1 = actual_tick_1;
  162.       time_to_display_2 = actual_tick_2;
  163.  
  164.       LED_PORT |= (PLAYER_1_LED | PLAYER_2_LED);  // turn on pla 1 and 2 led, to wait for them to be pressed
  165.       LED_PORT &= ~MAIN_LED;  // turn off main button led
  166.      
  167.       if(holdoff & PLAYER_1_BUTTON)  // if player 1 is pressed
  168.       {
  169.           debounce_timer_holdoff |= PLAYER_1_BUTTON;
  170.           // clear pla 1 led
  171.           LED_PORT &= ~PLAYER_1_LED;
  172.       }
  173.       if(holdoff & PLAYER_2_BUTTON) // if player 2 is pressed
  174.       {
  175.           debounce_timer_holdoff |= PLAYER_2_BUTTON;
  176.           // clear pla 1 led
  177.           LED_PORT &= ~PLAYER_2_LED;
  178.       }
  179.       if( (debounce_timer_holdoff & PLAYER_1_BUTTON) && (debounce_timer_holdoff & PLAYER_2_BUTTON) ) // if both player have been pressed, go to next state
  180.       {
  181.          debounce_timer_holdoff &= ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON);
  182.          system_state = STATE_CLEAR;
  183.       }    
  184.   }
  185.  
  186.   if(system_state == STATE_CLEAR)
  187.   {
  188.       //print_time_serial(2,actual_tick_1,actual_tick_2);  // display last time
  189.       time_to_display_1 = actual_tick_1;
  190.       time_to_display_2 = actual_tick_2;
  191.  
  192.       holdoff &= ~(PLAYER_1_BUTTON | PLAYER_2_BUTTON | MAIN_BUTTON); // clear all buttons
  193.       delay(50);  // wait to see if any button is depressed so it will set it's corresponding holdoff
  194.       system_state = STATE_WAIT_TO_START;   //change state
  195.       debounce_timer_holdoff |= MAIN_BUTTON;   //set main button state machine holdoff
  196.   }
  197.  
  198.   if (system_state == STATE_WAIT_TO_START)
  199.   {  
  200.       if( !( holdoff & (PLAYER_1_BUTTON | PLAYER_2_BUTTON) )  )  // if any player button has been pressed since cleared in last state, return to that state. Makes sure player buttons is low (targets reset)
  201.       {      
  202.           if(holdoff & MAIN_BUTTON & ~debounce_timer_holdoff)  // if statemachine holdoff is cleared and main button been pressed, start time and change state. If main button is clear, clear main button holdoff to enable start else go back to clearing state
  203.           {
  204.               LED_PORT &= ~MAIN_LED; //clear main button led
  205.              
  206.               cli();
  207.               start_tick_1 = last_tick_1_vola;
  208.               start_tick_2 = last_tick_2_vola;
  209.               sei();
  210.              
  211.               system_state = STATE_RUNNING_BOTH;
  212.           }
  213.           else if (~holdoff & MAIN_BUTTON)
  214.           {
  215.                time_to_display_1 = 0;
  216.                time_to_display_2 = 0;
  217.                //print_time_serial(3,0,0);   // print zeros, system ready to start
  218.                LED_PORT |= MAIN_LED;  //turn on main button led        
  219.                debounce_timer_holdoff &= ~MAIN_BUTTON;            
  220.           }
  221.           else
  222.           {
  223.                //print_time_serial(3,actual_tick_1,actual_tick_2);  // display last measured time again, system NOT ready to start
  224.                time_to_display_1 = actual_tick_1;
  225.                time_to_display_2 = actual_tick_2;
  226.                LED_PORT &= ~MAIN_LED; //clear main button led
  227.                system_state = STATE_CLEAR;
  228.           }
  229.       }
  230.       else
  231.       {
  232.            LED_PORT &= ~MAIN_LED; //clear main button led
  233.            system_state = STATE_CLEAR;
  234.       }  
  235.   }
  236.  
  237.   cli();
  238.   timer_tick = timer_tick_vola; // copy current timer tick from interrupt
  239.   sei();
  240.  
  241.   if(system_state == STATE_RUNNING_PL1 || system_state == STATE_RUNNING_BOTH ) // if player time is running, recalculate time and display.
  242.   {
  243.  
  244.       LED_PORT &= ~MAIN_LED;  //clear main button led, turn on pl 1
  245.       LED_PORT |= PLAYER_1_LED;
  246.  
  247.       if( (actual_tick_1 = (timer_tick - start_tick_1)) < DEBOUNCE_TIME) // if time since start less then minimum time, set mimimum time
  248.       {
  249.           actual_tick_1 = 0;
  250.       }    
  251.       else // if time since start greater then minimum time
  252.       {    
  253.            time_to_display_1 = actual_tick_1 - DEBOUNCE_TIME;
  254.           //print_time_serial(41,actual_tick_1,actual_tick_2); //display time pla 1
  255.       }
  256.      
  257.       if(holdoff & PLAYER_1_BUTTON) // if player 1 holdoff
  258.       {
  259.           cli();
  260.           last_tick_1 = last_tick_1_vola;
  261.           sei();
  262.          
  263.           if( timer_tick - last_tick_1 > DEBOUNCE_TIME ) // Wait until debounce time have passed
  264.           {
  265.               if( ~BUTTON_PORT & PLAYER_1_BUTTON) // if button is still pressed, Calculate finish time and clear running state of that player, when both player been clear state return to init
  266.               {
  267.                   LED_PORT &= ~PLAYER_1_LED; //clear pla 1 led
  268.                   debounce_timer_holdoff |=  MAIN_BUTTON;
  269.                   time_to_display_1 = actual_tick_1 = last_tick_1 - start_tick_1;
  270.                   system_state &= ~STATE_RUNNING_PL1;
  271.               }
  272.               else // else it was a button bounce, clear holdoff and do nothing
  273.               {
  274.                   holdoff &= ~PLAYER_1_BUTTON;
  275.               }
  276.           }  
  277.       }
  278.       else if(actual_tick_1 > MAXIMUM_TIME) // if time since start greater then maximum time and button not been pressed, terminate
  279.       {  
  280.           LED_PORT &= ~PLAYER_1_LED; //clear pla 1 led
  281.           debounce_timer_holdoff |= MAIN_BUTTON;
  282.           time_to_display_1 = actual_tick_1 = MAXIMUM_TIME;
  283.           system_state &= ~STATE_RUNNING_PL1;
  284.       }    
  285.   }
  286.  
  287.   if(system_state == STATE_RUNNING_PL2 || system_state == STATE_RUNNING_BOTH ) // if player time is running, recalculate time and display.
  288.   {
  289.  
  290.       LED_PORT &= ~MAIN_LED;  //clear main button led, turn on pl 2
  291.       LED_PORT |= PLAYER_2_LED;
  292.  
  293.       if( (actual_tick_2 = (timer_tick - start_tick_2)) < DEBOUNCE_TIME) // if time since start less then minimum time, set mimimum time
  294.       {
  295.           time_to_display_2 = 0;
  296.       }    
  297.       else // if time since start greater then minimum time
  298.       {    
  299.           //print_time_serial(42,actual_tick_1,actual_tick_2); //display time pl 2
  300.           time_to_display_2 = actual_tick_2 - DEBOUNCE_TIME;
  301.       }
  302.  
  303.       if(holdoff & PLAYER_2_BUTTON) // if player 2 holdoff
  304.       {
  305.           cli();
  306.           last_tick_2 = last_tick_2_vola;
  307.           sei();
  308.          
  309.           if( timer_tick - last_tick_2 > DEBOUNCE_TIME ) // Wait until debounce time have passed
  310.           {
  311.               if( ~BUTTON_PORT & PLAYER_2_BUTTON) // if button is still pressed, Calculate finish time and clear running state of that player, when both player been clear state return to init
  312.               {
  313.                   LED_PORT &= ~PLAYER_2_LED; //clear pla 2 led
  314.                   debounce_timer_holdoff |=  MAIN_BUTTON;
  315.                   time_to_display_2 = actual_tick_2 = last_tick_2 - start_tick_2;
  316.                   system_state &= ~STATE_RUNNING_PL2;
  317.               }
  318.               else // else it was a button bounce, clear holdoff and do nothing
  319.               {
  320.                   holdoff &= ~PLAYER_2_BUTTON;
  321.               }
  322.           }  
  323.       }
  324.       else if(actual_tick_2 > MAXIMUM_TIME) // if time since start greater then maximum time and button not been pressed, terminate
  325.       {  
  326.           LED_PORT &= ~PLAYER_2_LED; //clear pla 2 led
  327.           debounce_timer_holdoff |= MAIN_BUTTON;
  328.           time_to_display_2 = actual_tick_2 = MAXIMUM_TIME;
  329.           system_state &= ~STATE_RUNNING_PL2;
  330.       }    
  331.   }
  332.  
  333.   print_time(time_to_display_1,time_to_display_2);
  334.   //print_time_serial(1,time_to_display_1,time_to_display_2);
  335.  
  336. }
  337.  
  338.  
  339. void output_byte_spi(unsigned short data)
  340. {
  341.     //Shift in some data
  342.     SPDR = data;
  343.     //Wait for SPI process to finish
  344.     while(!(SPSR & (1<<SPIF))) ;
  345.    
  346.     //Latch into output latch
  347.     LATCH_PORT |= LATCH;
  348.     delayMicroseconds(1);
  349.     LATCH_PORT &= ~LATCH;
  350.     delayMicroseconds(1);
  351. }
  352.  
  353. void send_message_spi(char* data,unsigned short message_length)
  354. {
  355.     for(int i = 0; i < message_length ; i++)
  356.     {  
  357.         //Shift in some data
  358.         SPDR = data[i];
  359.         //Wait for SPI process to finish
  360.         while(!(SPSR & (1<<SPIF))) ;
  361.     }
  362.    
  363.     //Latch into output latch
  364.     LATCH_PORT |= LATCH;
  365.     delayMicroseconds(1);
  366.     LATCH_PORT &= ~LATCH;
  367.     delayMicroseconds(1);
  368.  
  369. }
  370.  
  371. void print_time(unsigned long time1, unsigned long time2)
  372. {
  373.     unsigned int t1_mm = time1/30000;
  374.     unsigned int t2_mm = time2/30000;;
  375.     unsigned int t1_ss = (time1/500)%60;
  376.     unsigned int t2_ss = (time2/500)%60;
  377.     unsigned short int t1_cc = (time1/5)%100;
  378.     unsigned short int t2_cc = (time2/5)%100;
  379.     char lcd_char_time[16] ={'-',':','-','-','.','-','-',' ',' ','-',':','-','-','.','-','-'};
  380.    
  381.     time_characters led_char_time = {LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR,LINE_CHAR};
  382.  
  383.     if(time1 != MAXIMUM_TIME)
  384.     {
  385.      
  386.       lcd_char_time[3] = (led_char_time.t1_ss_unit = t1_ss%10) + 48;
  387.       lcd_char_time[2] = (led_char_time.t1_ss_tenth = (t1_ss/10)%10) + 48;
  388.       lcd_char_time[0] = (led_char_time.t1_mm = t1_mm) + 48;
  389.       lcd_char_time[6] = (led_char_time.t1_cc_unit = t1_cc%10) + 48;
  390.       lcd_char_time[5] = (led_char_time.t1_cc_tenth = (t1_cc/10)%10) + 48;
  391.     }
  392.     if(time2 != MAXIMUM_TIME)
  393.     {
  394.       lcd_char_time[12] = (led_char_time.t2_ss_unit = t2_ss%10) + 48;
  395.       lcd_char_time[11] = (led_char_time.t2_ss_tenth = (t2_ss/10)%10) + 48;
  396.       lcd_char_time[9] = (led_char_time.t2_mm = t2_mm) + 48;
  397.       lcd_char_time[15] = (led_char_time.t2_cc_unit = t2_cc%10) + 48;
  398.       lcd_char_time[14] = (led_char_time.t2_cc_tenth = (t2_cc/10)%10) + 48;
  399.     }
  400.     lcd.setCursor(0, 1);
  401.  
  402.     for(unsigned short int i = 0; i < 16 ; i++)
  403.         lcd.print(lcd_char_time[i]);
  404.  
  405.    unsigned short int col = 0;
  406.      
  407.    clear_all_display_shadow();   //Clear displayshodow memory
  408.  
  409.    //Fungerar denna på full storlek?+?
  410.    col +=2; // two blank pixel coloumns    
  411.    col = write_character_to_display_shadow(charTable[led_char_time.t1_mm],col);
  412.    col = write_character_to_display_shadow(charTable[COLON_CHAR],col);
  413.    col = write_character_to_display_shadow(charTable[led_char_time.t1_ss_tenth],col);
  414.    col = write_character_to_display_shadow(charTable[led_char_time.t1_ss_unit],col);
  415.    col = write_character_to_display_shadow(charTable[DOT_CHAR],col);
  416.    col = write_character_to_display_shadow(charTable[led_char_time.t1_cc_tenth],col);
  417.    col = write_character_to_display_shadow(charTable[led_char_time.t1_cc_unit],col);
  418.  
  419.    col += 9; // pixel columns in between the two times
  420.    col = write_character_to_display_shadow(charTable[led_char_time.t2_mm],col);
  421.    col = write_character_to_display_shadow(charTable[COLON_CHAR],col);
  422.    col = write_character_to_display_shadow(charTable[led_char_time.t2_ss_tenth],col);
  423.    col = write_character_to_display_shadow(charTable[led_char_time.t2_ss_unit],col);
  424.    col = write_character_to_display_shadow(charTable[DOT_CHAR],col);
  425.    col = write_character_to_display_shadow(charTable[led_char_time.t2_cc_tenth],col);
  426.    col = write_character_to_display_shadow(charTable[led_char_time.t2_cc_unit],col);
  427.  
  428.    update_display();  // send display shadow to LED screen
  429.      
  430.    //LATCH_PORT &= ~LATCH;
  431.  
  432.    //delay(10);
  433.    //   SPCR |= (1<<SPIE); // SPI interrupt enable
  434.      
  435. }
  436.  
  437.  
  438. unsigned short int write_character_to_display_shadow(char* data, unsigned short col)
  439. {
  440.   unsigned int tmp;
  441.   unsigned short int i;
  442.   //uint16_t tmp[8];
  443.   for(i = 0; i < 8 ; i++) // for all rows, do pointer magic so it the character will be split between two bytes.  
  444.   {
  445.     *((char *)&tmp+1) = data[i];
  446.     *((char *)&tmp) = 0;
  447.     tmp = tmp>>col%8;
  448.  
  449.     display_shadow[col/8][i] |= *(((char *)&tmp)+1);
  450.     display_shadow[(col/8)+1][i] = *((char *)&tmp);  
  451.   }
  452.   return col+data[i]; // add the character width
  453. }
  454.  
  455. void clear_rest_display_shadow(unsigned short col)
  456. {
  457.   for(unsigned short panel = col/8 + 1 ; panel < DISPLAY_SIZE/8 ; panel++)
  458.   {
  459.     for(unsigned short i = 0; i < 8 ; i++)  
  460.     {
  461.       display_shadow[panel][i] = 0;  
  462.     }
  463.   }
  464. }
  465.  
  466. void clear_all_display_shadow(void)
  467. {
  468.   for(unsigned short panel = 0 ; panel < DISPLAY_SIZE/8 ; panel++)
  469.   {
  470.     for(unsigned short i = 0; i < 8 ; i++)  
  471.     {
  472.       display_shadow[panel][i] = 0;  
  473.     }
  474.   }
  475. }
  476.  
  477. void update_display(void) // fixa så att själva beräkningen för nästa utklockning sker medans hårdavaran sköter nuvarande. D.v.s. lägg till en en bytes temp buffer...
  478. {
  479.     SPDR = (display_shadow[0][0] & 0xF0) | (0x0F & display_shadow[0][1]>>4);
  480.    
  481.     for(unsigned short int i = 1; i < DISPLAY_SIZE ; i++)
  482.     {  
  483.         unsigned short int row = (i&1)<<1;
  484.         unsigned short int panel = i>>2;
  485.         char tmp;
  486.        
  487.         if(i >= (DISPLAY_SIZE/2))
  488.         {
  489.           row = row+4;
  490.           panel -= (DISPLAY_SIZE/8);
  491.         }
  492.    
  493.         if(i & 0b00000010)
  494.           tmp = (display_shadow[panel][row+1] & 0x0F) | (0xF0 & display_shadow[panel][row]<<4);
  495.         else
  496.           tmp = (display_shadow[panel][row] & 0xF0) | (0x0F & display_shadow[panel][row+1]>>4);  
  497.  
  498.         while(!(SPSR & (1<<SPIF))) ; //Wait for SPI process to finish
  499.        
  500.         SPDR = tmp;
  501.     }
  502.  
  503.   while(!(SPSR & (1<<SPIF))) ; //Wait for SPI process to finish
  504.   LATCH_PORT |= LATCH; //Latch into output latch
  505.   delayMicroseconds(1);
  506.   LATCH_PORT &= ~LATCH;
  507. }
  508.  
  509.  
  510. void clear_first_panel_display_shadow(void)
  511. {
  512.   for(unsigned short i = 0; i < 8 ; i++)  
  513.   {
  514.     display_shadow[0][i] = 0;  
  515.   }
  516. }
  517.  
  518.  
  519. void print_time_serial(unsigned short state, unsigned long time1, unsigned long time2) //debug printing of current state, volatile holdoff and the last measured time for each player
  520. {
  521.     unsigned int t1_ss = time1/500;
  522.     unsigned int t2_ss = time2/500;
  523.     unsigned short int t1_cc = (time1/5)%100;
  524.     unsigned short int t2_cc = (time2/5)%100;
  525.     // calculate time as seconds and hundreds of seconds before sending on serial port
  526.     Serial.print("State: ");
  527.     Serial.print(state);
  528.     Serial.print(" Holdoff: ");
  529.     Serial.print(holdoff);
  530.     Serial.print(" Time 1: ");
  531.     Serial.print(t1_ss);
  532.     Serial.print(".");
  533.     if(t1_cc < 10)
  534.     {
  535.         Serial.print("0");
  536.     }
  537.     Serial.print(t1_cc);
  538.     Serial.print(" Time 1: ");
  539.     Serial.print(t2_ss);
  540.     Serial.print(".");
  541.     if(t2_cc < 10)
  542.     {
  543.         Serial.print("0");
  544.     }
  545.     Serial.println(t2_cc);
  546. }
  547.    
  548.  
  549. ISR(TIMER1_COMPA_vect) // interrupt runs once for every timer tick. If button holdoff is clear and button is pressed, store current time in volatile variable of corresponding player
  550. {  
  551.     unsigned short int new_button_state;
  552.    
  553.     timer_tick_vola++;  //increment main timer, one tick (2 ms) have passed
  554.    
  555.     new_button_state = (~BUTTON_PORT & ~holdoff) & (MAIN_BUTTON | PLAYER_1_BUTTON | PLAYER_2_BUTTON);  // if a button is pressed and holdoff is clear set high button state, buttons is active low
  556.  
  557.     if(MAIN_BUTTON & new_button_state) // store both player time if start (main) button is pressed. The start time ie.
  558.     {
  559.         holdoff |= MAIN_BUTTON;
  560.         last_tick_1_vola = timer_tick_vola;
  561.         last_tick_2_vola = timer_tick_vola;        
  562.     }
  563.  
  564.     if(PLAYER_1_BUTTON & new_button_state)  // store player time if player 1 button is pressed. The finish time for that player ie.
  565.     {
  566.         holdoff |= PLAYER_1_BUTTON;
  567.         last_tick_1_vola = timer_tick_vola;        
  568.     }
  569.  
  570.     if(PLAYER_2_BUTTON & new_button_state) // store player time if player 2 button is pressed. The finish time for that player ie.
  571.     {
  572.         holdoff |= PLAYER_2_BUTTON;
  573.         last_tick_2_vola = timer_tick_vola;        
  574.     }
  575. }


Logga in för att visa de filer som bifogats till detta inlägg.


Upp
 Profil  
 
InläggPostat: 23.58 2017-07-09 
Admin
Användarvisningsbild

Blev medlem: 14.59 2003-05-26
Inlägg: 8083
Ort: Solna
:tumupp:


Upp
 Profil  
 
Visa inlägg nyare än:  Sortera efter  
Svara på tråd  [ 20 inlägg ]  Gå till sida 1, 2  Nästa

Alla tidsangivelser är UTC + 1 timme


Vilka är online

Användare som besöker denna kategori: Google [Bot] och 6 gäster


Du kan inte skapa nya trådar i denna kategori
Du kan inte svara på trådar i denna kategori
Du kan inte redigera dina inlägg i denna kategori
Du kan inte ta bort dina inlägg i denna kategori
Du kan inte bifoga filer i denna kategori

Sök efter:
Hoppa till:  
    Electrokit
Drivs av phpBB® Forum Software © phpBB Group
Swedish translation by Peetra & phpBB Sweden © 2006-2010