Arduino IDE program to control 8253 Programmable interval timer

Iam looking for someone who could help me write a bit of code that uses a teensy 2.0 ++ so that it will send instructions to a 8253 programmable interval timers to output square waves at different frequencies on each of the three internal timers. Its a pretty out dated chip so im having trouble understanding how to interface with it and send instructions. Im hoping to write a bigger program for the 8253 and ive read all the resources i can find on them but im having trouble with just the basics and getting started. Any thoughts or help would be great. Thanks!

This looks like a dubbel posted question. Ask moderator to move the question to the best section.

Without going into too much detail, it looks like you need to setup Mode 3 on each of the counters…
What frequencies are you looking for ?
What clock source are you going to use?

Setting the registers shouldn’t be too hard, with the CS, WR, and 8-bit data byte to write into the chip.

2 Likes

Here is the datasheet for the 8253

1 Like

You are also going to need a couple of shift registers if you can't afford to use 8 pins for the data bits since the device uses an 8 bit "bus"

Yes I believe the mode three is the continuous square wave mode. i will being using dozens of different frequencies in my final code so itll be a lot to write out but a little show of how you assign the frequencies would be helpful. I will be using a 1.902 mhz clock for the counter clock inputs

THIS has some examples, and a description of working with the 8253. As long as you sort out your hardware and clock, the rest should be pretty straightforward.

The trick will be developing the simplest user interface to control your three separate frequency selections.

Haha Yes i have read that page before. It is not as straight forward seeming to me but it is also my first time diving into something like this so forgive me. I understand as a starter CS will set low, A0 A1 will be set low to select counter 0, WR will be set low while sending control works. But then the control word process is where i get lost.

You can try the following. It compiles but isn't tested.

It assumes the use of an HC595 shift register to hold the D0..7 parallel data to to the 8253 (to save Teensy pins.) If you prefer to use parallel and have the pins available it'd be easy to switch it over.

All it does is send different count values to each of the three timers in the 8253 to produce different square wave. You can use a scope to see if it worked.

/*
 * Sketch: sketch_oct12c.ino
 * Target:  Teensy 2.0++
 *
 *  Drives HC595 serial shift register and 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;

//shift register pins
const uint8_t pinStrobe = 8;    //ST_CP of HC595
const uint8_t pinClock = 12;    //SH_CP of HC595
const uint8_t pinData = 11;     //DS of HC595
const uint8_t pinCS = 2;        //to 8253 chip-select
const uint8_t pinA0 = 3;
const uint8_t pinA1 = 4;
const uint8_t pinRD = 5;
const uint8_t pinWR = 6;

uint8_t timerData;
uint16_t timerValue;

void setup() 
{
    //set up GPIOs
    pinMode( pinStrobe, OUTPUT );    
    pinMode( pinCS, OUTPUT );
    pinMode( pinA0, OUTPUT );
    pinMode( pinA1, OUTPUT );
    pinMode( pinRD, OUTPUT );
    pinMode( pinWR, OUTPUT );

    digitalWrite( pinStrobe, HIGH );
    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
    
}//loop

void Write8253( uint8_t address, uint8_t dataval )
{
    //shift data byte out to HC595 shift register
    digitalWrite( pinStrobe, LOW );
    shiftOut( pinData, pinClock, LSBFIRST, dataval );
    digitalWrite( pinStrobe, HIGH );

    //set 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
    
}//Write8253
1 Like

OOoooh Man! Thank you so much! this is amazing! and soo helpful. and saves me from posting my embarrassing code attempt haha. Im bread boarding it right now to see if i can get it work. I actually wont be using a shift register so i will try to figure out how I can modify the code you provided. Seriously thank you a million times!

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