/**
* AVR M200 Punch card reader controller
* Main file
*
* This file implements interrupt control, user I/O and the program flow.
* The card model is implemented by punchcard.{h, c}.
*
* The complete program is very specific to the wiring of the microcontroller,
* as defined in wiring.h. It was developed for an AVR ATmega 644 with a 8Mhz
* clock.
*
* The uC is set on an Olimex AVR-P40 board, having the TX pad connected to
* PD1 (pin15) and RX pad with PD0 (pin14).
*
* It was compiled with AVRStudio + WinAVR (gcc 4, avr-libc required).
*
* This file is part of the Punched Paper Project - Punch Card Device Drivers
* Copyright (C) 2009 Sven Koeppel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see
* .
**/
#define __AVR_ATmega644__ 1
#define OSCSPEED 8000000 /* in Hz */
#include
#include // testweise
#include "avr/io.h"
#include "avr/interrupt.h"
#include "punchcard.h"
#include "wiring.h"
#include "io.h"
char Text1[80];
static FILE uart = FDEV_SETUP_STREAM(uart_transmit, NULL, _FDEV_SETUP_WRITE);
/**
** UART, I/O, LED
**/
void Init(void)
{
// Datenports einrichten
PORTA = 0x00; // erste 8 Daten-Input
PORTB = 0x00; // LED (auslassen, an=0x01), 3 Fehler (IN), Button (IN), ICSP
PORTC = 0x00; // 4 Daten, 3 Status (IN), 1 Signal (OUT)
PORTD = 0x00; // UART und -Flusssteuerung
DDRA = 0x00; // 8 Daten: Input
DDRB = (1 << DDB0); // Nur LED als Output, alles andere Input
DDRC = (1 << DDC6); // Nur Pick Command als Output, alles andere Input
DDRD = 0x00; // UART halt, IM jetzt auch
// Pin Change Intterupts aufsetzen
// PCMSK*: Pin Change Masks (welche Pins tragen zum Intterupt bei)
PCMSK1 = (1 << PCINT_ERROR) | (1 << PCINT_HCK) | (1 << PCINT_MOCK); // PORT B
PCMSK2 = (1 << PCINT_BSY) | (1 << PCINT_RDY); // PORT C
// Pin Change Interrupt Control Register: PC Interrupt Enable for Port B & C
PCICR = (1 << PCIE1) | (1 << PCIE2);
// Ausgezeichnete Interrupts aufsetzen (Index Mark IM haengt an INT0)
EICRA = (1 << ISC01) | (1 << ISC00); // rising edge von INT0 erzeugt intterupt
EIMSK = (1 << INT0); // interrupt fuer INT0 anschalten
// interrupt enable
sei();
}
void UartInit(uint32_t Baud)
{
int BaudRate = OSCSPEED / (16 * Baud) - 1;
UBRR0H = (unsigned char) BaudRate>>8;
UBRR0L = (unsigned char) BaudRate;
//set BaudRate
UCSR0B = UCSR0B | (0b00011000);
// RXEN & TXEN enable (Bits 4, 3 = 1)
UCSR0C = (UCSR0C | (0b10000110));
// USART Register Select (Bit 7 = 1)
// 8 data bits per frame (Bit 2, 1 = 1)
UCSR0C = UCSR0C & 0b11110111;
// 1 Stop bit (Bit 3 = 0)
}
/**
** Interrupt routine
**/
ISR(PCINT1_vect) {
// Called for Pin changes on Error Signals:
// * PINB_ERR
// * PINB_HCK
// * PINB_MOCK
uint8_t pinb_jetzt = PINB;
uint8_t pin_changed = (pinb_jetzt ^ status.prev_pinb) & PCMSK1;
uint8_t rising_edge = ((((~status.prev_pinb) & pinb_jetzt) & PCMSK1) & pin_changed) == pin_changed ? 1 : 0;
char* name = "???";
switch( pin_changed ) {
case (1 << PINB_ERROR): name = "ERR"; break;
case (1 << PINB_HCK): name = "HCK"; break;
case (1 << PINB_MOCK): name = "MOCK"; break;
case 0:
printf("Too lazy, but not "); // und jetzt nicht breaken :-)
default:
printf("Illegal PCINT1 raise: %x=>%x, %x, %x\n", status.prev_pinb, pinb_jetzt, pin_changed, rising_edge);
status.prev_pinb = pinb_jetzt;
return;
}
printf("%s %s\n", name, rising_edge ? "raising" : "falling");
status.prev_pinb = pinb_jetzt;
}
ISR(PCINT2_vect) {
// this routine is called for
// * PINC_IM : Index Mark (store current column)
// * PINC_RDY : Ready (reader ready for new Pick Command)
// * PINC_BSY : Busy (card edge - there is a card)
// to find out what changed, we make a backup of the former
// PINC at the end of this routine.
uint8_t pinc_jetzt = PINC;
uint8_t pin_changed = (pinc_jetzt ^ status.prev_pinc) & PCMSK2;
uint8_t rising_edge = check( pin_changed, (~status.prev_pinc) & pinc_jetzt );
switch( pin_changed ) {
case (1 << PCINT_RDY):
// Ready is directly indicated by GREEN button on device.
// Pick Command is only interpreted while Ready is up.
if(rising_edge) {
// reader got ready -- decide whether to start a new card,
// if we hadn't started already
if( check_pin(PINC, PINC_PC) ) {
// we have started already...
puts("READY rising, starting ASAP");
} else {
// check if there's space in the buffer to start up
if(card_buffer_count_free() > 0) {
// Start.
// urhm... wait for user to start? user condition?
// yeah: quick & dirty:
if(status.user_instruction == START_READER) {
// start. BSY signal will create new card.
start_reader();
}
}
}
puts("READY rising");
} else {
// device is no more ready -- perhaps user shutdown (by button).
puts("READY falling");
}
break;
case (1 << PCINT_BSY):
// busy =~= card edge
if(rising_edge) {
if(current_write_card.offset != CARD_EMPTY) {
// start a new card
if(card_buffer_write_advance() != SUCCESS) {
// No more space in buffer! VERY BAD!
puts("BUSY rising, LOOSING DATA!");
} else
printf("BUSY rising, new card %d (reader: %d)\n",
card_buffer.write_offset, card_buffer.read_offset);
} else {
puts("BUSY rising, staying card");
}
} else {
puts("BSY falling");
// nochmal sicherheitshalber, wobei das sowieso viel zu spaet ist
if( !card_buffer_count_free() ) {
stop_reader();
}
}
break;
case 0:
// Signal lag zu kurz an
printf("Lost Interrupt, state %x ed=%x\n", pinc_jetzt, rising_edge);
break;
default:
// illegal intterupt!
printf("Illegal interrupt! PINC was %x is %x changed %x flanke %x\n",
status.prev_pinc, pinc_jetzt, pin_changed, rising_edge);
break;
}
// backup new "old" value
status.prev_pinc = pinc_jetzt;
} // ISR
ISR(INT0_vect) {
// soho... willkommen zum neuen IM-Interrupt :-)
// hier koennen wir sicher sein, dass gerade eine IM-Flanke auftrat.
// die wird ab 50ns gemessen :-)
// die 5 Zyklen bis zur verarbeitung spielen dann eh keine Rolle mehr
if( ! (PINC & (1 << PINC_BSY)) ) {
// die kann man verwerfen, keine Kartenkante
puts("IMS");
return;
}
if(current_write_card.offset < 0 || current_write_card.offset >= CARD_LEN ) {
puts("BAD card! Stopping reader");
if(PINC & (1 << PORTC_PC)) {
puts("Reader wasnt stopped");
}
stop_reader();
return;
}
// oder radikal so (ist ja genug Zeit zum Abchecken da)
if( !card_buffer_count_free() )
stop_reader();
// store column
current_write_column =
(check_pin(PINA, PINA_D0) << COL0) | // copy register A
(check_pin(PINA, PINA_D1) << COL1) | // (bitwise since we don't want to
(check_pin(PINA, PINA_D2) << COL2) | // make assumptions about bit locations)
(check_pin(PINA, PINA_D3) << COL3) |
(check_pin(PINA, PINA_D4) << COL4) |
(check_pin(PINA, PINA_D5) << COL5) |
(check_pin(PINA, PINA_D6) << COL6) |
(check_pin(PINA, PINA_D7) << COL7) |
(check_pin(PINC, PINC_D8) << COL8) |
(check_pin(PINC, PINC_D9) << COL9) |
(check_pin(PINC, PINC_D11) << COL11) |
(check_pin(PINC, PINC_D12) << COL12);
// column stored. increase column counter
// (correctness will be checked on next call)
current_write_card.offset++;
// Das war die 80. Spalte (intern 79) => finalisieren.
if(current_write_card.offset == CARD_LEN) {
// diese karte ist fertig
// finalize the current card
//current_write_card.offset = CARD_READY; // unnoetig, da CARD_READY == CARD_LEN
puts("wr++");
// try to create a new card
if(card_buffer_write_advance() != SUCCESS) {
// no more space! turn off Punch Instruction!
stop_reader();
puts("No more space on buffer.");
status.reader_waits_for_space = 1;
}
}
// und Blinken darf er auch gerne :-)
if (PINB & 0b00000001)
start_led();
else
stop_led();
}
int main()
{
card_buffer_flush();
Init();
UartInit(38400);
start_led();
// nette sachen machen koennen
stdout = &uart;
user_input_loop();
return 0;
}