Prime-ary Prototype: Complete!
596 words | Posted on June 10th, 2009
Scott was 23.71 years old when he wrote this!
Filed under: C/C++, Circuitry, General, Microcontrollers, Prime Numbers
For the last several weeks I’ve been hacking together a small prototype of a microcontroller (ATTiny2313)-powered prime number generator. I can finally say that it (the prototype) is complete, functional, and elegant [get the song I'm referencing while it's up!]. The name says what it does, so I won’t waste time describing it. If you’re interested in how it does what it does, check out the other posts with the same category tag for a full (and I mean full as in “more than you ever wanted to know”) description. A schematic is soon to come. Code is below. For now, here’s a video of the completed project:
And the source code I used. I chose the ATTiny2313 because it was cheap (~$2) and has 18 IO pins to work with. I had to fight memory usage every step of the way. I’m limited to 2kb, and this program compiles within 1% of that! For future projects (more LEDs, more menus) I’ll use the 20-pin and/or 40-pin ATMega series. I’m thinking about having one be in charge of the display (multiplexing) leaving the other to think about generating prime numbers.
#define F_CPU 10240000
#include <avr/interrupt.h>
#include <util/delay.h>
#include <math.h>
// ~~~ PORT D ~~~
#define r1 0b00000100
#define r2 0b00000010
#define r3 0b00001000
#define r4 0b00100000
#define r5 0b00010000
#define f1 0b01000000
#define id 0b00000001
#define f0 0b10111111
// ~~~ PORT B ~~~
#define c1 0b00010000
#define c2 0b00000001
#define c3 0b00000010
#define c4 0b00000100
#define c5 0b00001000
char delay=1;
int redo=0;
char rows[] = {r1,r2,r3,r4,r5};
char cols[] = {c1,c2,c3,c4,c5};
char vals[] = {0,0,0,0,0};
char funcs=f1+id;
unsigned long showNum=5;//33554431;
void displayNum();
void incrimentNum();
void menu_pause();
void menuCheck();
char button();
char isPrime(unsigned long);
unsigned int twoTo(char);
int main(void) {
DDRD = r1+r2+r3+r4+r5+f1+id;
DDRB = c1+c2+c3+c4+c5;
DDRB &= ~_BV(DDB7);
PORTB = 0;
PORTD = 0;
unsigned int i=0;
int j;
//convertNum();
//showNum=5;
while (1) {
showNum+=1;
if (isPrime(showNum)){
convertNum();
}
for (j=0;j<redo;j++) {
displayNum();
menu();
}
if (i%10==0){funcs^=r1;
funcs^=id;
if (i%100==0){funcs^=r2;
if (i%1000==0){funcs^=r3;i=0;
}
}
}
i++;
}
return 0;
}
char isPrime(unsigned long test){
if (test%2==0) return 0;
unsigned long div = 3;
while(div*div<test+1){
if (test%div==0) return 0;
div+=2;
displayNum();
}
return 1;
}
void menu(){
//return;
char j,but;
but = button();
if (but==0) return;
else if (but==1){
while (1) {
PORTD=id;
if (PINB & _BV(PB7)) {
funcs=f1;
for (j=0;j<200;j++){
if (j%25==0) funcs^=r1;
displayNum();
}
}
else {
if (redo==0) redo=200;
else redo=0;
_delay_ms(300);
return;
}
}
}
return;
}
char button(){
PORTD=id;
if (PINB & _BV(PB7)) return 0; // not pressed
_delay_ms(1000);
if (PINB & _BV(PB7)) return 1; // pressed
return 2; // held down
}
void convertNum(){
char col,row,rowcode;
unsigned long msk=1;
for (col=0;col<5;col++){
rowcode=0;
for (row=0;row<5;row++){
if (showNum&msk) rowcode+=rows[row];
msk = msk < < 1;
}
vals[col]=rowcode;
}
return;
}
void displayNum(){
char i,j;
PORTB = 0b0;
PORTD = funcs;
_delay_ms(delay);
for (i=0;i<5;i++){
PORTD = vals[i];
PORTB = cols[i];
_delay_ms(delay);
}
return;
}
This entry was posted on Wednesday, June 10th, 2009 at 7:16 pmand is filed under C/C++, Circuitry, General, Microcontrollers, Prime Numbers. You can follow any responses to this entry through the RSS 2.0 feed. You can skip to the end and leave a response. Pinging is currently not allowed.
4 Responses to “Prime-ary Prototype: Complete!”
| Daniel Holth wrote the following at 09:02:25 AM on August 24th, 2010 |
|
I’m not the only one eagerly awaiting the 4kb successor to the ATtiny2313. Most of your code size is probably from linking in math libraries. You might be able to change your algorithm slightly to avoid doing that. |
| Yann Vernier wrote the following at 12:06:43 PM on August 24th, 2010 |
|
Actually, the code doesn’t seem to use anything from math.h/libm (such as, for instance, sqrt). I would guess most of the code size comes from how it has been compiled; I tend to use avr-gcc -Os -fwhole-program. The costly calls in there are multiplication (the tiny2313 doesn’t have a multiplier), division and modulo (32-bit, no less), and, if you have an unfortunate combination of avr-libc and compiler settings, the delay calls themselves. |
| Yann Vernier wrote the following at 12:20:35 PM on August 24th, 2010 |
|
Found the culprit. The delay functions are meant to be called with constant values, and delay was declared as a variable. With -fwhole-program, gcc could tell it was never written. Adding “const” to the declaration char delay=1; caused the code to drop to under 1k. Thus it really did generate floating point code, for a single constant 1, no less. |
| Kenneth Finnegan wrote the following at 01:54:32 AM on August 28th, 2010 |
|
Very nice, but as far as your display, I think you should invest a little time into learning how to use the timer interrupts on the AVRs. It makes for much easier coding, generally smaller code size (the function calls are implicit) and is one of the first steps towards low power consumption programming (replacing the idle loops with sleep calls). I wrote up a relatively simple example using this a few months ago: http://kennethfinnegan.blogspot.com/2010/03/attiny2313-four-digit-clock.html |
