/**
* 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"
char Text1[80];
int uart_transmit(char c, FILE* stream);
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)
}
int uart_transmit(char c, FILE* stream) {
if(c == '\n')
uart_transmit('\r', stream);
while (!(UCSR0A & 0b00100000));
// PENDING: Hardware flow control
UDR0 = c;
return 0;
}
unsigned char UartReceive(void)
{
if (UCSR0A & 0b10000000)
//if RXC(Bit 7) = 1
return UDR0;
else
return 0;
}
void UartTransmit(unsigned char Data)
{
while (!(UCSR0A & 0b00100000));
//while UDRE = 0 --> ;
UDR0 = Data;
}
void LedMode (unsigned char Temp)
{
switch (Temp)
{
// 1 - LED ON
case 1: PORTB = PORTB & 0&11111110;
PORTC = PORTC & 0&10111111;
break;
// 2 - Led OFF
case 2: PORTB = PORTB | 0b00000001;
PORTC = PORTC | 0b01000000;
break;
// 3 - Toggle Led
case 3: if (PINB & 0b00000001) PORTB = PORTB & 0b11111110;
else PORTB = PORTB | 0b00000001; break;
}
}
unsigned char Length(char Temp[20])
{
unsigned char L=0;
while (Temp[L]) L++;
return L;
}
void Write(char Text[80])
{
unsigned char Len, i, T;
Len = Length(Text);
//UartTransmit(Len);
for (i=0; i= 0; bit--) {
str[pos++] = ( (1 << bit) & byte ) ? '1' : '0';
}
str[8] = ' ';
str[9] = '\0';
strcat(Text1, str);
}
/**
** Interrupt routine
**/
static struct previous_pin_memory {
uint8_t pinc;
uint8_t pinb;
} prev;
static uint8_t user_start = 0; // muss noch sauber umgesetzt werden
static uint8_t reader_waits_for_space = 0; // also defacto der WRITER
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 ^ prev.pinb) & PCMSK1;
uint8_t rising_edge = ((((~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", prev.pinb, pinb_jetzt, pin_changed, rising_edge);
prev.pinb = pinb_jetzt;
return;
}
printf("%s %s\n", name, rising_edge ? "raising" : "falling");
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 ^ prev.pinc) & PCMSK2;
uint8_t rising_edge = check( pin_changed, (~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(user_start) {
// 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",
prev.pinc, pinc_jetzt, pin_changed, rising_edge);
break;
}
// backup new "old" value
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.");
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);
LedMode(1);
// nette sachen machen koennen
stdout = &uart;
unsigned char Action = 0, display_menu;
enum format {
HEX = 4,
DEBUG = 5,
BINARY = 6
} out_format = DEBUG;
while (1)
{
Action = 0;
display_menu = 0;
//out_format = 4;
puts("");
puts("Menu");
puts(" 1) Start reading");
puts(" 2) Stop reading");
puts(" 3) Stop; Reset buffer");
puts(" 4) HEX 5) DEBUG 6) BINARY JONES");
puts("");
puts("Choose Action: ");
while (! display_menu)
{
// erst mal buffer abarbeiten
if( current_read_card.offset == CARD_READY ) {
int col;
printf("Karte in rd %d rw %d, cols rd %d rw %d\n", card_buffer.read_offset, card_buffer.write_offset,
current_read_card.offset, current_write_card.offset);
// Karte abgearbeitet
current_read_card.offset = CARD_DONE;
//break; // Karte NICHT komplett ausgeben. Hier stoppen.
if(out_format != BINARY) {
for(col = 0; col < CARD_LEN; col++) {
if(out_format == HEX) {
printf("%x,%x ", (uint8_t) (current_read_card.columns[col] >> 8),
(uint8_t) (current_read_card.columns[col]));
} else if(out_format == DEBUG) {
printf("%02d: ", col+1);
column_print( ¤t_read_card.columns[col] );
} else {
puts("Bad output format");
break;
}
}
puts("");
} else { // binary
puts("");
// make two columns to three bytes, after Douglas Jones
uint8_t x = 0;
Column even_col, odd_col;
while(x <= CARD_LEN) {
even_col = current_read_card.columns[x++];
odd_col = current_read_card.columns[x++];
putchar( even_col >> 4 ); // erstes byte
putchar( ((even_col & 017) << 4) | (odd_col >> 8) ); // zweites byte
putchar( odd_col & 00377 ); // drittes byte
}
puts("");
}
}
if( current_read_card.offset == CARD_DONE) {
// alle karten abgearbeitet, versuche zur naechsten
// Karte zu kommen
if( card_buffer_read_advance() != SUCCESS) {
puts("output waiting for next");
} else {
puts("cr+");
}
// und der Writer kann jetzt auch wieder loslegen
// also eigentlich wartet hier ja der WRITER
if(reader_waits_for_space) {
// leser hat auf uns gewartet (puffer war voll, leser war angehalten,
// wir haben nun eine weitere Karte gelesen => Leser kann in dieses
// Feld nun wieder schreiben)
if(card_buffer_write_advance() == SUCCESS) {
// es kann wieder weitergehen fuer den Leser
reader_waits_for_space = 0;
start_reader();
} else {
puts("Deadline, writer waits for reader");
}
}
}
// dann nutzereingaben abarbeiten
Action = UartReceive();
if (Action) Action = Action - 48;
if (Action)
{
display_menu = 1;
switch(Action) {
case 1:
user_start = 1;
start_reader();
start_led();
break;
case 2:
user_start = 0;
stop_reader();
stop_led();
break;
case 3:
puts("Soft Reset");
user_start = 0;
stop_reader();
stop_led();
card_buffer_flush();
break;
case 4:
case 5:
case 6:
out_format = Action;
printf("Setting out format to %d\n", out_format);
break;
default:
printf("Illegal input %d\n", Action);
}
} // if action
}
}
return 0;
}