Here's an attempt at a parallel version. It uses all of port 'C' for the data bus and uses direct port manipulation for simplicity.
Because the use of a parallel I/O port allows reading I added that functionality too (untested!)
/*
* Sketch: sketch_oct13c.ino
* Target: Teensy 2.0++
*
* Uses parallel data for the 8253 to
* (hopefully!) demonstrate loading timers
* to produce square waves.
*/
typedef enum
{
COUNTER0 = 0, //00
COUNTER1, //01
COUNTER2, //10
WR_MODE_WORD //11
}e8253_ADDRS_t;
//parallel data to 8253
const uint8_t pinD0 = 10; //PORTC0 declarations for clarity only
const uint8_t pinD1 = 11; //...
const uint8_t pinD2 = 12;
const uint8_t pinD3 = 13;
const uint8_t pinD4 = 14;
const uint8_t pinD5 = 15;
const uint8_t pinD6 = 16;
const uint8_t pinD7 = 17; //PORTC7
const uint8_t pinCS = 2; //to 8253 chip-select
const uint8_t pinA0 = 3; //address A0
const uint8_t pinA1 = 4; //address A1
const uint8_t pinRD = 5; //!read
const uint8_t pinWR = 6; //!write
uint8_t timerData;
uint16_t timerValue;
void setup()
{
//set data direction to input initially
DDRC = 0x00;
PORTC = 0x00;
pinMode( pinCS, OUTPUT );
pinMode( pinA0, OUTPUT );
pinMode( pinA1, OUTPUT );
pinMode( pinRD, OUTPUT );
pinMode( pinWR, OUTPUT );
digitalWrite( pinCS, HIGH );
digitalWrite( pinWR, HIGH );
digitalWrite( pinRD, HIGH );
//counter 0
timerData = 0b00110110; //counter 0, load least then most, Mode 3, binary count
timerValue = 0x7fff;
Write8253( WR_MODE_WORD, timerData );
Write8253( COUNTER0, (uint8_t)(timerValue & 0x00ff) );
Write8253( COUNTER0, (uint8_t)((timerValue >> 8) & 0x00ff) );
//counter 1
timerData = 0b01110110; //counter 1, load least then most, Mode 3, binary count
timerValue = 0xfff5;
Write8253( WR_MODE_WORD, timerData );
Write8253( COUNTER1, (uint8_t)(timerValue & 0x00ff) );
Write8253( COUNTER1, (uint8_t)((timerValue >> 8) & 0x00ff) );
//counter 2
timerData = 0b10110110; //counter 2, load least then most, Mode 3, binary count
timerValue = 0x1000;
Write8253( WR_MODE_WORD, timerData );
Write8253( COUNTER2, (uint8_t)(timerValue & 0x00ff) );
Write8253( COUNTER2, (uint8_t)((timerValue >> 8) & 0x00ff) );
}//setup
void loop()
{
//nothing to do in loop
//for this proof of concept
}//loop
void Write8253( uint8_t address, uint8_t dataval )
{
//set databus
PORTC = dataval; //set databus value
DDRC = 0xff; //and drive to pins (make pins outputs)
//set 8253 address pins
digitalWrite( pinA0, (address & 0x01) ? HIGH:LOW );
digitalWrite( pinA1, (address & 0x02) ? HIGH:LOW );
//wiggle CS and WR to send to 8253
digitalWrite( pinCS, LOW ); //set chip select
delayMicroseconds( 10 ); //wait >> Taw
digitalWrite( pinWR, LOW ); //assert WR low
delayMicroseconds( 10 ); //wait >> Tww
digitalWrite( pinWR, HIGH ); //de-assert WR
delayMicroseconds( 10 ); //wait >> Twa
digitalWrite( pinCS, HIGH ); //de-assert CS
//set databus back to default HiZ input state
DDRC = 0x00;
}//Write8253
uint16_t Read8253( uint8_t counter )
{
//we can only read counters, not the mode register
if( counter > 2 )
return 0x0000;
//write a mode word that latches the count for the desired counter
uint8_t mask = (counter & 0x03) << 6; //set SC1:0 to counter address, RL1:0 low
Write8253( WR_MODE_WORD, mask ); //write latches counter value for read
//issue two reads to get the latched counter value (LSB read first)
return( ReadByte(counter) + (ReadByte(counter) << 8) );
}//Read8253
uint8_t ReadByte( uint8_t address )
{
//set the address pins
digitalWrite( pinA0, (address & 0x01) ? HIGH:LOW );
digitalWrite( pinA1, (address & 0x02) ? HIGH:LOW );
digitalWrite( pinCS, LOW ); //assert chip select low
delayMicroseconds( 10 ); //wait >> Tar
digitalWrite( pinRD, LOW ); //assert RD low
delayMicroseconds( 10 ); //wait >> Trr
digitalWrite( pinRD, HIGH ); //de-assert RD and read the byte from the bus
uint8_t mask = PINC;
delayMicroseconds( 10 ); //wait >> Twa
digitalWrite( pinCS, HIGH ); //de-assert CS
return mask;
}//ReadByte