ADNS2051 Library Program Demonstration
Dependencies: ADNS2051lib FatFileSystem mbed MI0283QTlib
Revision 0:b560d4d15292, committed 2012-05-28
- Comitter:
- clemente
- Date:
- Mon May 28 20:50:49 2012 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ADNS2051lib.lib Mon May 28 20:50:49 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/clemente/code/ADNS2051lib/#462017ee2a5b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FatFileSystem.lib Mon May 28 20:50:49 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/AdamGreen/code/FatFileSystem/#6ceefe1c53e4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MI0283QTlib.lib Mon May 28 20:50:49 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/clemente/code/MI0283QTlib/#7ad454fed160
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDFileSystem.cpp Mon May 28 20:50:49 2012 +0000 @@ -0,0 +1,457 @@ +/* mbed SDFileSystem Library, for providing file access to SD cards + * Copyright (c) 2008-2010, sford + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Introduction + * ------------ + * SD and MMC cards support a number of interfaces, but common to them all + * is one based on SPI. This is the one I'm implmenting because it means + * it is much more portable even though not so performant, and we already + * have the mbed SPI Interface! + * + * The main reference I'm using is Chapter 7, "SPI Mode" of: + * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + * + * SPI Startup + * ----------- + * The SD card powers up in SD mode. The SPI interface mode is selected by + * asserting CS low and sending the reset command (CMD0). The card will + * respond with a (R1) response. + * + * CMD8 is optionally sent to determine the voltage range supported, and + * indirectly determine whether it is a version 1.x SD/non-SD card or + * version 2.x. I'll just ignore this for now. + * + * ACMD41 is repeatedly issued to initialise the card, until "in idle" + * (bit 0) of the R1 response goes to '0', indicating it is initialised. + * + * You should also indicate whether the host supports High Capicity cards, + * and check whether the card is high capacity - i'll also ignore this + * + * SPI Protocol + * ------------ + * The SD SPI protocol is based on transactions made up of 8-bit words, with + * the host starting every bus transaction by asserting the CS signal low. The + * card always responds to commands, data blocks and errors. + * + * The protocol supports a CRC, but by default it is off (except for the + * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) + * I'll leave the CRC off I think! + * + * Standard capacity cards have variable data block sizes, whereas High + * Capacity cards fix the size of data block to 512 bytes. I'll therefore + * just always use the Standard Capacity cards with a block size of 512 bytes. + * This is set with CMD16. + * + * You can read and write single blocks (CMD17, CMD25) or multiple blocks + * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When + * the card gets a read command, it responds with a response token, and then + * a data token or an error. + * + * SPI Command Format + * ------------------ + * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. + * + * +---------------+------------+------------+-----------+----------+--------------+ + * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | + * +---------------+------------+------------+-----------+----------+--------------+ + * + * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) + * + * All Application Specific commands shall be preceded with APP_CMD (CMD55). + * + * SPI Response Format + * ------------------- + * The main response format (R1) is a status byte (normally zero). Key flags: + * idle - 1 if the card is in an idle state/initialising + * cmd - 1 if an illegal command code was detected + * + * +-------------------------------------------------+ + * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | + * +-------------------------------------------------+ + * + * R1b is the same, except it is followed by a busy signal (zeros) until + * the first non-zero byte when it is ready again. + * + * Data Response Token + * ------------------- + * Every data block written to the card is acknowledged by a byte + * response token + * + * +----------------------+ + * | xxx | 0 | status | 1 | + * +----------------------+ + * 010 - OK! + * 101 - CRC Error + * 110 - Write Error + * + * Single Block Read and Write + * --------------------------- + * + * Block transfers have a byte header, followed by the data, followed + * by a 16-bit CRC. In our case, the data will always be 512 bytes. + * + * +------+---------+---------+- - - -+---------+-----------+----------+ + * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | + * +------+---------+---------+- - - -+---------+-----------+----------+ + */ + +#include "SDFileSystem.h" + +#define SD_COMMAND_TIMEOUT 5000 + +SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name) : + FATFileSystem(name), _spi(mosi, miso, sclk), _cs(cs) { + _cs = 1; +} + +#define R1_IDLE_STATE (1 << 0) +#define R1_ERASE_RESET (1 << 1) +#define R1_ILLEGAL_COMMAND (1 << 2) +#define R1_COM_CRC_ERROR (1 << 3) +#define R1_ERASE_SEQUENCE_ERROR (1 << 4) +#define R1_ADDRESS_ERROR (1 << 5) +#define R1_PARAMETER_ERROR (1 << 6) + +// Types +// - v1.x Standard Capacity +// - v2.x Standard Capacity +// - v2.x High Capacity +// - Not recognised as an SD Card + +#define SDCARD_FAIL 0 +#define SDCARD_V1 1 +#define SDCARD_V2 2 +#define SDCARD_V2HC 3 + +int SDFileSystem::initialise_card() { + // Set to 100kHz for initialisation, and clock card with cs = 1 + _spi.frequency(100000); + _cs = 1; + for(int i=0; i<16; i++) { + _spi.write(0xFF); + } + + // send CMD0, should return with all zeros except IDLE STATE set (bit 0) + if(_cmd(0, 0) != R1_IDLE_STATE) { + fprintf(stderr, "No disk, or could not put SD card in to SPI idle state\n"); + return SDCARD_FAIL; + } + + // send CMD8 to determine whther it is ver 2.x + int r = _cmd8(); + if(r == R1_IDLE_STATE) { + return initialise_card_v2(); + } else if(r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) { + return initialise_card_v1(); + } else { + fprintf(stderr, "Not in idle state after sending CMD8 (not an SD card?)\n"); + return SDCARD_FAIL; + } +} + +int SDFileSystem::initialise_card_v1() { + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + _cmd(55, 0); + if(_cmd(41, 0) == 0) { + return SDCARD_V1; + } + } + + fprintf(stderr, "Timeout waiting for v1.x card\n"); + return SDCARD_FAIL; +} + +int SDFileSystem::initialise_card_v2() { + + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + _cmd(55, 0); + if(_cmd(41, 0) == 0) { + _cmd58(); + return SDCARD_V2; + } + } + + fprintf(stderr, "Timeout waiting for v2.x card\n"); + return SDCARD_FAIL; +} + +int SDFileSystem::disk_initialize() { + + int i = initialise_card(); +// printf("init card = %d\n", i); +// printf("OK\n"); + + _sectors = _sd_sectors(); + + // Set block length to 512 (CMD16) + if(_cmd(16, 512) != 0) { + fprintf(stderr, "Set 512-byte block timed out\n"); + return 1; + } + + _spi.frequency(20000000); // Set to 20MHz for data transfer + return 0; +} + +int SDFileSystem::disk_write(const char *buffer, int block_number) { + // set write address for single block (CMD24) + if(_cmd(24, block_number * 512) != 0) { + return 1; + } + + // send the data block + _write(buffer, 512); + return 0; +} + +int SDFileSystem::disk_read(char *buffer, int block_number) { + // set read address for single block (CMD17) + if(_cmd(17, block_number * 512) != 0) { + return 1; + } + + // receive the data + _read(buffer, 512); + return 0; +} + +int SDFileSystem::disk_status() { return 0; } +int SDFileSystem::disk_sync() { return 0; } +int SDFileSystem::disk_sectors() { return _sectors; } + +// PRIVATE FUNCTIONS + +int SDFileSystem::_cmd(int cmd, int arg) { + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + _cs = 1; + _spi.write(0xFF); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} +int SDFileSystem::_cmdx(int cmd, int arg) { + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + + +int SDFileSystem::_cmd58() { + _cs = 0; + int arg = 0; + + // send a command + _spi.write(0x40 | 58); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + int ocr = _spi.write(0xFF) << 24; + ocr |= _spi.write(0xFF) << 16; + ocr |= _spi.write(0xFF) << 8; + ocr |= _spi.write(0xFF) << 0; +// printf("OCR = 0x%08X\n", ocr); + _cs = 1; + _spi.write(0xFF); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + +int SDFileSystem::_cmd8() { + _cs = 0; + + // send a command + _spi.write(0x40 | 8); // CMD8 + _spi.write(0x00); // reserved + _spi.write(0x00); // reserved + _spi.write(0x01); // 3.3v + _spi.write(0xAA); // check pattern + _spi.write(0x87); // crc + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT * 1000; i++) { + char response[5]; + response[0] = _spi.write(0xFF); + if(!(response[0] & 0x80)) { + for(int j=1; j<5; j++) { + response[i] = _spi.write(0xFF); + } + _cs = 1; + _spi.write(0xFF); + return response[0]; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + +int SDFileSystem::_read(char *buffer, int length) { + _cs = 0; + + // read until start byte (0xFF) + while(_spi.write(0xFF) != 0xFE); + + // read data + for(int i=0; i<length; i++) { + buffer[i] = _spi.write(0xFF); + } + _spi.write(0xFF); // checksum + _spi.write(0xFF); + + _cs = 1; + _spi.write(0xFF); + return 0; +} + +int SDFileSystem::_write(const char *buffer, int length) { + _cs = 0; + + // indicate start of block + _spi.write(0xFE); + + // write the data + for(int i=0; i<length; i++) { + _spi.write(buffer[i]); + } + + // write the checksum + _spi.write(0xFF); + _spi.write(0xFF); + + // check the repsonse token + if((_spi.write(0xFF) & 0x1F) != 0x05) { + _cs = 1; + _spi.write(0xFF); + return 1; + } + + // wait for write to finish + while(_spi.write(0xFF) == 0); + + _cs = 1; + _spi.write(0xFF); + return 0; +} + +static int ext_bits(char *data, int msb, int lsb) { + int bits = 0; + int size = 1 + msb - lsb; + for(int i=0; i<size; i++) { + int position = lsb + i; + int byte = 15 - (position >> 3); + int bit = position & 0x7; + int value = (data[byte] >> bit) & 1; + bits |= value << i; + } + return bits; +} + +int SDFileSystem::_sd_sectors() { + + // CMD9, Response R2 (R1 byte + 16-byte block read) + if(_cmdx(9, 0) != 0) { + fprintf(stderr, "Didn't get a response from the disk\n"); + return 0; + } + + char csd[16]; + if(_read(csd, 16) != 0) { + fprintf(stderr, "Couldn't read csd response from disk\n"); + return 0; + } + + // csd_structure : csd[127:126] + // c_size : csd[73:62] + // c_size_mult : csd[49:47] + // read_bl_len : csd[83:80] - the *maximum* read block length + + int csd_structure = ext_bits(csd, 127, 126); + int c_size = ext_bits(csd, 73, 62); + int c_size_mult = ext_bits(csd, 49, 47); + int read_bl_len = ext_bits(csd, 83, 80); + +// printf("CSD_STRUCT = %d\n", csd_structure); + + if(csd_structure != 0) { + fprintf(stderr, "This disk tastes funny! I only know about type 0 CSD structures\n"); + return 0; + } + + // memory capacity = BLOCKNR * BLOCK_LEN + // where + // BLOCKNR = (C_SIZE+1) * MULT + // MULT = 2^(C_SIZE_MULT+2) (C_SIZE_MULT < 8) + // BLOCK_LEN = 2^READ_BL_LEN, (READ_BL_LEN < 12) + + int block_len = 1 << read_bl_len; + int mult = 1 << (c_size_mult + 2); + int blocknr = (c_size + 1) * mult; + int capacity = blocknr * block_len; + + int blocks = capacity / 512; + + return blocks; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDFileSystem.h Mon May 28 20:50:49 2012 +0000 @@ -0,0 +1,81 @@ +/* mbed SDFileSystem Library, for providing file access to SD cards + * Copyright (c) 2008-2010, sford + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MBED_SDFILESYSTEM_H +#define MBED_SDFILESYSTEM_H + +#include "mbed.h" +#include "FATFileSystem.h" + +/** Access the filesystem on an SD Card using SPI + * + * @code + * #include "mbed.h" + * #include "SDFileSystem.h" + * + * SDFileSystem sd(p5, p6, p7, p12, "sd"); // mosi, miso, sclk, cs + * + * int main() { + * FILE *fp = fopen("/sd/myfile.txt", "w"); + * fprintf(fp, "Hello World!\n"); + * fclose(fp); + * } + */ +class SDFileSystem : public FATFileSystem { +public: + + /** Create the File System for accessing an SD Card using SPI + * + * @param mosi SPI mosi pin connected to SD Card + * @param miso SPI miso pin conencted to SD Card + * @param sclk SPI sclk pin connected to SD Card + * @param cs DigitalOut pin used as SD Card chip select + * @param name The name used to access the virtual filesystem + */ + SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name); + virtual int disk_initialize(); + virtual int disk_write(const char *buffer, int block_number); + virtual int disk_read(char *buffer, int block_number); + virtual int disk_status(); + virtual int disk_sync(); + virtual int disk_sectors(); + +protected: + + int _cmd(int cmd, int arg); + int _cmdx(int cmd, int arg); + int _cmd8(); + int _cmd58(); + int initialise_card(); + int initialise_card_v1(); + int initialise_card_v2(); + + int _read(char *buffer, int length); + int _write(const char *buffer, int length); + int _sd_sectors(); + int _sectors; + + SPI _spi; + DigitalOut _cs; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon May 28 20:50:49 2012 +0000 @@ -0,0 +1,1124 @@ +#include "mbed.h" +#include "ADNS2051.h" +#include "MI0283QTlib.h" + + +/** Demo software for ADNS2051 optical sensor. + * + */ + +// masks type +#define PREWITT 1 +#define KIRSCH 2 +#define SOBEL 3 +// gamma value +#define GAMMA_25 1 // gamma value=2.5 +#define GAMMA_22 2 // gamma value=2.2 +#define GAMMA_18 3 // gamma value=1.8 + +/** Color and Edges enhancement + * I took these two functions from the book: "Digital Media Processing DSP Algorithms Using C Hazarathaiah Malepati". + * + * From the book: + * Histogram equalization technique of color enhancement in the RGB domain. + * As we know, if the color component is represented with 8-bit precision, then the values 0 to 255 are used to represent a + * particular color component.We say that the image colors are well represented when the color components occupy + * the total range of 0 to 255. If the color components of an image do not occupy the full range (i.e., 0 to 255), then + * we have the scope to enhance the colors of that image.With the histogram equalization technique, first we find + * the minimum (Xmin) and maximum (Xmax) values of a particular color component, and then we translate that + * color component to have minimum value at zero by subtracting the Xmin from its values, and finally, multiply the + * color component by 255/(Xmax− Xmin) to obtain the maximum color component 255. This process is illustrated + * in Figure 10.1. The same process is applied for all three color components of an image. This process enhances + * image colors (green becomes greener, yellow becomes more yellow, etc.), + */ +void color_enhancement( unsigned char *pixelmap); + +/** + * From the book: + * With edge enhancement, we increase the contrast of the image along the edges. The edge enhancement is done by + * strengthening the high-frequency components of an image. One way of enhancing edges is by adding weighted + * high-frequency components of an image to itself. + */ +void edges_enhancement( unsigned char *pixelmap); + +/** From the book: Image processing in C Dwayne Phillips + * The quick edge function performs edge detection using the single 3 x 3 + * quick mask. It performs convolution over the image array using the quick mask. + * It thresholds the output image if requested, and xes the edges of the output + * image. + * Geometric operations change the spatial relationships between objects in an + * image. They do this by moving objects around and changing the size and + * shape of objects. Geometric operations help rearrange an image so we can + * see what we want to see a little better. + * The three basic geometric operations are displacement, stretching, and + * rotation. A fourth operation is the cross product. + * Displacement moves or displaces an image in the vertical and horizontal + * directions. Stretching enlarges or reduces an image in the vertical and horizontal + * directions. Rotation turns or rotates an image by any angle. + */ +void quick_edge(unsigned char *pixelmap, unsigned char *outmap, unsigned int threshold); +void setup_masks(unsigned int detect_type, int *mask_0, int *mask_1, int *mask_2, int *mask_3, int *mask_4, int *mask_5, int *mask_6, int *mask_7); +void perform_convolution(unsigned char *image, unsigned char *out_image, unsigned char detect_type, unsigned char threshold); +void geometry(unsigned char *the_image, unsigned char *out_image, + float x_angle, + float x_stretch, float y_stretch, + int x_displace, int y_displace, + float x_cross, float y_cross, + int bilinear, + int rows, + int cols); +unsigned char bilinear_interpolate(unsigned char *the_image, double x, double y, int rows, int cols); +/* I found these books very useful and instructive. Recommend them wholeheartedly. */ + + +/** Draw enlarged image to the x and y coordinates. + * The image will be enlarged by 4. + * + * @param *pxlmap the array image + * @param x the x position + * @param y the y position + */ +void magnify( unsigned char *pxlmap, unsigned int x, unsigned int y); +void gammacorrection( unsigned char *pixelmap, unsigned char gammaval); + +// +DigitalOut myled(LED2); +DigitalOut DBG_LED(LED1); +DigitalOut TS_CS(p15); +// +Serial pc(USBTX, USBRX); +// +ADNS2051 optical( p19, p20); +// +GLCD lcd( p11, p12, p13, p14, p17, p26); + +unsigned char pxlmap[256]; +//unsigned char edgemap[256]; +unsigned char tmpmap[256]; + +int new_hi=63, new_low=10; +int threshold_val=50; +float edges_val=0.5; + +#define COLOR_LVL 32 + +/* + #define COLOR_LVL 256 + + gamma LUT generated using this formula: + + new_pixel=(( org_pixel/COLOR_LVL)^(1/gamma))*COLOR_LVL + + // set gamma value... + gammaval=2.5; + float f2=1/gammaval; + // start LUT generation... + for ( i=0; i<COLOR_LVL; i++) { + float f1=(float)i/COLOR_LVL; + gammaLUT[i] = pow( f1,f2)*COLOR_LVL; + } + +*/ + +/* LUT for gamma value: 2.5 */ +unsigned char gmm25_LUT[32]={ + 0, 7, 10, 12, 13, 15, 16, 17, 18, 19, 20, 20, 21, 22, 22, 23, 24, + 24, 25, 25, 26, 27, 27, 28, 28, 28, 29, 29, 30, 30, 31, 31, +}; + +/* LUT for gamma value: 2.2 */ +unsigned char gmm22_LUT[32]={ + 0, 6, 9, 10, 12, 13, 14, 16, 17, 17, 18, 19, 20, 21, 21, 22, 23, + 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 29, 30, 30, 31, 31, +}; + +/* LUT for gamma value: 1.8 */ +unsigned char gmm18_LUT[32]={ + 0, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21, + 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 30, 31, +}; + +void gammacorrection( unsigned char *pixelmap, unsigned char gammaval) +{ + unsigned char *pGammaLUT; + unsigned char x0, y0; + int M=16, N=16; + int j, i, p, q; + + switch (gammaval) { + case GAMMA_25: + pGammaLUT=(unsigned char*)gmm25_LUT; + break; + case GAMMA_22: + pGammaLUT=(unsigned char*)gmm22_LUT; + break; + case GAMMA_18: + pGammaLUT=(unsigned char*)gmm18_LUT; + break; + } + + for(j = 0;j < M;j++){ + p = j*N;q = (j + 1)*N; + for(i = p;i < q; i++){ + y0 = pixelmap[i]; + x0 = pGammaLUT[y0]; + pixelmap[i] = x0; + } + } +} + +#define FILL 255 + +/******************************************* +* +* geometry(.. +* +* This routine performs geometric +* transformations on the pixels in an +* image array. It performs basic +* displacement, stretching, and rotation. +* +* The basic equations are: +* +* new x = x.cos(a) + y.sin(a) + x_displace +* + x.x_stretch +x.y.x_cross +* +* new y = y.cos(a) - x.sin(a) + y_displace +* + y.y_stretch +x.y.y_cross +* +*******************************************/ +void geometry(unsigned char *the_image, unsigned char *out_image, + float x_angle, + float x_stretch, float y_stretch, + int x_displace, int y_displace, + float x_cross, float y_cross, + int bilinear, + int rows, + int cols) +{ + double cosa, sina, radian_angle, tmpx, tmpy; + float fi, fj, x_div, y_div, x_num, y_num; + int i, j, new_i, new_j; + + + /****************************** + * + * Load the terms array with + * the correct parameters. + * + *******************************/ + + /* the following magic number is from + 180 degrees divided by pi */ + radian_angle = x_angle/57.29577951; + cosa = cos(radian_angle); + sina = sin(radian_angle); + + /************************************ + * + * NOTE: You divide by the + * stretching factors. Therefore, if + * they are zero, you divide by 1. + * You do this with the x_div y_div + * variables. You also need a + * numerator term to create a zero + * product. You do this with the + * x_num and y_num variables. + * + *************************************/ + + if(x_stretch < 0.00001){ + x_div = 1.0; + x_num = 0.0; + } + else{ + x_div = x_stretch; + x_num = 1.0; + } + + if(y_stretch < 0.00001){ + y_div = 1.0; + y_num = 0.0; + } + else{ + y_div = y_stretch; + y_num = 1.0; + } + + /************************** + * + * Loop over image array + * + **************************/ + + for(i=0; i<rows; i++){ + for(j=0; j<cols; j++){ + + fi = i; + fj = j; + + tmpx = (double)(j)*cosa + + (double)(i)*sina + + (double)(x_displace) + + (double)(x_num*fj/x_div) + + (double)(x_cross*i*j); + + tmpy = (double)(i)*cosa - + (double)(j)*sina + + (double)(y_displace) + + (double)(y_num*fi/y_div) + + (double)(y_cross*i*j); + + if(x_stretch != 0.0) + tmpx = tmpx - (double)(fj*cosa + fi*sina); + if(y_stretch != 0.0) + tmpy = tmpy - (double)(fi*cosa - fj*sina); + + new_j = tmpx; + new_i = tmpy; + + if(bilinear == 0){ + if(new_j < 0 || + new_j >= cols || + new_i < 0 || + new_i >= rows) + out_image[j+(i*rows)] = FILL; + else + out_image[j+(i*rows)] = + the_image[new_j+(new_i*rows)]; + } /* ends if bilinear */ + else{ + out_image[j+(i*rows)] = + bilinear_interpolate(the_image, + tmpx, tmpy, + rows, cols); + } /* ends bilinear if */ + + } /* ends loop over j */ + } /* ends loop over i */ + +} /* ends geometry */ + + + +/******************************************* +* +* bilinear_interpolate(.. +* +* This routine performs bi-linear +* interpolation. +* +* If x or y is out of range, i.e. less +* than zero or greater than rows or cols, +* this routine returns a zero. +* +* If x and y are both in range, this +* routine interpolates in the horizontal +* and vertical directions and returns +* the proper gray level. +* +*******************************************/ +unsigned char bilinear_interpolate(unsigned char *the_image, double x, double y, int rows, int cols) +{ + double fraction_x, fraction_y, + one_minus_x, one_minus_y, + tmp_double; + int ceil_x, ceil_y, floor_x, floor_y; + short p1, p2, p3, result = FILL; + + /****************************** + * + * If x or y is out of range, + * return a FILL. + * + *******************************/ + + if(x < 0.0 || + x >= (double)(cols-1) || + y < 0.0 || + y >= (double)(rows-1)) + return(result); + + tmp_double = floor(x); + floor_x = tmp_double; + tmp_double = floor(y); + floor_y = tmp_double; + tmp_double = ceil(x); + ceil_x = tmp_double; + tmp_double = ceil(y); + ceil_y = tmp_double; + + fraction_x = x - floor(x); + fraction_y = y - floor(y); + + one_minus_x = 1.0 - fraction_x; + one_minus_y = 1.0 - fraction_y; + + tmp_double = one_minus_x * + (double)(the_image[floor_x+(floor_y*rows)]) + + fraction_x * + (double)(the_image[ceil_x+(floor_y*rows)]); + p1 = tmp_double; + + tmp_double = one_minus_x * + (double)(the_image[floor_x+(ceil_y*rows)]) + + fraction_x * + (double)(the_image[ceil_x+(ceil_y*rows)]); + p2 = tmp_double; + + tmp_double = one_minus_y * (double)(p1) + + fraction_y * (double)(p2); + p3 = tmp_double; + + + return(p3); + +} /* ends bilinear_interpolate */ + + + +/*************************** +* +* Directions for the masks +* 3 2 1 +* 4 x 0 +* 5 6 7 +* +****************************/ +/* masks for kirsch operator */ +int kirsch_mask_0[9] = { + 5, 5, 5, + -3, 0, -3, + -3, -3, -3}; + +int kirsch_mask_1[9] = { + -3, 5, 5, + -3, 0, 5, + -3, -3, -3}; + +int kirsch_mask_2[9] = { + -3, -3, 5, + -3, 0, 5, + -3, -3, 5}; + +int kirsch_mask_3[9] = { + -3, -3, -3, + -3, 0, 5, + -3, 5, 5}; + +int kirsch_mask_4[9] = { + -3, -3, -3, + -3, 0, -3, + 5, 5, 5}; + +int kirsch_mask_5[9] = { + -3, -3, -3, + 5, 0, -3, + 5, 5, -3}; + +int kirsch_mask_6[9] = { + 5, -3, -3, + 5, 0, -3, + 5, -3, -3}; + +int kirsch_mask_7[9] = { + 5, 5, -3, + 5, 0, -3, + -3, -3, -3}; + + + /* masks for prewitt operator */ + +int prewitt_mask_0[9] = { + 1, 1, 1, + 1, -2, 1 + -1, -1, -1 }; + +int prewitt_mask_1[9] = { + 1, 1, 1, + 1, -2, -1, + 1, -1, -1 }; + +int prewitt_mask_2[9] = { + 1, 1, -1, + 1, -2, -1, + 1, 1, -1 }; + +int prewitt_mask_3[9] = { + 1, -1, -1, + 1, -2, -1, + 1, 1, 1}; + +int prewitt_mask_4[9] = { + -1, -1, -1, + 1, -2, 1, + 1, 1, 1}; + +int prewitt_mask_5[9] = { + -1, -1, 1, + -1, -2, 1, + 1, 1, 1}; + +int prewitt_mask_6[9] = { + -1, 1, 1, + -1, -2, 1, + -1, 1, 1}; + +int prewitt_mask_7[9] = { + 1, 1, 1, + -1, -2, 1, + -1, -1, 1}; + + + /* masks for sobel operator */ + +int sobel_mask_0[9] = { + 1, 2, 1, + 0, 0, 0, + -1, -2, -1}; + +int sobel_mask_1[9] = { + 2, 1, 0, + 1, 0, -1, + 0, -1, -2 }; + +int sobel_mask_2[9] = { + 1, 0, -1, + 2, 0, -2, + 1, 0, -1}; +int sobel_mask_3[9] = { + 0, -1, -2, + 1, 0, -1, + 2, 1, 0 }; +int sobel_mask_4[9] = { + -1, -2, -1, + 0, 0, 0, + 1, 2, 1}; + +int sobel_mask_5[9] = { + -2, -1, 0, + -1, 0, 1, + 0, 1, 2}; + +int sobel_mask_6[9] = { + -1, 0, 1, + -2, 0, 2, + -1, 0, 1}; + +int sobel_mask_7[9] = { + 0, 1, 2, + -1, 0, 1, + -2, -1, 0}; + + +/*********************************************** +* +* setup_masks(... +* +* This function copies the mask values defined +* at the top of this file into the mask +* arrays mask_0 through mask_7. +* +***********************************************/ +void setup_masks(unsigned int detect_type, int *mask_0, int *mask_1, int *mask_2, int *mask_3, int *mask_4, int *mask_5, int *mask_6, int *mask_7) +{ + int i; + + if(detect_type == KIRSCH){ + for(i=0; i<9; i++){ + mask_0[i] = kirsch_mask_0[i]; + mask_1[i] = kirsch_mask_1[i]; + mask_2[i] = kirsch_mask_2[i]; + mask_3[i] = kirsch_mask_3[i]; + mask_4[i] = kirsch_mask_4[i]; + mask_5[i] = kirsch_mask_5[i]; + mask_6[i] = kirsch_mask_6[i]; + mask_7[i] = kirsch_mask_7[i]; + } + } /* ends if detect_type == KIRSCH */ + + + if(detect_type == PREWITT){ + for(i=0; i<9; i++){ + mask_0[i] = prewitt_mask_0[i]; + mask_1[i] = prewitt_mask_1[i]; + mask_2[i] = prewitt_mask_2[i]; + mask_3[i] = prewitt_mask_3[i]; + mask_4[i] = prewitt_mask_4[i]; + mask_5[i] = prewitt_mask_5[i]; + mask_6[i] = prewitt_mask_6[i]; + mask_7[i] = prewitt_mask_7[i]; + } + } /* ends if detect_type == PREWITT */ + + + if(detect_type == SOBEL){ + for(i=0; i<9; i++){ + mask_0[i] = sobel_mask_0[i]; + mask_1[i] = sobel_mask_1[i]; + mask_2[i] = sobel_mask_2[i]; + mask_3[i] = sobel_mask_3[i]; + mask_4[i] = sobel_mask_4[i]; + mask_5[i] = sobel_mask_5[i]; + mask_6[i] = sobel_mask_6[i]; + mask_7[i] = sobel_mask_7[i]; + } + } /* ends if detect_type == SOBEL */ + +} /* ends setup_masks */ + +/********************************************************** +* +* perform_convolution(... +* +* This function performs convolution between the input +* image and 8 3x3 masks. The result is placed in +* the out_image. +* +********************************************************/ +void perform_convolution(unsigned char *image, unsigned char *out_image, unsigned char detect_type, unsigned char threshold) +{ + + int b, i, j, sum, p, q, c, k; + + int mask_0[9]; + int mask_1[9]; + int mask_2[9]; + int mask_3[9]; + int mask_4[9]; + int mask_5[9]; + int mask_6[9]; + int mask_7[9]; + + int max, + min, + new_hi, + new_low; + + int M=16, N=16; + + setup_masks(detect_type, &mask_0[0], &mask_1[0],&mask_2[0], &mask_3[0], &mask_4[0], &mask_5[0],&mask_6[0], &mask_7[0]); + + new_hi = 60; + new_low = 10; + + min = 10; + max = 63; + + /* clear output image array */ + for(i=0; i<N*M; i++) + out_image[i] = 0; + + for( j = 1;j < (M-1);j++){ + p = j*N; q = (j+1)*N; + for( i = (p+1);i < (q-1);i++) { + + /* Convolve for all 8 directions */ + + /* 0 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_0[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + /* 1 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_1[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + /* 2 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_2[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + /* 3 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_3[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + /* 4 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_4[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + /* 5 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_5[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + /* 6 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_6[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + /* 7 direction */ + + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + image[k+b] * mask_7[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + if(sum > out_image[i]) + out_image[i] = sum; + + + } /* ends loop over j */ + } /* ends loop over i */ + + /* if desired, threshold the output image */ + if(threshold == 1){ + for( j = 0;j < M;j++){ + p = j*N; q = (j+1)*N; + for( i = p;i < q;i++) { + if(out_image[i] > 50){ + out_image[i] = new_hi; + } + else{ + out_image[i] = new_low; + } + } + } + } /* ends if threshold == 1 */ + +} /* ends perform_convolution */ + + +/******************************************* + * + * quick_edge(... + * + * This function finds edges by using + * a single 3x3 mask. + * + *******************************************/ +void quick_edge(unsigned char *pixelmap, unsigned char *outmap, unsigned int threshold) +{ + int i, j, k, c, p, q, b; + int max, sum; + + int M=16, N=16; + + int quick_mask[9] = { + -1, 0, -1, + 0, 4, 0, + -1, 0, -1 }; + + max = 63; + + /* Do convolution over image array */ + for( j = 1;j < (M-1);j++){ + p = j*N; q = (j+1)*N; + for( i = (p+1);i < (q-1);i++) { + sum = 0; + c=0; + for( k=(i-N); k<=(i+N); k+=N) { + for(b=-1; b<2; b++){ + sum = sum + pixelmap[k+b] * quick_mask[c++]; + } + } + if(sum < 0) sum = 0; + if(sum > max) sum = max; + outmap[i] = sum; + } /* ends loop over i */ + } /* ends loop over j */ + + /* if desired, threshold the output image */ + if(threshold == 1){ + for( j = 0;j < M;j++){ + p = j*N; q = (j+1)*N; + for( i = p;i < q;i++) { + if(outmap[i] > threshold_val){ + outmap[i] = new_hi; + } + else{ + outmap[i] = new_low; + } + } + } + } /* ends if threshold == 1 */ + +} /* ends quick_edge */ + + + +void edges_enhancement( unsigned char *pixelmap) +{ + int i, j, N, M, p, q; + unsigned char BufX[256]; + int y0, x0; + + N=16; + M=16; + + for(j = 0;j < 1;j++) { // copy top row + p = j*N; q = (j+1)*N; + for(i = p;i < q;i++) + BufX[i] = pixelmap[i]; + } + for(j = 0;j < M;j++) { // copy left column + p = j*N; + BufX[p] = pixelmap[p]; + } + for(j = 1;j < M-1;j++) { + p = j*N+1; q = (j+1)*N-1; + for(i = p;i < q;i++) { + x0 = pixelmap[i]; y0 = pixelmap[i-1]; + x0 = x0 << 2; + x0 = x0 - y0; y0 = pixelmap[i+1]; + x0 = x0 - y0; y0 = pixelmap[i-N-1]; + x0 = x0 - y0; y0 = pixelmap[i+N+1]; + x0 = x0 - y0; + x0 = (int) pixelmap[i] + (int) x0*edges_val; + if (x0 < 0) x0 = 0; + if (x0 > 63) x0 = 63; + BufX[i] = (unsigned char) x0; + } + } + // + for(j = 0;j < M;j++){ + p = j*N; q = (j+1)*N; + for(i = p;i < q;i++) + pixelmap[i] = BufX[i]; + } +} + +void color_enhancement( unsigned char *pixelmap) +{ + int i, j, N, M, p, q; + unsigned char Hist[64]; + int y0, x0; + float x; + + N=16; + M=16; + + for(i=0; i<64; i++) + Hist[i]=0; + + // Starting the color enhancement + // + for(j = 0;j < M;j++){ + p = j*N; + q = (j+1)*N; + for(i = p;i < q;i++) + Hist[ pixelmap[i]] += 1; + } + + y0 = 0; + for(p = 0;p < 64;p++) { + y0+= Hist[p]; + if (y0 > 32) + break; + } + + y0=p; + x = 0; + for(j = 0;j < M;j++) { + p = j*N; q = (j+1)*N; + for(i = p;i < q;i++) { + x0 = (int) pixelmap[i] - y0; + if (x0 < 0) + x0 = 0; + if (x0 > 63) + x0 = 63; + pixelmap[i] = x0; + } + } + + for(j = 63;j > 0;j--) + if (Hist[j] > 0) break; + + x = (float) 64.0/(64-y0 + 63-j); + + for(j = 0;j < M;j++) { + p = j*N; q = (j+1)*N; + for(i = p;i < q;i++) + pixelmap[i] = (unsigned char) ((float) pixelmap[i]*x + 0.5); + } + +} + +int main() { + + char buf[256], c; + int i, ii; + int thrshld=0; + int masktype=1; + int gammaval=0; // gamma OFF + + myled = 0; + TS_CS=1; + // + int subidx, idx; + + lcd.lcd_init(); + lcd.lcd_clear( LCD_WHITE); + lcd.backlightset( 1); + lcd.lcd_drawstr( "Images from optical mouse: Init", 0, 0, lcd.lcd_RGB( 0, 255, 0)); + + while( optical.init() ); + optical.setframerate(); + + sprintf(buf, "Images from optical mouse: PixelDump [Chip rev: 0x%X]", optical.get_revisionID()); + lcd.lcd_drawstr( buf, 0, 0, lcd.lcd_RGB( 0, 255, 0)); + wait( 1.0); + + // + while( 1) { + + // you can use the serial terminal to change some parameters: + // [T/t] threshold ON/OFF + // [V/v] threshold value inc/dec + // [H/h] high value inc/dec + // [E/e] edge enhancement inc/dec + // [m] change mask type + // [g] change gamma value + // + if( pc.readable()) { + c = pc.getc(); + if ( c=='t') { + thrshld=0; + printf("thrshld OFF\r\n"); + } + if ( c=='T') { + thrshld=1; + printf("thrshld ON\r\n"); + } + // threshold_val, new_hi, new_low + if ( c=='V') { + threshold_val++; + if ( threshold_val>63) + threshold_val=63; + printf("threshold_val=%d\r\n", threshold_val); + } + if ( c=='v') { + threshold_val--; + if ( threshold_val<0) + threshold_val=0; + printf("threshold_val=%d\r\n", threshold_val); + } + if ( c=='H') { + new_hi++; + if ( new_hi>63) + new_hi=63; + printf("new_hi=%d\r\n", new_hi); + } + if ( c=='h') { + new_hi--; + if ( new_hi<0) + new_hi=0; + printf("new_hi=%d\r\n", new_hi); + } + /* edges enhancement */ + if ( c=='e') { + edges_val-=0.1; + if ( edges_val<0) + edges_val=0; + printf("edges_val=%1.2f\r\n", edges_val); + } + if ( c=='E') { + edges_val+=0.1; + if ( edges_val>1) + edges_val=1; + printf("edges_val=%1.2f\r\n", edges_val); + } + // + if ( c=='m') { + masktype+=1; + if ( masktype>3) + masktype=1; + printf("masktype=%d\r\n", masktype); + } + // + if ( c=='g') { + gammaval+=1; + if ( gammaval>3) + gammaval=0; + printf("gammaval=%d\r\n", gammaval); + } + } + + unsigned char mv=optical.readmotion(); + sprintf(buf, "SQUAL:%d, dX:%+d, dY:%+d, Motion:0x%X ", optical.surfacequality(), optical.deltaX, optical.deltaY, mv); + lcd.lcd_drawstr( buf, 0, 200, lcd.lcd_RGB( 0, 255, 0)); + + sprintf(buf, "AvrPxl:%d, MxPxl:%d ", optical.averagepixel(), optical.maxpixelval() ); + lcd.lcd_drawstr( buf, 0, 220, lcd.lcd_RGB( 0, 255, 0)); + + optical.pixeldump( &tmpmap[0]); + + if ( gammaval) { + gammacorrection( &tmpmap[0], gammaval); + } + + // pixelmap has the correct order: pos 0 is the left,upper image position. + // Refer to the DS pag:35 about how to display the array + c=0; + subidx=0xFF; + for( i=0;i<16;i++) { + idx=subidx; + for ( ii=0; ii<16; ii++) { + pxlmap[c++]=tmpmap[idx]; + idx-=16; + } + subidx--; + } + + // Use the feature geometry only for implementing the bilinear better viewing. + geometry( &pxlmap[0], &tmpmap[0], + 0.0, // angle + 0.0, 0.0, // streatch x, y + 0.0, 0.0, // displace x, y + 0.0, 0.0, // cross x, y + 1, // bilinear ON/OFF + 16, 16); // rows, cols + + // I copy the correct image in the array pxlmap to use it as a basis for successive processing. + for ( i=0; i<(16*16); i++) + pxlmap[i] = tmpmap[i]; + + // 1 + magnify( &tmpmap[0], 20, 20); + + // + for( i=0;i<256;i++) { + tmpmap[i]=pxlmap[i]; + } + + // 2 + color_enhancement( &tmpmap[0]); + magnify( &tmpmap[0], 120, 20); + + // + for( i=0;i<256;i++) { + tmpmap[i]=pxlmap[i]; + } + + // 3 + edges_enhancement( &tmpmap[0]); + magnify( &tmpmap[0], 220, 20); + + // 4 + quick_edge( &pxlmap[0], &tmpmap[0], thrshld); + magnify( &tmpmap[0], 20, 100); + + // 5 + perform_convolution( &pxlmap[0], &tmpmap[0], masktype, thrshld); + magnify( &tmpmap[0], 120, 100); + + // 6 + // visualizzo l'immagine originale... + magnify( &pxlmap[0], 220, 100); + + } + + while(1) { + myled = 1; + wait(0.2); + myled = 0; + wait(0.2); + } +} + +/* use the geometry function to magnify the image. */ +void magnify( unsigned char *pxlmap, unsigned int x, unsigned int y) +{ + unsigned int r, c; + float xy_str=(float)64/16; + unsigned char tmpmap[256]; + + // inizio ciclo... + for ( r=0; r<16; r+=4) { + for ( c=0; c<16; c+=4) { + // eseguo lo zoom sull'immagine... + geometry( &pxlmap[0], &tmpmap[0], + 0.0, // angle + xy_str, xy_str, // streatch x, y + (float)c, (float)r, // displace x, y + 0.0, 0.0, // cross x, y + 1, // bilinear ON/OFF + 16, 16); // rows, cols + // la visualizzo... + int idx=0; + for( int i=0;i<16;i++) { + for ( int ii=0; ii<16; ii++) { + lcd.lcd_drawpixel( x+((c/4)*16)+ii, y+((r/4)*16)+i, lcd.lcd_RGB( tmpmap[idx],tmpmap[idx],tmpmap[idx]) ); + idx++; + } + } + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon May 28 20:50:49 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/e2ac27c8e93e