// Using the heap for memory allocation |
|
#include "mem_syms.h" |
#define ADDR DEC |
|
void print_heap_stats() { |
Serial.print("Avail mem:"); Serial.println(AVAIL_MEM, DEC); |
Serial.print("Heap size:"); Serial.println(HEAP_SIZE, DEC); |
Serial.print("Heap starts at:"); Serial.println((unsigned int) HEAP_START, ADDR); |
Serial.print("Heap ends at:"); Serial.println( (unsigned int) HEAP_END, ADDR); |
} |
|
void print_array(int a[], unsigned int len) { |
Serial.print("array at "); |
Serial.print( (unsigned int) a, ADDR); |
Serial.print(" has len "); |
Serial.println(len); |
|
if ( ! a ) { |
// oops, no array |
Serial.println("Null pointer!"); |
return; |
} |
|
for (int i=0; i < len; i++ ) { |
Serial.print(a[i], DEC); |
if (i < len-1) { Serial.print(","); } |
} |
Serial.println("."); |
} |
|
// create a new array of ints, of length len, and fill with |
// some data |
int * make_random_array(unsigned int len) { |
Serial.println("make random array"); |
int *a; |
a = (typeof(a)) malloc(sizeof(*a) * len); |
// always check the return code of malloc |
if ( a == 0 ) { |
Serial.println("No memory!"); |
while ( 1 ) {} |
} |
|
for (int i=0; i < len; i++) { |
// note a[i] is just shorthand for *(a+i) |
// where the units of i are sizeof thing pointed to by a |
a[i] = random(0, 100); |
} |
return a; |
} |
|
void setup() |
{ |
Serial.begin(9600); |
|
Serial.println("MEM09 - dynamic arrays"); |
|
unsigned int array_1_len; |
int * array_1; |
|
unsigned int array_2_len; |
int * array_2; |
|
unsigned int array_3_len; |
int * array_3; |
|
print_heap_stats(); |
array_1_len = 20; |
array_1 = make_random_array(array_1_len); |
print_heap_stats(); |
print_array(array_1, array_1_len); |
Serial.println("free 1"); |
free(array_1); |
print_heap_stats(); |
|
array_2_len = 10; |
array_2 = make_random_array(array_2_len); |
print_heap_stats(); |
print_array(array_2, array_2_len); |
|
array_3_len = 10; |
array_3 = make_random_array(array_3_len); |
print_heap_stats(); |
print_array(array_3, array_3_len); |
|
Serial.println("free 2"); |
free(array_2); |
print_heap_stats(); |
|
Serial.println("free 3"); |
free(array_3); |
print_heap_stats(); |
|
} |
|
void loop() { |
} |
// Using the heap for memory allocation |
|
#include "mem_syms.h" |
#define ADDR DEC |
|
void setup() { |
// a pointer to an int |
long int * x_p; |
// a pointer to a char |
char * y_p; |
|
// to keep track of where the heap starts and ends for us |
// there might already be stuff on the heap, which we want to |
// ignore. |
char * hs; |
char * he; |
char * p; |
|
Serial.begin(9600); |
|
Serial.println("MEM07"); |
|
Serial.print("Avail mem:"); Serial.println(AVAIL_MEM, DEC); |
Serial.print("Heap starts at:"); Serial.println((unsigned int) HEAP_START, ADDR); |
Serial.print("Heap ends at:"); Serial.println((unsigned int) HEAP_END, ADDR); |
|
/* allocate memory for an int from the heap and remember its address |
we could say: |
x_p = (int *) malloc(sizeof(int)); |
but then if we changed the type of x_p we would probably forget |
to fix the malloc call and things would break, subtly and horribly |
So instead we use the typeof and sizeof compiler operators |
*/ |
|
Serial.print("Allocate a chunk, size:"); |
Serial.println(sizeof(*x_p)); |
|
hs = HEAP_END; |
x_p = (typeof(x_p)) malloc(sizeof(*x_p)); |
|
// why is this an inefficeint use of memory? |
//X y_p = (typeof(y_p)) malloc(sizeof(*y_p)); |
|
he = HEAP_END; |
|
// if x_p == 0, the null pointer value, then malloc failed |
|
Serial.print("Avail mem:"); Serial.println(AVAIL_MEM, DEC); |
Serial.print("Heap starts at:"); Serial.println((unsigned int) HEAP_START, ADDR); |
Serial.print("Heap ends at:"); Serial.println( (unsigned int) HEAP_END, ADDR); |
|
|
// why did we use 2 bytes from the heap more than we expect for x_p? |
|
*x_p = 0x89abcdef; |
Serial.print("x_p:"); |
Serial.println( (unsigned int) x_p, ADDR); |
Serial.print("*x_p:"); |
Serial.println( (unsigned int) *x_p, HEX); |
|
Serial.print("y_p:"); |
Serial.println( (unsigned int) y_p, ADDR); |
|
//Y *y_p = 'H'; |
|
Serial.print("*y_p:"); |
Serial.println(*y_p); |
|
|
// print the contents of the heap we just allocated from |
|
Serial.print("Heap:"); |
for ( p = hs; p < he; p++) { |
unsigned char c = *p; |
Serial.print( c, HEX ); |
Serial.print(" "); |
} |
Serial.println(); |
|
} |
|
void loop() { |
} |
// idiom to include only once |
#ifndef graphics_line_h |
#define graphics_line_h |
|
/* |
|
Typedefs for manipulating lines on the LCD. |
|
Each line consists of a start point (xs, ys) an end point (xe, ye), and a |
color. We want to put all of these attributes into a single container called a |
struct so that we can move a line around as a single item of data. |
|
We define a new type called graphics_line below. If s is an instance of |
graphics_line, then we can manipulate single as a single piece of data, or we |
can access its fields (attributes) using . notation as follows: |
single.xs |
|
If you have an array list of graphics lines, then you can first get to the |
struct, then access the fields |
list[i].color |
|
If you have a pointer p to an instance of the graphics_line type then the |
whole structure is accessed as |
*p |
while if you want to get to an attribute, you use -> notation |
p->ye |
or |
(*p).ye |
|
*/ |
typedef struct { |
int xs; |
int ys; |
int xe; |
int ye; |
unsigned int color; |
} graphics_line; |
|
#endif |
#include <string.h> |
#include "graphics_line.h" |
#include "assert.h" |
|
#include <Adafruit_ST7735.h> |
#include <Adafruit_GFX.h> |
#include <SPI.h> |
|
/* now we have an array of graphics_line */ |
|
/* remember graphics_line lines[] is the same as |
graphics_line *line, but the [] reminds us that we are passing an |
array, not a single element. |
*/ |
|
#define CS 6 |
#define DC 7 |
#define RST 8 |
|
// Colour definition |
#define BLACK 0x0000 |
|
Adafruit_ST7735 tft = Adafruit_ST7735(CS, DC, RST); |
|
void draw_lines(int num_lines, graphics_line lines[]){ |
for (int i=0; i < num_lines; i++) { |
// note how the . "dot" notation gets the field of the struct |
tft.drawLine(lines[i].xs, lines[i].ys, |
lines[i].xe, lines[i].ye, |
lines[i].color); |
} |
} |
|
// creates num_lines random lines with random colors and returns pointer |
// to the list containing them |
graphics_line * make_random_lines(int num_lines) { |
graphics_line * list; |
|
// a temporary line |
graphics_line tmp; |
|
list = (typeof(list)) malloc(num_lines * sizeof(*list)); |
|
assert(list != 0, 2); |
|
for (int i=0; i < num_lines; i++) { |
tmp.xs = random(0, tft.width()); |
tmp.ys = random(0, tft.height()); |
tmp.xe = random(0, tft.width()); |
tmp.ye = random(0, tft.height()); |
tmp.color = tft.Color565(random(0, 255), random(0, 255), random(0, 255)); |
|
list[i] = tmp; |
} |
|
return list; |
} |
|
|
void setup() { |
int num_lines = 128; |
graphics_line *random_lines; |
|
Serial.begin( 9600 ); |
|
// wait for button press to start |
button_pause("Press button to start"); |
|
tft.initR(INITR_REDTAB); // initialize a ST7735R chip, red tab |
|
Serial.println("Generating lines"); |
random_lines = make_random_lines(num_lines); |
|
Serial.println("Drawing lines"); |
tft.fillScreen(BLACK); |
draw_lines(num_lines, random_lines); |
|
Serial.println("Freeing lines"); |
free(random_lines); |
} |
|
|
void loop() { |
|
} |
#include <Arduino.h> |
/* assert(expr, error_code) |
|
If expr is false, then the program enters a loop in which it keeps |
flashing error_code blinks |
|
*/ |
|
static int assert_pin = 13; |
void assert(int expr, int error_code) { |
if ( expr ) { return; } |
pinMode(13, OUTPUT); |
while(1) { |
digitalWrite(assert_pin, HIGH); |
delay(1000); |
digitalWrite(assert_pin, LOW); |
for (int i=0; i < error_code-1; i++) { |
delay(100); |
digitalWrite(assert_pin, HIGH); |
delay(200); |
digitalWrite(assert_pin, LOW); |
} |
delay(100); |
} |
} |
|
// Attach a pushbutton between ground and pin 9 for pause control |
static int pauseButton = 9; |
|
void button_pause(char *msg) { |
if ( msg ) { |
Serial.print(msg); |
} |
else { |
Serial.print("B?"); |
} |
|
// set pauseButton to INPUT and |
pinMode(pauseButton, INPUT); |
// turn on internal pull up resistor |
digitalWrite(pauseButton, HIGH); |
|
while( digitalRead(pauseButton) ) { delay(200); } |
while( ! digitalRead(pauseButton) ) { delay(200); } |
Serial.println("!"); |
} |
|
#include <string.h> |
#include "assert.h" |
|
#include <Adafruit_ST7735.h> |
#include <Adafruit_GFX.h> |
#include <SPI.h> |
#include <SD.h> |
|
#define SD_CS 5 // Chip select line for SD card |
#define CS 6 |
#define DC 7 |
#define RST 8 |
|
Adafruit_ST7735 tft = Adafruit_ST7735(CS, DC, RST); |
|
Sd2Card card; |
|
/* The 1.8" LCD has a uSD (micro SD) card slot, which can be used to store |
data, images, or graphics programs. |
|
A SD card is organized as blocks of 512 bytes. You cannot read or write |
individual bytes, an entire block must be moved in and out. So what this |
means is that you need a 512 byte buffer for I/O. Even though there are |
byte address commands available, they still cause blocks to move, and so |
are very expensive, and cause needless writes to the card. |
|
Also, writes are much more expensive than reads, so if you do not need to |
write anything, don't. |
|
Some displays recognize that the uSD card might have a |
FAT file system on them, and so reads and writes the data in a form that |
allows you to also read and write to the card as if it was a memory stick. |
Not for our screen, so you will not be able to mount this card in your own |
machine. But, you can still access the raw data using the dd command in |
OSX and Linux. |
|
To address a block on the card you need to provide a sector address, which |
is 3 bytes long. This means that a sector addres ranges |
0 .. 16777215, or 0x0 .. 0xffffff |
first. |
|
The Sd2card.h header file defines various useful types for accessing |
the memory card: |
unsigned char - sector address |
and these basic operations to read and write a block |
uint8_t readBlock(uint32_t block, uint8_t* dst); |
uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src); |
|
*/ |
|
#define BLOCK_SIZE 512 |
unsigned char out_buf[BLOCK_SIZE]; |
unsigned char in_buf[BLOCK_SIZE]; |
|
uint32_t start_time; |
uint32_t stop_time; |
|
|
|
void start_clock() { |
start_time = millis(); |
} |
|
uint32_t stop_clock() { |
unsigned long elapsed_time; |
stop_time = millis(); |
elapsed_time = stop_time - start_time; |
Serial.print("Elapsed time "); |
Serial.println(elapsed_time); |
return elapsed_time; |
} |
|
void setup() { |
|
/* write at the 1G mark so we skip the useful data on the beginning |
of the card */ |
uint32_t start_sector; |
uint32_t end_sector; |
uint32_t cur_sector; |
|
Serial.begin( 9600 ); |
|
// wait for button press to start |
button_pause("Press button to start"); |
|
tft.initR(INITR_REDTAB); // initialize a ST7735R chip, red tab |
|
// initialize memory card |
if (!card.init(SPI_HALF_SPEED, SD_CS)) { |
Serial.println("initialization failed. Things to check:"); |
Serial.println("* is a card is inserted?"); |
Serial.println("* Is your wiring correct?"); |
return; |
} else { |
Serial.println("Wiring is correct and a card is present."); |
} |
|
// Select starting and end sector |
start_sector = 2097152; |
end_sector = start_sector + 32; |
cur_sector = start_sector; |
|
for (cur_sector = start_sector; cur_sector < end_sector; cur_sector++) { |
|
// write a block of data to the SD card |
for (int i=0; i < BLOCK_SIZE; i++) { |
out_buf[i] = random(0, 256); |
} |
|
Serial.print("Writing block "); |
Serial.println(cur_sector); |
|
start_clock(); |
card.writeBlock(cur_sector, out_buf); |
stop_clock(); |
|
// read it back for comparison |
Serial.print("Reading block "); |
Serial.println(cur_sector); |
|
start_clock(); |
card.readBlock(cur_sector, in_buf); |
stop_clock(); |
|
// compare what we wrote to the SD card and what we just read |
for (int i=0; i < BLOCK_SIZE; i++) { |
if ( in_buf[i] != out_buf[i] ) { |
Serial.print(" Byte mismatch "); |
Serial.print( i ); |
Serial.print(" in:"); |
Serial.print( in_buf[i], HEX ); |
Serial.print(" out:"); |
Serial.println( out_buf[i], HEX ); |
|
button_pause("Continue?"); |
|
break; |
} |
} |
} |
} |
|
void loop() { |
|
} |