In my quest to build a hardware-based binary prime number generator I decided to build a demonstration model / rapid prototype to prove to myself (and the world) that I can reliably (and quickly) generate prime numbers. This code needs a lot of work, but it’s functional. Instead of messing with tons of little LEDs, it just dumps the output to a LCD. Of note, the library to run the LCD takes up about 90% of the memory of the chip leaving only a handful of free bytes to write the actual code in!
img_1984

Keep in mind this is a PROTOTYPE and that the full version will have over 80 LEDs to represent every number in binary form (up to 2^25, 33.5 million, with 25 LEDs/number). For now, this version (which dumps data to a HD44780 LCD screen) serves as a proof of concept. Powered by an ATTiny2313.

Here’s some video:

Here’s the code responsible:

#define F_CPU 1E6
#include <stdlib.h>
#include <avr/io.h>
#include <math.h>
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"

const unsigned long int primeMax=pow(2,25);
unsigned long int primeLast=2;
unsigned long int primeTest=0;
unsigned int primeDivy=0;

void wait(void);
void init(void);
void updateDisplay(void);
char *toString(unsigned long int);

int main(void){
    init();
    short maybePrime;
    unsigned int i;
    //for(primeTest=3;primeTest<sqrt(primeMax);primeTest=primeTest+2){
    for(primeTest=2;primeTest<sqrt(primeMax);primeTest++){
        maybePrime=1;
        //for (i=3;i<=(sqrt(primeTest)+1);i=i+2){
        for (i=2;i<=(sqrt(primeTest)+1);i++){
            primeDivy=i;
            updateDisplay();
            if (primeTest%primeDivy==0){maybePrime=0;break;}
        }
        if (maybePrime==1){primeLast=primeTest;updateDisplay();}
    }
    return 0;
}

void updateDisplay(void){
    lcd_gotoxy(12,0);lcd_puts(toString(primeLast));
    lcd_gotoxy(5,1);lcd_puts(toString(primeTest));
    lcd_gotoxy(16,1);lcd_puts(toString(primeDivy));
    //_delay_ms(1000);
    return;
}

void init(void){
    lcd_init(LCD_DISP_ON);
    //lcd_clrscr();
    lcd_puts("PRIME IDENTIFICATIONn");
    //lcd_puts("MAX PRIME: ");
    //lcd_puts(toString(primeMax));
    //lcd_puts("nsquare root: ");
    //lcd_puts(toString(sqrt(primeMax)+1));
    _delay_ms(2000);
    lcd_clrscr();
    lcd_puts("LAST PRIME:n");
    lcd_puts("TRY:");
    lcd_gotoxy(14,1);lcd_puts("/");
    return;
}

char *toString(unsigned long int x){
    char s1[8];
    ltoa(x,s1,10);
    return s1;
}

In other news, I’ve seen the new Star Trek movie and found it enjoyable. My wife liked it too (perhaps more than I did). Here’s a relevant news story I found informative:





Additional Resources

After a day of tinkering I finally figured out how to control a HD44780-style LCD display from an ATTiny2313 class ATMEL AVR microcontroller. There are a lot of websites out there claiming to show you how to do this on similar AVRs. I tried about 10 of them and, intriguingly, only one of them worked! I don’t know if it’s user error, or an incompatibility of the ATTiny2313 running code written for an ATMega8, but since it took me so long to get this right I decided to share it on the internet for anyone else having a similar struggle. First, the results:
attiny_2313_lcd_hd44780
You might recognize this LCD panel from some PC parallel port / LCD interface projects I worked on about 5 years ago. It’s a 20-column, 2-row, 8-bit parallel character LCD. This means that ranter than telling each little square to light up to form individual letters, you can just output text to the microcontroller embedded in the display and it can draw the letters, move the cursor, or clear the screen.
attiny_2313_lcd_hd44780_2
As you can see this thing is pretty easy to wire-up to the ATTiny2313. These are the connections I made:

  • LCD1 -> GND
  • LCD2 -> +5V
  • LCD3 (contrast) -> GND
  • LCD4 (RS) -> AVR D0 (pin2)
  • LCD5 (R/W) -> AVR D1 (pin3)
  • LCD6 (ES) -> AVR D2 (pin6)
  • LCD 11-14 (data) -> AVR B0-B3 (pins 12-15)

The code I used to FINALLY allow me to control this LCD from the ATTiny2313 was found on Martin Thomas’ page. [HIS CODE] I included the .h and .c files and successfully ran the following program (with great results) on my AVR. The internal RC clock works, and supposedly any external clock (<8MHz) should work too.

// THIS THE TEST PROGRAM "main.c"
// ATTiny2313 / HD44780 LCD INTERFACE
#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"

int main(void)
{
    int i=0;
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    lcd_puts("ATTiny 2313 LCD Demo");
    lcd_puts("  www.SWHarden.com  ");
    _delay_ms(1000);
    lcd_clrscr();
    for (;;) {
        lcd_putc(i);
        i++;
        _delay_ms(50);
    }
}
// THIS IS PART OF MY INCLUDED "lcd.h"
// THE WIRING CHART DESCRIBED IN THIS BLOG ENTRY
// IS MATCHED TO THE VALUES DISPLAYED BELOW
#define LCD_PORT         PORTB        /**< port for the LCD lines   */
#define LCD_DATA0_PORT   LCD_PORT     /**< port for 4bit data bit 0 */
#define LCD_DATA1_PORT   LCD_PORT     /**< port for 4bit data bit 1 */
#define LCD_DATA2_PORT   LCD_PORT     /**< port for 4bit data bit 2 */
#define LCD_DATA3_PORT   LCD_PORT     /**< port for 4bit data bit 3 */
#define LCD_DATA0_PIN    0            /**< pin for 4bit data bit 0  */
#define LCD_DATA1_PIN    1            /**< pin for 4bit data bit 1  */
#define LCD_DATA2_PIN    2            /**< pin for 4bit data bit 2  */
#define LCD_DATA3_PIN    3            /**< pin for 4bit data bit 3  */
#define LCD_RS_PORT      PORTD     /**< port for RS line         */
#define LCD_RS_PIN       0            /**< pin  for RS line         */
#define LCD_RW_PORT      PORTD     /**< port for RW line         */
#define LCD_RW_PIN       1            /**< pin  for RW line         */
#define LCD_E_PORT       PORTD     /**< port for Enable line     */
#define LCD_E_PIN        2            /**< pin  for Enable line     */

// AND A LITTLE LOWER, I CHANGED THIS LINE TO 4-BIT MODE
#define LCD_FUNCTION_8BIT     0      /*   DB4: set 8BIT mode (0->4BIT mode) */

And some video of the output… Notice how this display can show English (lowercase/uppercase/numbers) as well as the Japanese character set! Sweet!





Additional Resources

UPDATE: I found a method of PC/microcontroller communication which I feel is simpler, easier, and definitely cheaper than this! It’s not good for everything, but worth looking at. It’s a way to communicate with a PC using your sound card and zero components!

Another page HERE has more info on how to do this with an intermediate IC if yours doesn’t have RX/TX pins…

I recently had the desire to be able to see data from an ATMEL AVR microcontroller (the ATTiny2313) for development and debugging purposes. I wanted an easy way to have my microcontroller talk to my PC (and vise versa) with a minimum number of parts. The easiest way to do this was to utilize the UART capabilities of the ATTiny2313 to talk to my PC through the serial port. One problem is that the ATTiny2313(as with most microcontrollers) puts out 5V for “high” (on) and 0V for “low” (off). The RS-232 standard (which PC serial ports use) required -15V for high and +15v for low! Obviously the microcontroller needs some help to achieve this. The easiest way was to use the MAX232 serial level converter which costs about 3 bucks at DigiKey. Note that it requires a few 10uF capacitors to function properly.

Here’s a more general schematic:

I connected my ATTiny2313 to the MAX232 in a very standard way. (photo) MAX232 pins 13 and 14 go to the serial port, and the ATTiny2313 pins 2 and 3 go to the MAX232 pins 12 and 11 respectively. I will note that they used a oscillator value (3.6864MHz) different than mine (9.216MHz).

Determining the speed of serial communication is important. This is dependent on your oscillator frequency! I said I used a 9.216Mhz oscillator. First, a crystal or ceramic oscillator is required over the internal RC oscillator because the internal RC oscillator is not accurate enough for serial communication. The oscillator you select should be a perfect multiple of 1.8432MHz. Mine is 5x this value. Many people use 2x this value (3.6864Mhz) and that’s okay! You just have to make sure your microchip knows (1) to use the external oscillator (google around for how to burn the fuses on your chip to do this) and (2) what the frequency of your oscillator is and how fast it should be sending data. This is done by setting the UBRRL value. The formula to do this is here:

The datasheet of your microcontroller may list a lot of common crystal frequencies, bandwidths, and their appropriate UBRR values. However my datasheet lacked an entry for a 9.216MHz crystal, so I had to do the math myself. I Googled around and no “table” is available! Why not make one? (picture, below). Anyway, for my case I determined that if I set the UBRR value to 239, I could transmit data at 2800 baud (bits/second). This is slow enough to ensure accuracy, but fast enough to quickly dump a large amount of text to a PC terminal.

>>> IMPORTANT UPDATE!

This will make your life easier. The page wormfood.net/avrbaudcalc.php has a chart of common crystals and the baud rates they work best with! Try to pick a combination that provides the least error possible...

This is the bare-minimum code to test out my setup. Just load the code (written in C, compiled with avr-gcc) onto your chip and it’s ready to go. Be sure you set your fuses to use an external oscillator and that you set your UBRRL value correctly.

  

  #include <avr/io.h>  

  #include <avr/interrupt.h>  

  #include <util/delay.h>  

   

 int main (void)  

 {  

   unsigned char data=0;  

   UBRRL = 239;  

   UCSRB = (1 < < RXEN) | (1 << TXEN);  

   UCSRC = (1 < < UCSZ1) | (1 << UCSZ0);  

   for (;;)  

     {  

     if (data>'Z'||data< 'A')  

     {  

       UDR = 10; UDR = 13; data='A';_delay_ms(100);  

     }  

     UDR = data;  

     data += 1;  

     _delay_ms(100);  

     }  

 }  

 

Once you load it, it’s ready to roll! It continuously dumps letters to the serial port. To receive them, open HyperTerminal (on windows, under accessories) or minicom (on Linux, look it up!). Set your baud rate to 2800 (or whatever you selected) and you’re in business. This (picture below) is the output of the microcontroller to HyperTerminal on my PC. Forgive the image quality, I photographed the LCD screen instead of taking a screenshot.

This is the circuit which generates the output of the previous image. I have a few extra components. I have an LED which I used for debugging purposes, and also a switch (labeled “R”). The switch (when pressed) grounds pin 1 of the ATTiny2313 which resets it. If I want to program the chip, I hold “R” down and the PC can program it with the inline programmer “parallel port, straight-through, DAPA style. One cable going into the circuit is for the parallel port programmer, one cable is for the serial port (data transfer), and one is for power (5v which I stole from a USB port).

I hope you found this information useful. Feel free to contact me with any questions you may have, but realize that I’m no expert, and I’m merely documenting my successes chronologically on this website.