/** * \file main.c * \brief firmware for the i2c-ledmatrix * * this is a really simple piece of code, since the main work is done by the * I2C-library. * \author Ronald Schaten * \version $Id: main.c,v 1.1 2008/07/16 05:44:45 rschaten Exp $ * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. */ /** * \mainpage I2C LED Matrix * * \section sec_intro Introduction * * This project turns an AVR ATmega8 microcontroller into a LED controller for * a matrix of 8x8 LEDs. The controller is acting as I2C-slave, so you can * control the patterns to display via this bus (also known as TWI, Two Wire * Interface). * * \section sec_purpose Purpose * * For my next project, I need to display number values on * seven-segment-displays. I bought a bunch of 4-digit-displays a while ago, * now I'm going to put them to a use. They are built with four digits in one * case, and 12 pins on the underside. Eight of them are the cathodes of the * segments (seven segments plus dot), four are the anodes. One for each digit. * * You can imagine these modules as a matrix of four times eight LEDs, as can * be seen in the included circuit. I use two of these, so I have a matrix of * eight times eight LEDs. * * The rows and columns of this matrix are connected to the microcontroller, so * it can power them row by row. This has two advantages: at first a maximum of * eight LEDs is powered at a time, so power consumption is lowered. And at * second you need only 16 pins of the controller to address a total of 64 * LEDs. * * Driving the LEDs in this way makes them flicker a bit, but the controller is * fast enough to keep the flickering way above the level you would be able to * recognize. * * I could have connected my display modules directly to the main controller of * my next project, but I don't have enough free pins on that. As a further * benefit, multiplexing the LEDs on a second controller makes the main program * easier to write, since I don't have to mind the timing. So the solution is * to use a cheap ATmega8 as LED driver and use the I2C-bus to tell it what to * display. * * \section sec_i2c I2C communication * * The ATmega8 has a built-in hardware I2C-interface, so it doesn't take very * much code to use it. Nevertheless, I used a little library that Uwe * Grosse-Wortmann (uwegw) published on roboternetz.de. * I only reformatted it a bit to make the code resemble my style. It is well * commented, but the comments are in german. Since only one global array, one * init-function and an interrupt service routine are used, it shouldn't be too * hard for english-speaking people to figure out how it is used. * * \subsection sec_usage Usage * * On the other end of the communication, I used the excellent Procyon * AVRlib written by Pascal Stang. You can find it here. * * A basic code example would look like this: * * \code * #define I2C_LEDMATRIX 0x10 // address of the device * timerInit(); // initialize timers * timerPause(100); // give everything a little time to settle * i2cInit(); // initialize i2c function library * timerPause(100); // wait a bit more * while (1) { // endless loop * uint8_t buffer[9]; // prepare buffer * // loop until 255 * for (uint8_t i = 0; i <= 255; i++) { * // set all bytes of the buffer to value i * memset(buffer, i, sizeof(buffer)); * // send the buffer via I2C-bus * i2cMasterSend(I2C_LEDMATRIX, sizeof(buffer), buffer); * timerPause(500); // wait, so you have the time to watch * } * } * \endcode * * Note: the buffer doesn't contain any numbers that should be displayed on * 7segment-displays. At least not in this example. It only holds bit-patterns. * * \subsection sec_numbers Displaying numbers * * If you solder 7segment displays to the unit and intend to display numbers or * characters on it, you need to define them on the master-side of the bus. I * didn't include the definitions in this library because I want the master to * have the full flexibility of displaying whatever it wants to, even if it are * no numbers. * * However, if you are going to use 7segment displays, definition of the * numbers still depends on how you soldered them to the controller. I don't * know if the pin-outs are commonly standardized. * * To give an example of how you would implement this, here is a fragment of * code that defines hexadecimal numbers for usage on my displays: * * \code * // Names of the segments: * // aaaaa * // f b * // f b * // ggggg * // e c * // e c * // ddddd h * uint8_t characters[16]; * // c e g a h f b d * characters[ 0] = (1 << 0) | (1 << 1) | (0 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (1 << 6) | (1 << 7); // 0 * characters[ 1] = (1 << 0) | (0 << 1) | (0 << 2) | (0 << 3) | (0 << 4) | (0 << 5) | (1 << 6) | (0 << 7); // 1 * characters[ 2] = (0 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (0 << 5) | (1 << 6) | (1 << 7); // 2 * characters[ 3] = (1 << 0) | (0 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (0 << 5) | (1 << 6) | (1 << 7); // 3 * characters[ 4] = (1 << 0) | (0 << 1) | (1 << 2) | (0 << 3) | (0 << 4) | (1 << 5) | (1 << 6) | (0 << 7); // 4 * characters[ 5] = (1 << 0) | (0 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (0 << 6) | (1 << 7); // 5 * characters[ 6] = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (0 << 6) | (1 << 7); // 6 * characters[ 7] = (1 << 0) | (0 << 1) | (0 << 2) | (1 << 3) | (0 << 4) | (0 << 5) | (1 << 6) | (0 << 7); // 7 * characters[ 8] = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (1 << 6) | (1 << 7); // 8 * characters[ 9] = (1 << 0) | (0 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (1 << 6) | (1 << 7); // 9 * characters[10] = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (1 << 6) | (0 << 7); // a * characters[11] = (1 << 0) | (1 << 1) | (1 << 2) | (0 << 3) | (0 << 4) | (1 << 5) | (0 << 6) | (1 << 7); // b * characters[12] = (0 << 0) | (1 << 1) | (0 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (0 << 6) | (1 << 7); // c * characters[13] = (1 << 0) | (1 << 1) | (1 << 2) | (0 << 3) | (0 << 4) | (0 << 5) | (1 << 6) | (1 << 7); // d * characters[14] = (0 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (0 << 6) | (1 << 7); // e * characters[15] = (0 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (0 << 4) | (1 << 5) | (0 << 6) | (0 << 7); // f * \endcode * * \section sec_install Building and installing * * The firmware is built and installed on the controller with the included * makefile. You might need to need to customize it to match your individual * environment. * * If you take a brand-new controller you shouldn't have to hassle with the * fuses of the controller. The internal oscillator at 1MHz is enough to keep * the display flicker-free. The settings I used are included in the makefile, * so you can use it to reset controllers you already changed in other * projects. * * Oh, and if you want the slave to use an I2C-address different from 0x10: no * problem. Just change it in the code. * * \section sec_drawbacks Drawbacks * * Till now, the device worked in all situations I tested it in. So far * everything is fine. * * \section sec_files Files in the distribution * * - \e Readme.txt: Documentation, created from the htmldoc-directory. * - \e htmldoc/: Documentation, created from main.c. * - \e refman.pdf: Documentation, created from main.c. * - \e main.c: Source code of the firmware. * - \e main_*.hex: Compiled version of the firmware. * - \e twislave.c: I2C-library. * - \e twislave.h: I2C-library. * - \e project.doxygen: Support for creating the documentation. * - \e License.txt: Public license for all contents of this project. * - \e Changelog.txt: Logfile documenting changes in soft-, firm- and * hardware. * * \section sec_thanks Thanks! * * I'd like to thank the authors of the libraries I used: Uwe * Grosse-Wortmann (uwegw) for the I2C-slave and Pascal * Stang for the Procyon AVRlib. * * \section sec_license About the license * * My work is licensed under the GNU General Public License (GPL). A copy of * the GPL is included in License.txt. * * (c) 2008 by Ronald Schaten - http://www.schatenseite.de */ #include #include #include #include #include // keeping constants in program memory #include "twislave.h" /** * initialize hardware */ void init_ports(void){ // set DDR for all digit-pins DDRB = 0xff; DDRC |= (1 << PINC0) | (1 << PINC1); // unset PORT for all digit-pins PORTB = 0x00; PORTC &= ~((1 << PINC0) | (1 << PINC1)); DDRD = 0x00; // segment selector PORTD = 0x00; // segments, has to be 0x00 } /** * select which digit should be displayed * \param digit number of the digit */ void selectDigit(uint8_t digit) { switch (digit) { case 0: case 1: case 2: case 3: case 4: case 5: PORTB = (1 << digit); PORTC &= ~((1 << PINC0) | (1 << PINC1)); break; case 6: PORTB = 0x00; PORTC &= ~((1 << PINC1)); PORTC |= (1 << PINC0); break; case 7: PORTB = 0x00; PORTC &= ~((1 << PINC0)); PORTC |= (1 << PINC1); break; default: PORTB = 0x00; PORTC = 0x00; } } /** * set output of the currently selected digit * \param byte bit-pattern to show */ void showByte(uint8_t byte) { DDRD = byte; } /** * show a pattern on a certain digit (or row, if you don't use 7segment * displays). the output is cleared before selecting the new digit, so there * won't be 'shadows' on the display. * \param digit number of the digit * \param byte bit-pattern to show */ void showDigitByte(uint8_t digit, uint8_t byte) { showByte(0x00); selectDigit(digit); showByte(byte); } /** * main-function. initializes everything and contains the main loop which * controls the actual output. the rxbuffer[] is filled from the I2C-library, * so we just have go through the array and display its values on the * corresponding digit. * \return An integer. Whatever... :-) */ int main(void) { // initialize output ports init_ports(); // init watchdog wdt_enable(WDTO_15MS); // 15ms watchdog // init I2C communication init_twi_slave(0x10); while (1) { wdt_reset(); // feed the watchdog for (uint8_t digit = 0; digit <= 7; digit++) { // display all eight digits showDigitByte(digit, rxbuffer[digit]); } } return 0; }