Monday, June 22, 2015

The BQ4845 RTC

The simplest external device I could see for my first attempt at a peripheral was the TI BQ4845 RTC. It's a DIP package and works like an SRAM, can have a 32,768Hz crystal attached directly to it, and has a simple interface for connecting and programming.

After ensuring I knew how to drive it and talk to it using an Arduino clone I tried putting it on a breadboard and talking to it from the CPUville kit. Something very odd happened - I could read the minutes register okay but I was getting the constant value 11000000 back for the seconds or hours.

Back to the Arduino clone to double check the IC was okay, and it was. I could not see anything on my schematic or connections that would cause this, and the control signals looked right when recorded by the logic probe.

I decided to solder up a prototype board anyway thinking (hoping?) that would solve the problem. It didn't. I was thrown off briefly when the IC wouldn't count time for the Arduino clone either, but eventually the crystal must have started osciallating and the seconds ticked up. No idea why that happened either. This is why you don't let software people tinker with any hardware you care about, or that has to work properly! Clueless.

So I'm watching the minutes tick up and the seconds and hours going nowhere when a casual glance at the CPUville board makes me notice the DIP switches attached to I/O port 0 are showing this same constant pattern! Then it becomes obvious the CPUville board is still acting on the I/O requests and servicing the reads from the DIP switches whose signal I assume is getting to the CPU before the RTC ones from off-board.

The design of the kit allows the on-board DIP switches and LEDs to be disconnected using a single jumper. But it also needs those switches to set the starting address after reset with the v7 ROM that comes with the kit. I can't use the v8 ROM from the memory expansion kit which doesn't read the switches because it also assumes more than 2KB of RAM. But I can have the switches connection when coming out of reset and then remove the jumper once the CPUville monitor is running. And now the seconds and hours are working too :)

I have assembled a copy of the v7 code with the start address hard-coded to get around this but I need to buy a 2K EEPROM to hold it. I also need to make a small mod to my EEPROM programmer to allow it to work with either 8K or 2K EEPROMs.

I still can't get a value from the AM/PM bit in the hours register even when in 12 hour mode, either with the CPUville or the Arduino clone.

I also have not hooked up the interrupt line yet because I need to make a CPU daughterboard to get access to those missing control signals and also add them to my expansion headers.

There is a reset pin I could use too, but all it does is ground the reset line if it detects there is not enough voltage at VCC. This doesn't seem useful enough to worry about for what I'm trying to build.

So the BQ4845 is dead simple to use for the hobbyist. I'm surprised there are not more DIY computers using this IC. I could only find one other.

4 comments:

Unknown said...

David can you provide a picture of the pinouts you have hooked up to the BQ4845 and arduino? i have a few of these and also BQ3285p's to play around with, these are cheap and now no longer produced by TI but their large stock base and cleap to buy price makes them ideal for homebrew projects where you don't want/or need a Dallas RTC.

What library have you used or have you done your own, will be interesting to hear, thanks, john

David Taylor said...

Hi John. Here are the pins I used on an Arduino Mega256 clone. I like to use it for prototyping because it is easy to hook up 8- or 16-bit address and data buses without shift registers.

pin 10 / PB4 = /CS
pin 11 / PB5 = /OE
pin 12 / PB6 = /WE

Port F = data bus
Port K 0:3 = address bus

I just wrote the code to talk to the chip in the sketch. Eg

#define ASSERT_CS PORTB &= ~0b00010000; delayMicroseconds(1);
#define CLEAR_CS PORTB |= 0b00010000;

#define ASSERT_OE PORTB &= ~0b00100000; delayMicroseconds(1);
#define CLEAR_OE PORTB |= 0b00100000;

#define ASSERT_WE PORTB &= ~0b01000000; delayMicroseconds(1);
#define CLEAR_WE PORTB |= 0b01000000;

// vals must be a 6 element array
// output order is:
// 0 = hh
// 1 = mm
// 2 = ss
// 3 = dd
// 4 = mm
// 5 = yy
void get_time(uint8_t * vals) {
ASSERT_CS

write_reg_no_cs(CONTROL, 12);

DDRF = 0x00; // set data bus to input

PORTK = HOURS;
ASSERT_OE
vals[0] = PINF;
CLEAR_OE

PORTK = MINUTES;
ASSERT_OE
vals[1] = PINF;
CLEAR_OE

PORTK = SECONDS;
ASSERT_OE
vals[2] = PINF;
CLEAR_OE

PORTK = DAY;
ASSERT_OE
vals[3] = PINF;
CLEAR_OE

PORTK = MONTH;
ASSERT_OE
vals[4] = PINF;
CLEAR_OE

PORTK = YEAR;
ASSERT_OE
vals[5] = PINF;
CLEAR_OE

write_reg_no_cs(CONTROL, 4);

CLEAR_CS
}

Unknown said...

Excellent, I fancy having another go at these, a lot of people avoid them because of size but they're cheap and fun. I'll keep you posted how I get on.

Unknown said...

David, any chance you can do a clear picture of one hooked up for me by any chance? John