Microcontroller Programming
Alright, this is getting written at 3AM after hours and hours of
tears. I've had problems, trials and tribulations, small
triumphs, only to be dashed by cruel, cruel microcontroller
behavior. I will detail my problems and solutions at a later
date. Right now, I'm going to list my code (because it looks
impressive) and tell you what I (less impressively) was able to
accomplish for today. Updates will be appended at the bottom as I
get my stuff in order.
Also, refer to the PCB making for more info on my coding setup. PCB
So I wanted to code checkers, where you actually played the
computer. This is coded, and some of it is debugged. It's a
pretty simple intelligence, but it will make a good move without
looking ahead. I still need to code some behavior, like kinging,
and not moving forward down the board like some stupid lemming.
However, I don't have enough memory on the tiny45 to do it, by like
10%, and my atmega88 board still needs some work as of 3AM the day
before the assignment is due. So postponement basically.
What I did get done is to have the board print out, and you get to make
a move, and that move is reflected in the board. So you could
play yourself. This is just an expedient substitute until the
rest of it gets working. You can't i.e. make a jump, hell you can
only make one move really because I haven't coded it to receive an
array of chars or ints, i.e. the number 10 is an array of length
2. So the only move it can make where you type it in is from
square 5 to 9. This will be changed, it's just been a lot of
coding and I didn't quite get there. Again updates appended on
the bottom. Right now is Oct 20. Below I have pictured a
random initialization of the gameboard, which is a collection of three
32-bit ints (white, black, kings) mapped to a gameboard. Refer to
the gui() method in the code at the bottom. There's a lot of
bitwise operations and tedious stuff. Some of this was adapted
from an earlier undergraduate assignment I coded having to do with
chess. The c file itself is in the directory, so it can be opened
formatted.

In that screenshot, and in the screenshot below, there's some
artifacting which omits parts of the gameboard. In real life, it
looks pretty decent. term.py doesn't do a very good job of
formatting character widths, so things don't line up perfectly in
places, but for a textual gui, it works ok.

This is the interactive part. It moved from a fresh board to
having made a move, based on what you typed in. I haven't coded
arrayed input and I'm having some typecasting issues between char and
int (uint8_t), so right now that is the only move possible, but that's
easily fixed if I had more time. For reference if it's hard to
see, it moved from square 5 to 9. Once I get arrayed input, you
could like play yourself a game of checkers, but hopefully I can just
skip that and go directly to playing a game against the computer.
We'll see how things go.
//
//
// Checkers.c from hello.echo.45.c
//
// 9600 baud serial echo hello-world program
//
// Neil Gershenfeld
// CBA MIT 10/5/08
//
// (c) Massachusetts Institute of Technology 2008
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//
//
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdlib.h>
#define tx_pin PB2 // transmit pin
#define rx_pin PB3 // receive pin
#define led_pin PB4 // LED pin
#define bit_delay_time 104 // bit delay, 1/9600 in usec for 8 MHZ/8
#define char_delay_time 1 // char delay time, in ms
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit
delay
#define char_delay() _delay_ms(char_delay_time) // RS232 char delay
#define output(pin) (DDRB |= byte(pin)) // set pin for output
#define set(pin) (PORTB |= byte(pin)) // set pin in PORTB
#define clear(pin) (PORTB &= ~(byte(pin))) // clear pin in PORTB
#define byte(bit) (1 << bit) // byte with bit set
#define bit_get(byte,bit) (byte & (1 << bit)) // test for bit
set
#define pin_test(bit) (PINB & (1 << bit)) //test for pin test
#define bit_test(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))
#define BIT(x) (0x01 << (x))
typedef long bitboard;
typedef uint8_t uint8;
typedef enum {
empty,
white,
black,
white_king,
black_king
} piece;
typedef struct {
bitboard white_pieces;
bitboard black_pieces;
bitboard kings;
bool white_turn;
} gamestate;
typedef struct {
uint8 src;
uint8 dest;
uint8 captured[2];
} move;
uint8 i;
uint8 j;
uint8 k;
uint8 cap_col;
uint8 cap_row;
uint8 cap_pos;
uint8 start_row;
uint8 start_col;
uint8 start_pos;
uint8 end_row;
uint8 end_col;
uint8 end_pos;
move mv;
uint8 capture[2];
uint8 num_captures = 0;
gamestate current_state;
uint8 get_int() {
//
// read and return a character
// assumes no line driver (doesn't invert bits)
//
uint8 rxbyte=0;
while (! pin_test(rx_pin))
//
// wait for start bit
//
;
//
// delay to middle of first data bit
//
half_bit_delay();
bit_delay();
//
// read data bits
//
if pin_test(rx_pin)
rxbyte |= (0 << 0);
else
rxbyte |= (1 << 0);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 1);
else
rxbyte |= (1 << 1);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 2);
else
rxbyte |= (1 << 2);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 3);
else
rxbyte |= (1 << 3);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 4);
else
rxbyte |= (1 << 4);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 5);
else
rxbyte |= (1 << 5);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 6);
else
rxbyte |= (1 << 6);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 7);
else
rxbyte |= (1 << 7);
return rxbyte;
}
char get_char() {
//
// read and return a character
// assumes no line driver (doesn't invert bits)
//
unsigned char rxbyte=0;
while (! pin_test(rx_pin))
//
// wait for start bit
//
;
//
// delay to middle of first data bit
//
half_bit_delay();
bit_delay();
//
// read data bits
//
if pin_test(rx_pin)
rxbyte |= (0 << 0);
else
rxbyte |= (1 << 0);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 1);
else
rxbyte |= (1 << 1);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 2);
else
rxbyte |= (1 << 2);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 3);
else
rxbyte |= (1 << 3);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 4);
else
rxbyte |= (1 << 4);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 5);
else
rxbyte |= (1 << 5);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 6);
else
rxbyte |= (1 << 6);
bit_delay();
if pin_test(rx_pin)
rxbyte |= (0 << 7);
else
rxbyte |= (1 << 7);
return rxbyte;
}
void put_char(char txchar) {
//
// print the character in txchar
// assumes no line driver (doesn't invert bits)
//
// start bit
//
set(tx_pin);
bit_delay();
//
// data bits
//
if bit_get(txchar,0)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
if bit_get(txchar,1)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
if bit_get(txchar,2)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
if bit_get(txchar,3)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
if bit_get(txchar,4)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
if bit_get(txchar,5)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
if bit_get(txchar,6)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
if bit_get(txchar,7)
clear(tx_pin);
else
set(tx_pin);
bit_delay();
//
// stop bit
//
clear(tx_pin);
bit_delay();
}
void print_string(char *str) {
//
// print the null-terminated program memory string str
//
while (pgm_read_byte(str) != 0x00)
put_char(pgm_read_byte(str++));
char_delay();
}
void blink() {
//
// blink the LED
//
set(led_pin);
_delay_ms(10);
clear(led_pin);
}
piece get_piece(uint8 position)
{
if (bit_test(current_state.kings, LONGBIT(position - 1))) {
if (bit_test(current_state.white_pieces,
LONGBIT(position - 1)))
return white_king;
else
return black_king;
}
else {
if (bit_test(current_state.white_pieces,
LONGBIT(position - 1)))
return white;
else if
(bit_test(current_state.black_pieces, LONGBIT(position - 1)))
return black;
else return empty;
}
}
void set_piece(uint8 position, piece type) {
if (type == empty) {
bit_clear(current_state.white_pieces,
LONGBIT(position - 1));
bit_clear(current_state.black_pieces,
LONGBIT(position - 1));
bit_clear(current_state.kings,
LONGBIT(position - 1));
return;
}
if (type == white_king || type == black_king)
bit_set(current_state.kings,
LONGBIT(position - 1));
if (type == white || type == white_king)
bit_set(current_state.white_pieces,
LONGBIT(position - 1));
else
bit_set(current_state.black_pieces,
LONGBIT(position - 1));
}
void move_piece(uint8 position_from, uint8 position_to) {
set_piece(position_to, get_piece(position_from));
set_piece(position_from, empty);
}
/*
void (uint8 position) {
piece current;
current = get_piece(position);
if (current == white)
set_piece(position, white_king);
else if (current == black)
set_piece(position, black_king);
}
*/
void capture_piece(uint8 position) {
set_piece(position, empty);
}
//char guimessage[] PROGMEM = "Hope that you can read this:\n";
void guitest() {
while (1) {
print_string("But can you read
this?\n");
_delay_ms(1000);
}
}
char gui1[] PROGMEM =
"\n 1";
char gui2[] PROGMEM =
"\n|-------|-------|-------|-------|-------|-------|-------|-------|\n|";
char gui3[] PROGMEM =
" | WK |";
char gui4[] PROGMEM =
" | W |";
char gui5[] PROGMEM =
" | BK |";
char gui6[] PROGMEM =
" | B |";
char gui7[] PROGMEM =
" | E |";
char gui8[] PROGMEM = " WK
| |";
char gui9[] PROGMEM = "
W | |";
char gui10[] PROGMEM = " BK
| |";
char gui11[] PROGMEM = " B
| |";
char gui12[] PROGMEM = " E
| |";
char gui13[] PROGMEM = " 29\n\n";
void gui() {
uint8 ccounter = 0; //these counters are a little bit ugly
uint8 rcounter = 0;
uint8 rswitch = 0;
//print_string("\r
1\r");
print_string(gui1);
print_string(gui2);
//print_string("\r|----|----|----|----|----|----|----|----|\r|");
for (i=0; i<32; i++) {
if (ccounter == 4) {
//print_string("\r|----|----|----|----|----|----|----|----|\r|");
print_string(gui2);
ccounter = 0;
rcounter = 1;
}
else if (rswitch == 4) {
//print_string("\r|----|----|----|----|----|----|----|----|\r|");
print_string(gui2);
rswitch = 0;
rcounter = 0;
}
if (rcounter == 0)
{
if
(bit_test(current_state.white_pieces,LONGBIT(i))) {
if
(bit_test(current_state.kings,LONGBIT(i)))
//print_string(" | WK |");
print_string(gui3);
else
//print_string(" | W |");
print_string(gui4);
}
else if
(bit_test(current_state.black_pieces,LONGBIT(i))) {
if
(bit_test(current_state.kings, LONGBIT(i)))
//print_string(" | BK |");
print_string(gui5);
else
//print_string(" | B |");
print_string(gui6);
}
else
//print_string(" | E |");
print_string(gui7);
ccounter++;
}
else if (rcounter == 1) {
if
(bit_test(current_state.white_pieces,LONGBIT(i))) {
if
(bit_test(current_state.kings,LONGBIT(i)))
//print_string(" WK | |");
print_string(gui8);
else
//print_string(" W | |");
print_string(gui9);
}
else if
(bit_test(current_state.black_pieces, LONGBIT(i))) {
if
(bit_test(current_state.kings, LONGBIT(i)))
//print_string(" BK | |");
print_string(gui10);
else
//print_string(" B | |");
print_string(gui11);
}
else //print_string("
E | |");
print_string(gui12);
rswitch++;
}
}
print_string(gui2);
print_string(gui13);
}
/*
signed short evaluate() {
signed short quad_white = 0;
signed short quad_black = 0;
uint8 wcount = 0;
uint8 bcount = 0;
for (i=1; i<=8; i++) {
for (j=0; j<4; j++) {
if
(!bit_test(current_state.kings,LONGBIT((i-1)*4+j))) {
if
(bit_test(current_state.white_pieces,LONGBIT((i-1)*4+j)))
wcount++;
else if
(bit_test(current_state.black_pieces,LONGBIT((i-1)*4+j)))
bcount++;
}
else {
if
(bit_test(current_state.white_pieces,LONGBIT((i-1)*4+j))) {
quad_white += 10;
if (j == 1 || j
== 4)
quad_white -= 2;
}
else {
quad_black += 10;
if (j == 1 || j
== 4)
quad_black -= 2;
}
}
}
quad_white += (8-i)*(8-i)*wcount;
quad_black += i*i*bcount;
wcount = 0;
bcount = 0;
}
return quad_white - quad_black;// + (rand_num-0.5)*10;
}*/
uint8 isvalidmove(uint8 start_pos, uint8 end_pos, uint8 start_row,
uint8 start_col, uint8 end_row, uint8 end_col) {
//method returns 3 different states, 0 for illegal
move, 1 for legal move, and end_pos for incomplete move (meaning
another jump may be possible)
if
(bit_test(current_state.white_pieces,LONGBIT(end_pos)) ||
bit_test(current_state.black_pieces,LONGBIT(end_pos))) //checks if
end_pos is empty
return 0;
//not sure if implementing the following if
statement will end up being necessary
if
(!bit_test(current_state.white_pieces,LONGBIT(start_pos)) &&
!bit_test(current_state.black_pieces,LONGBIT(start_pos)))
return 0;
if
(bit_test(current_state.white_pieces,LONGBIT(start_pos)) &&
!current_state.white_turn)
return 0;
if
(bit_test(current_state.black_pieces,LONGBIT(start_pos)) &&
current_state.white_turn)
return 0;
if (start_col - end_col == 1 || end_col - start_col
== 1) { //one square diagonally (abs method didn't want to work here)
if
(bit_test(current_state.white_pieces, LONGBIT(start_pos)) &&
current_state.white_turn) { //test white pieces first
if
(end_row - start_row == -1) //note the numerical layout of the
board, which causes sign
return 1; //if doesn't cause capture,
and only jumps one space diagonal forward, return true
if (end_row -
start_row == 1 && bit_test(current_state.kings,
LONGBIT(start_pos)))
return 1;
}
else if
(bit_test(current_state.black_pieces, LONGBIT(start_pos)) &&
!current_state.white_turn) { //now test black pieces
if (end_row -
start_row == 1)
return 1;
if (end_row -
start_row == -1 && bit_test(current_state.kings,
LONGBIT(start_pos)))
return 1;
}
}
else if(start_col - end_col == 2 || start_col -
end_col == -2) { //now see about capture moves Note: this
method only does one capture, then calls another method to recur
//again, abs wasn't working for above
cap_col = (start_col + end_col)/2;
cap_row = (start_row + end_row)/2;
//converting back to 1-32 board
representation
if (cap_row == 1 || cap_row == 3
|| cap_row == 5 || cap_row == 7)
cap_pos =
(cap_row - 1)*4 + cap_col/2;
else
cap_pos =
(cap_row - 1)*4 + 1 + (cap_col-1)/2;
if (start_row - end_row != 2
&& start_row - end_row != -2)
return 0;
else
if(bit_test(current_state.white_pieces, LONGBIT(start_pos)) &&
current_state.white_turn) {
if(!bit_test(current_state.black_pieces, LONGBIT(cap_pos-1)))
//eliminate no black pieces in captured square (also a jump of 3 rows)
return 0;
if(!bit_test(current_state.kings, LONGBIT(start_pos))) { //eliminate
pawns jumping backwards, or 5 rows
if(end_row - start_row != -2)
return 0;
}
}
}
else if(bit_test(current_state.black_pieces,
LONGBIT(start_pos)) && !current_state.white_turn) {
if(!bit_test(current_state.white_pieces, LONGBIT(cap_pos-1)))
return 0;
if(!bit_test(current_state.kings, LONGBIT(start_pos))) {
if(end_row - start_row != 2)
return 0;
}
}
return end_pos; //used to be return 2 i.e. another
jump may be possible
}
uint8 row(uint8 index) {
uint8 start_row = 1;
if (bit_test(j, BIT(2)))
start_row += 1;
if (bit_test(j, BIT(3)))
start_row += 2;
if (bit_test(j, BIT(4)))
start_row += 4;
return start_row;
}
uint8 col(uint8 index) {
uint8 start_col = 1;
//these should be listed for 8x8 #s
if(!bit_test(j, BIT(2))) //set which includes
first row in decimal format
start_col += 1;
if(bit_test(j, BIT(1)))
start_col += 4;
if(bit_test(j, BIT(0)))
start_col += 2;
return start_col;
}
/*
void best_move() {
//uint8 cap;
//uint8 temp;
uint8 test;
uint8 test2;
signed short eval;
signed short best = 0;
for (j=0; j<32; j++) { //all start positions
start_pos = j;
start_col = col(j);
start_row = row(j);
for (k=0; k<32; k++)
{ //all end positions
num_captures = 0;
end_pos = k;
end_col = col(k);
end_row = row(k);
test
= isvalidmove(start_pos,end_pos,start_row,start_col,end_row,end_col);
if
(test == 0)
continue;
if
(test != 1) { //one jump possible, now look for a second
cap_col = (start_col + end_col)/2;
cap_row = (start_row + end_row)/2;
//converting back to 1-32 board representation NOTE:
NOT 0-31, in contrast to start_pos
if (cap_row == 1 || cap_row == 3 || cap_row == 5 ||
cap_row == 7)
cap_pos = (cap_row - 1)*4 +
cap_col/2;
else
cap_pos = (cap_row - 1)*4 + 1 +
(cap_col-1)/2;
capture[num_captures++] = cap_pos;
//now change values to those for next possible jump
start_row = end_row;
start_col = end_col;
end_row = row(test);
end_col = col(test);
test2 = isvalidmove(end_pos, test, start_row,
start_col, end_row, end_col);
if (test2 != 1 && test2 != 0) { //second
jump, stop here due to memory limitation (and no recursion)
cap_col = (start_col + end_col)/2;
cap_row = (start_row + end_row)/2;
//converting back to 1-32 board
representation NOTE: NOT 0-31, in contrast to start_pos
if (cap_row == 1 || cap_row == 3
|| cap_row == 5 || cap_row == 7)
cap_pos =
(cap_row - 1)*4 + cap_col/2;
else
cap_pos =
(cap_row - 1)*4 + 1 + (cap_col-1)/2;
capture[num_captures++] = cap_pos;
end_pos = test2;
}
}
//test = 1
just shorts to here
move_piece(start_pos, end_pos); //start_col, start_row, end_col,
end_row get changed around, but not the pos variables
eval =
evaluate();
move_piece(end_pos, start_pos); //undo the speculative move
if
(current_state.white_turn) {
if (eval > best) {
best = eval;
mv.src = start_pos;
mv.dest = end_pos;
for (i=0; i<num_captures; i++)
mv.captured[i]
= capture[i];
}
}
else {
//for black this number is negative
if (eval < best) {
best = eval;
mv.src = start_pos;
mv.dest = end_pos;
for (i=0; i<num_captures; i++)
mv.captured[i]
= capture[i];
}
}
}
}
move_piece(mv.src, mv.dest);
for (i=0; i<num_captures; i++)
capture_piece(mv.captured[i]);
}
*/
char message1[] PROGMEM = "Your Move Starting Square# (1-32):";
char message2[] PROGMEM = "Your Move Ending Square# (1-32):";
char init[] PROGMEM = "Initializing game, refer to gui, you're black,
get the first move\n";
char update[] PROGMEM = "45's move:";
char message3[] PROGMEM = "Separation\n";
char message3[] PROGMEM = "Move is indeed a valid move\n";
int main(void) {
uint8 rxbyte1;
uint8 rxbyte2;
uint8 counter = 0;
uint8 test = 0;
bitboard white_pieces =
0b11111111000000000000000000000000; //last bit is the first
square in decimal
bitboard black_pieces =
0b00000000000000000000000011111111;
bitboard kings =
0b00000000000000000000000000000000;
current_state.white_pieces = white_pieces;
current_state.black_pieces = black_pieces;
current_state.kings = kings;
output(tx_pin);
output(led_pin);
clear(tx_pin);
while(counter < 6 ) { //deals with the gibberish
that gets printed.
print_string(message3);
_delay_ms(1000);
counter++;
}
//guitest();
gui();
//print_string(init);
//print_string("Initializing game, refer to gui,
you're white, get the first move");
while (1) {
print_string(message1);
//rxbyte1 = get_int();
rxbyte1 = get_char();
//put_char((char)rxbyte1);
put_char(rxbyte1);
put_char('\n');
blink();
print_string(message2);
//rxbyte2 = get_int();
rxbyte2 = get_char();
//put_char((char)rxbyte2);
put_char(rxbyte2);
put_char('\n');
blink();
move_piece((uint8)rxbyte1,
(uint8)rxbyte2); //casting here isn't doing anything yet
move_piece(5,9);
if isvalidmove(rxbyte1, rxbyte2,
row(rxbyte1), col(rxbyte1), row(rxbyte2), col(rxbyte2))
print_string(message4);
//move_piece(rxbyte1, rxbyte2);
//print_string("45's moved");
put_char('\n');
//best_move();
gui();
}
}
/*char message[] PROGMEM = "rcvd char:";
int main(void) {
unsigned char rxbyte;
output(tx_pin);
output(led_pin);
clear(tx_pin);
while (1) {
rxbyte = get_char();
print_string(message);
put_char(rxbyte);
put_char('\n');
blink();
}
}*/