A library with drivers for different peripherals on the LPC4088 QuickStart Board or related add-on boards.
Dependencies: FATFileSystem
Fork of EALib by
Revision 0:0fdadbc3d852, committed 2013-09-26
- Comitter:
- embeddedartists
- Date:
- Thu Sep 26 06:37:02 2013 +0000
- Child:
- 1:f3ef2b577bf6
- Commit message:
- First version
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adafruit_GFX.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,479 @@ +/* +This is the core graphics library for all our displays, providing a common +set of graphics primitives (points, lines, circles, etc.). It needs to be +paired with a hardware-specific library for each display device we carry +(to handle the lower-level functions). + +Adafruit invests time and resources providing this open source code, please +support Adafruit & open-source hardware by purchasing products from Adafruit! + +Copyright (c) 2013 Adafruit Industries. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mbed.h" +#include "Adafruit_GFX.h" +#include "glcdfont.c" +#ifdef __AVR__ +#include <avr/pgmspace.h> +#else +#define pgm_read_byte(addr) (*(const unsigned char *)(addr)) +#endif + +Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h): +WIDTH(w), HEIGHT(h) +{ + _width = WIDTH; + _height = HEIGHT; + rotation = 0; + cursor_y = cursor_x = 0; + textsize = 1; + textcolor = textbgcolor = 0xFFFF; + wrap = true; +} + +// Draw a circle outline +void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + drawPixel(x0 , y0+r, color); + drawPixel(x0 , y0-r, color); + drawPixel(x0+r, y0 , color); + drawPixel(x0-r, y0 , color); + + while (x<y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 + x, y0 - y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + y, y0 + x, color); + drawPixel(x0 - y, y0 + x, color); + drawPixel(x0 + y, y0 - x, color); + drawPixel(x0 - y, y0 - x, color); + } +} + +void Adafruit_GFX::drawCircleHelper( int16_t x0, int16_t y0, + int16_t r, uint8_t cornername, uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x<y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (cornername & 0x4) { + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 + y, y0 + x, color); + } + if (cornername & 0x2) { + drawPixel(x0 + x, y0 - y, color); + drawPixel(x0 + y, y0 - x, color); + } + if (cornername & 0x8) { + drawPixel(x0 - y, y0 + x, color); + drawPixel(x0 - x, y0 + y, color); + } + if (cornername & 0x1) { + drawPixel(x0 - y, y0 - x, color); + drawPixel(x0 - x, y0 - y, color); + } + } +} + +void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r, + uint16_t color) { + drawFastVLine(x0, y0-r, 2*r+1, color); + fillCircleHelper(x0, y0, r, 3, 0, color); +} + +// Used to do circles and roundrects +void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, + uint8_t cornername, int16_t delta, uint16_t color) { + + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + while (x<y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + if (cornername & 0x1) { + drawFastVLine(x0+x, y0-y, 2*y+1+delta, color); + drawFastVLine(x0+y, y0-x, 2*x+1+delta, color); + } + if (cornername & 0x2) { + drawFastVLine(x0-x, y0-y, 2*y+1+delta, color); + drawFastVLine(x0-y, y0-x, 2*x+1+delta, color); + } + } +} + +// Bresenham's algorithm - thx wikpedia +void Adafruit_GFX::drawLine(int16_t x0, int16_t y0, + int16_t x1, int16_t y1, + uint16_t color) { + int16_t steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + swap(x0, y0); + swap(x1, y1); + } + + if (x0 > x1) { + swap(x0, x1); + swap(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs(y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0<=x1; x0++) { + if (steep) { + drawPixel(y0, x0, color); + } else { + drawPixel(x0, y0, color); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +// Draw a rectangle +void Adafruit_GFX::drawRect(int16_t x, int16_t y, + int16_t w, int16_t h, + uint16_t color) { + drawFastHLine(x, y, w, color); + drawFastHLine(x, y+h-1, w, color); + drawFastVLine(x, y, h, color); + drawFastVLine(x+w-1, y, h, color); +} + +void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y, + int16_t h, uint16_t color) { + // Update in subclasses if desired! + drawLine(x, y, x, y+h-1, color); +} + +void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y, + int16_t w, uint16_t color) { + // Update in subclasses if desired! + drawLine(x, y, x+w-1, y, color); +} + +void Adafruit_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + // Update in subclasses if desired! + for (int16_t i=x; i<x+w; i++) { + drawFastVLine(i, y, h, color); + } +} + +void Adafruit_GFX::fillScreen(uint16_t color) { + fillRect(0, 0, _width, _height, color); +} + +// Draw a rounded rectangle +void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, + int16_t h, int16_t r, uint16_t color) { + // smarter version + drawFastHLine(x+r , y , w-2*r, color); // Top + drawFastHLine(x+r , y+h-1, w-2*r, color); // Bottom + drawFastVLine(x , y+r , h-2*r, color); // Left + drawFastVLine(x+w-1, y+r , h-2*r, color); // Right + // draw four corners + drawCircleHelper(x+r , y+r , r, 1, color); + drawCircleHelper(x+w-r-1, y+r , r, 2, color); + drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color); + drawCircleHelper(x+r , y+h-r-1, r, 8, color); +} + +// Fill a rounded rectangle +void Adafruit_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, + int16_t h, int16_t r, uint16_t color) { + // smarter version + fillRect(x+r, y, w-2*r, h, color); + + // draw four corners + fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color); + fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color); +} + +// Draw a triangle +void Adafruit_GFX::drawTriangle(int16_t x0, int16_t y0, + int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color) { + drawLine(x0, y0, x1, y1, color); + drawLine(x1, y1, x2, y2, color); + drawLine(x2, y2, x0, y0, color); +} + +// Fill a triangle +void Adafruit_GFX::fillTriangle ( int16_t x0, int16_t y0, + int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color) { + + int16_t a, b, y, last; + + // Sort coordinates by Y order (y2 >= y1 >= y0) + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + if (y1 > y2) { + swap(y2, y1); swap(x2, x1); + } + if (y0 > y1) { + swap(y0, y1); swap(x0, x1); + } + + if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if(x1 < a) a = x1; + else if(x1 > b) b = x1; + if(x2 < a) a = x2; + else if(x2 > b) b = x2; + drawFastHLine(a, y0, b-a+1, color); + return; + } + + int16_t + dx01 = x1 - x0, + dy01 = y1 - y0, + dx02 = x2 - x0, + dy02 = y2 - y0, + dx12 = x2 - x1, + dy12 = y2 - y1, + sa = 0, + sb = 0; + + // For upper part of triangle, find scanline crossings for segments + // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 + // is included here (and second loop will be skipped, avoiding a /0 + // error there), otherwise scanline y1 is skipped here and handled + // in the second loop...which also avoids a /0 error here if y0=y1 + // (flat-topped triangle). + if(y1 == y2) last = y1; // Include y1 scanline + else last = y1-1; // Skip it + + for(y=y0; y<=last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + /* longhand: + a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + drawFastHLine(a, y, b-a+1, color); + } + + // For lower part of triangle, find scanline crossings for segments + // 0-2 and 1-2. This loop is skipped if y1=y2. + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + for(; y<=y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + /* longhand: + a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); + b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); + */ + if(a > b) swap(a,b); + drawFastHLine(a, y, b-a+1, color); + } +} + +void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, + const uint8_t *bitmap, int16_t w, int16_t h, + uint16_t color) { + + int16_t i, j, byteWidth = (w + 7) / 8; + + for(j=0; j<h; j++) { + for(i=0; i<w; i++ ) { + if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) { + drawPixel(x+i, y+j, color); + } + } + } +} + + +size_t Adafruit_GFX::write(uint8_t c) { + if (c == '\n') { + cursor_y += textsize*8; + cursor_x = 0; + } else if (c == '\r') { + // skip em + } else { + drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); + cursor_x += textsize*6; + if (wrap && (cursor_x > (_width - textsize*6))) { + cursor_y += textsize*8; + cursor_x = 0; + } + } + + return 1; + +} + +// Draw a character +void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, + uint16_t color, uint16_t bg, uint8_t size) { + + if((x >= _width) || // Clip right + (y >= _height) || // Clip bottom + ((x + 6 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; + + for (int8_t i=0; i<6; i++ ) { + uint8_t line; + if (i == 5) + line = 0x0; + else + line = pgm_read_byte(font+(c*5)+i); + for (int8_t j = 0; j<8; j++) { + if (line & 0x1) { + if (size == 1) // default size + drawPixel(x+i, y+j, color); + else { // big size + fillRect(x+(i*size), y+(j*size), size, size, color); + } + } else if (bg != color) { + if (size == 1) // default size + drawPixel(x+i, y+j, bg); + else { // big size + fillRect(x+i*size, y+j*size, size, size, bg); + } + } + line >>= 1; + } + } +} + +void Adafruit_GFX::setCursor(int16_t x, int16_t y) { + cursor_x = x; + cursor_y = y; +} + +void Adafruit_GFX::setTextSize(uint8_t s) { + textsize = (s > 0) ? s : 1; +} + +void Adafruit_GFX::setTextColor(uint16_t c) { + // For 'transparent' background, we'll set the bg + // to the same as fg instead of using a flag + textcolor = textbgcolor = c; +} + +void Adafruit_GFX::setTextColor(uint16_t c, uint16_t b) { + textcolor = c; + textbgcolor = b; +} + +void Adafruit_GFX::setTextWrap(bool w) { + wrap = w; +} + +uint8_t Adafruit_GFX::getRotation(void) { + return rotation; +} + +void Adafruit_GFX::setRotation(uint8_t x) { + rotation = (x & 3); + switch(rotation) { + case 0: + case 2: + _width = WIDTH; + _height = HEIGHT; + break; + case 1: + case 3: + _width = HEIGHT; + _height = WIDTH; + break; + } +} + +// Return the size of the display (per current rotation) +int16_t Adafruit_GFX::width(void) { + return _width; +} + +int16_t Adafruit_GFX::height(void) { + return _height; +} + +void Adafruit_GFX::invertDisplay(bool i) { + // Do nothing, must be subclassed if supported +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Adafruit_GFX.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,79 @@ +#ifndef _ADAFRUIT_GFX_H + + +#define swap(a, b) { int16_t t = a; a = b; b = t; } + +class Adafruit_GFX { + + public: + + Adafruit_GFX(int16_t w, int16_t h); // Constructor + + // This MUST be defined by the subclass: + virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0; + + // These MAY be overridden by the subclass to provide device-specific + // optimized code. Otherwise 'generic' versions are used. + virtual void + drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color), + drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color), + drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color), + drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color), + fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color), + fillScreen(uint16_t color), + invertDisplay(bool i); + + // These exist only with Adafruit_GFX (no subclass overrides) + void + drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color), + drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, + uint16_t color), + fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color), + fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, + int16_t delta, uint16_t color), + drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color), + fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, + int16_t x2, int16_t y2, uint16_t color), + drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color), + fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, + int16_t radius, uint16_t color), + drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, + int16_t w, int16_t h, uint16_t color), + drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, + uint16_t bg, uint8_t size), + setCursor(int16_t x, int16_t y), + setTextColor(uint16_t c), + setTextColor(uint16_t c, uint16_t bg), + setTextSize(uint8_t s), + setTextWrap(bool w), + setRotation(uint8_t r); + + + virtual size_t write(uint8_t); + + int16_t + height(void), + width(void); + + uint8_t getRotation(void); + + protected: + const int16_t + WIDTH, HEIGHT; // This is the 'raw' display w/h - never changes + int16_t + _width, _height, // Display w/h as modified by current rotation + cursor_x, cursor_y; + uint16_t + textcolor, textbgcolor; + uint8_t + textsize, + rotation; + bool + wrap; // If set, 'wrap' text at right edge of display +}; + +#endif // _ADAFRUIT_GFX_H + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EaLcdBoard.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,602 @@ + +#include "mbed.h" +#include "EaLcdBoard.h" + +#define LCDB_MAGIC 0xEA01CDAE + + +#define LCDB_PCA9532_I2C_ADDR (0x64 << 1) + +/* PCA9532 registers*/ +#define LCDB_PCA9532_INPUT0 0x00 +#define LCDB_PCA9532_INPUT1 0x01 +#define LCDB_PCA9532_PSC0 0x02 +#define LCDB_PCA9532_PWM0 0x03 +#define LCDB_PCA9532_PSC1 0x04 +#define LCDB_PCA9532_PWM1 0x05 +#define LCDB_PCA9532_LS0 0x06 +#define LCDB_PCA9532_LS1 0x07 +#define LCDB_PCA9532_LS2 0x08 +#define LCDB_PCA9532_LS3 0x09 +#define LCDB_PCA9532_AUTO_INC 0x10 + +#define LCDB_LS_MODE_ON 0x01 +#define LCDB_LS_MODE_BLINK0 0x02 +#define LCDB_LS_MODE_BLINK1 0x03 + +#define LCDB_CTRL_3V3 0x0001 +#define LCDB_CTRL_5V 0x0002 +#define LCDB_CTRL_DISP_EN 0x0010 +#define LCDB_CTRL_BL_EN 0x0080 +#define LCDB_CTRL_BL_C 0x0100 +#define LCDB_EEPROM_WP 0x8000 + +#define LCDB_EEPROM_I2C_ADDR (0x56 << 1) +#define LCDB_EEPROM_PAGE_SIZE 32 +#define LCDB_EEPROM_TOTAL_SIZE 8192 + +/* + * Set which sequence string version that is supported + */ +#define LCDB_SEQ_VER 1 + +#ifndef MIN +#define MIN(x, y) (((x)<(y))?(x):(y)) +#endif + +#define EA_LCD_TMP_BUFFER_SZ 256 +static char* eaLcdTmpBuffer[EA_LCD_TMP_BUFFER_SZ]; + + +/* Structure containing the parameters for the LCD panel as stored on LCD Board */ + +/* LCD display types */ +typedef enum { + TFT = 0, /* standard TFT */ + ADTFT, /* advanced TFT */ + HRTFT, /* highly reflective TFT */ + MONO_4BIT, /* 4-bit mono */ + MONO_8BIT, /* 8-bit mono */ + CSTN /* color STN */ +} nxp_lcd_panel_t; + +typedef struct { + uint8_t h_back_porch; /* Horizontal back porch in clocks */ + uint8_t h_front_porch; /* Horizontal front porch in clocks */ + uint8_t h_sync_pulse_width; /* HSYNC pulse width in clocks */ + uint16_t pixels_per_line; /* Pixels per line (horizontal resolution) */ + uint8_t v_back_porch; /* Vertical back porch in clocks */ + uint8_t v_front_porch; /* Vertical front porch in clocks */ + uint8_t v_sync_pulse_width; /* VSYNC pulse width in clocks */ + uint16_t lines_per_panel; /* Lines per panel (vertical resolution) */ + uint8_t invert_output_enable; /* Invert output enable, 1 = invert*/ + uint8_t invert_panel_clock; /* Invert panel clock, 1 = invert*/ + uint8_t invert_hsync; /* Invert HSYNC, 1 = invert */ + uint8_t invert_vsync; /* Invert VSYNC, 1 = invert */ + uint8_t ac_bias_frequency; /* AC bias frequency in clocks */ + uint8_t bits_per_pixel; /* Maximum bits per pixel the display supports */ + uint32_t optimal_clock; /* Optimal clock rate (Hz) */ + nxp_lcd_panel_t lcd_panel_type; /* LCD panel type */ + uint8_t dual_panel; /* Dual panel, 1 = dual panel display */ +} nxp_lcd_param_t; + +static uint32_t str_to_uint(char* str, uint32_t len); + +EaLcdBoard::EaLcdBoard(PinName sda, PinName scl) : _i2c(sda, scl) { + _blink0Shadow = 0; + _blink1Shadow = 0; + _ledStateShadow = 0; + _lcdPwrOn = false; +} + +EaLcdBoard::Result EaLcdBoard::open(LcdController::Config* cfg, char* initSeq) { + + EaLcdBoard::Result result = Ok; + + // load LCD configuration from storage + if (cfg == NULL) { + result = getLcdConfig(&_cfg); + cfg = &_cfg; + } + + // load init sequence from storage + if (result == Ok && initSeq == NULL) { + result = getInitSeq((char*)eaLcdTmpBuffer, EA_LCD_TMP_BUFFER_SZ); + initSeq = (char*)eaLcdTmpBuffer; + } + + if (result != Ok) { + return result; + } + + return parseInitString(initSeq, cfg); +} + +EaLcdBoard::Result EaLcdBoard::close() { + int r = 0; + + do { + r = lcdCtrl.setPower(false); + if (r != 0) break; + + _lcdPwrOn = false; + + r = lcdCtrl.close(); + } while(0); + + if (r != 0) { + return LcdAccessError; + } + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::setFrameBuffer(uint32_t address) { + int r = 0; + + do { + + // begin by powering on the display + if (!_lcdPwrOn) { + r = lcdCtrl.setPower(true); + if (r != 0) break; + + _lcdPwrOn = true; + } + + // activate specified frame buffer + r = lcdCtrl.setFrameBuffer(address); + if (r != 0) break; + + } while(0); + + if (r != 0) { + return LcdAccessError; + } + + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getLcdConfig(LcdController::Config* cfg) { + store_t h; + + nxp_lcd_param_t lcdParam; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + eepromRead((uint8_t*)&lcdParam, h.lcdParamOff, + (h.initOff-h.lcdParamOff)); + + cfg->horizontalBackPorch = lcdParam.h_back_porch; + cfg->horizontalFrontPorch = lcdParam.h_front_porch; + cfg->hsync = lcdParam.h_sync_pulse_width; + cfg->width = lcdParam.pixels_per_line; + cfg->verticalBackPorch = lcdParam.v_back_porch; + cfg->verticalFrontPorch = lcdParam.v_front_porch; + cfg->vsync = lcdParam.v_sync_pulse_width; + cfg->height = lcdParam.lines_per_panel; + cfg->invertOutputEnable = (lcdParam.invert_output_enable == 1); + cfg->invertPanelClock = (lcdParam.invert_panel_clock == 1); + cfg->invertHsync = (lcdParam.invert_hsync == 1); + cfg->invertVsync = (lcdParam.invert_vsync == 1); + cfg->acBias = lcdParam.ac_bias_frequency; + cfg->bpp = LcdController::Bpp_16_565; + cfg->optimalClock = lcdParam.optimal_clock; + cfg->panelType = (LcdController::LcdPanel)lcdParam.lcd_panel_type; + cfg->dualPanel = (lcdParam.dual_panel == 1); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getDisplayName(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if (len < NameBufferSize) { + return BufferTooSmall; + } + + strncpy(buf, (char*)h.lcd_name, NameBufferSize); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getDisplayMfg(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if (len < NameBufferSize) { + return BufferTooSmall; + } + + strncpy(buf, (char*)h.lcd_mfg, NameBufferSize); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getInitSeq(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if ((h.pdOff-h.initOff) > len) { + return BufferTooSmall; + } + + eepromRead((uint8_t*)buf, h.initOff, + (h.pdOff-h.initOff)); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getPowerDownSeq(char* buf, int len) { + store_t h; + + getStore(&h); + + if (h.magic != LCDB_MAGIC) { + return InvalidStorage; + } + + if ((h.tsOff-h.pdOff) > len) { + return BufferTooSmall; + } + + eepromRead((uint8_t*)buf, h.pdOff, + (h.tsOff-h.pdOff)); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::getStore(store_t* store) { + int num = 0; + + if (store == NULL) return InvalidArgument; + + num = eepromRead((uint8_t*)store, 0, sizeof(store_t)); + if (num < (int)sizeof(store_t)) { + return InvalidStorage; + } + + return Ok; +} + +// ########################### +// An EEPROM is used for persistent storage +// ########################### + +int EaLcdBoard::eepromRead(uint8_t* buf, uint16_t offset, uint16_t len) { + int i = 0; + char off[2]; + + if (len > LCDB_EEPROM_TOTAL_SIZE || offset+len > LCDB_EEPROM_TOTAL_SIZE) { + return -1; + } + + off[0] = ((offset >> 8) & 0xff); + off[1] = (offset & 0xff); + + _i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)off, 2); + for ( i = 0; i < 0x2000; i++); + _i2c.read(LCDB_EEPROM_I2C_ADDR, (char*)buf, len); + + return len; +} + +int EaLcdBoard::eepromWrite(uint8_t* buf, uint16_t offset, uint16_t len) { + int16_t written = 0; + uint16_t wLen = 0; + uint16_t off = offset; + uint8_t tmp[LCDB_EEPROM_PAGE_SIZE+2]; + + if (len > LCDB_EEPROM_TOTAL_SIZE || offset+len > LCDB_EEPROM_TOTAL_SIZE) { + return -1; + } + + wLen = LCDB_EEPROM_PAGE_SIZE - (off % LCDB_EEPROM_PAGE_SIZE); + wLen = MIN(wLen, len); + + while (len) { + tmp[0] = ((off >> 8) & 0xff); + tmp[1] = (off & 0xff); + memcpy(&tmp[2], (void*)&buf[written], wLen); + _i2c.write(LCDB_EEPROM_I2C_ADDR, (char*)tmp, wLen+2); + + // delay to wait for a write cycle + //eepromDelay(); + wait_ms(1); + + len -= wLen; + written += wLen; + off += wLen; + + wLen = MIN(LCDB_EEPROM_PAGE_SIZE, len); + } + + return written; +} + +// ########################### +// string parsing (initialization and power down strings) +// ########################### + +EaLcdBoard::Result EaLcdBoard::parseInitString(char* str, LcdController::Config* cfg) { + char* c = NULL; + uint32_t len = 0; + Result result = Ok; + + if (str == NULL) { + return InvalidCommandString; + } + + while(*str != '\0') { + + // skip whitespaces + while(*str == ' ') { + str++; + } + + c = str; + + // find end of command + while(*str != ',' && *str != '\0') { + str++; + } + + len = (str-c); + + if (*str == ',') { + str++; + } + + switch (*c++) { + + case 'v': + result = checkVersion(c, len-1); + break; + + // sequence control command (pca9532) + case 'c': + execSeqCtrl(c, len-1); + break; + + // delay + case 'd': + execDelay(c, len-1); + break; + + // open lcd (init LCD controller) + case 'o': + + if (cfg != NULL) { + + if (lcdCtrl.open(cfg) != 0) { + result = LcdAccessError; + } + } + + else { + result = InvalidArgument; + } + + break; + + } + + if (result != Ok) { + break; + } + + + } + + + return result; +} + +EaLcdBoard::Result EaLcdBoard::checkVersion(char* v, uint32_t len) { + uint32_t ver = str_to_uint(v, len); + + if (ver > LCDB_SEQ_VER) { + return VersionNotSupported; + } + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::execDelay(char* del, uint32_t len) { + wait_ms(str_to_uint(del, len)); + + return Ok; +} + +EaLcdBoard::Result EaLcdBoard::execSeqCtrl(char* cmd, uint32_t len) { + + switch (*cmd++) { + + // display enable + case 'd': + setDisplayEnableSignal(*cmd == '1'); + break; + + // backlight contrast + case 'c': + setBacklightContrast(str_to_uint(cmd, len)); + break; + + // 3v3 enable + case '3': + set3V3Signal(*cmd == '1'); + break; + + // 5v enable + case '5': + set5VSignal(*cmd == '1'); + break; + } + + return Ok; +} + +// ########################### +// PCA9532 is used as a control inteface to the display. +// voltages can be turned on/off and backlight can be controlled. +// ########################### + +// Helper function to set LED states +void EaLcdBoard::setLsStates(uint16_t states, uint8_t* ls, uint8_t mode) +{ +#define IS_LED_SET(bit, x) ( ( ((x) & (bit)) != 0 ) ? 1 : 0 ) + + int i = 0; + + for (i = 0; i < 4; i++) { + + ls[i] |= ( (IS_LED_SET(0x0001, states)*mode << 0) + | (IS_LED_SET(0x0002, states)*mode << 2) + | (IS_LED_SET(0x0004, states)*mode << 4) + | (IS_LED_SET(0x0008, states)*mode << 6) ); + + states >>= 4; + } +} + +void EaLcdBoard::setLeds(void) +{ + uint8_t buf[5]; + uint8_t ls[4] = {0,0,0,0}; + uint16_t states = _ledStateShadow; + + // LEDs in On/Off state + setLsStates(states, ls, LCDB_LS_MODE_ON); + + // set the LEDs that should blink + setLsStates(_blink0Shadow, ls, LCDB_LS_MODE_BLINK0); + setLsStates(_blink1Shadow, ls, LCDB_LS_MODE_BLINK1); + + buf[0] = LCDB_PCA9532_LS0 | LCDB_PCA9532_AUTO_INC; + buf[1] = ls[0]; + buf[2] = ls[1]; + buf[3] = ls[2]; + buf[4] = ls[3]; + + _i2c.write(LCDB_PCA9532_I2C_ADDR, (char*)buf, 5); +} + +void EaLcdBoard::pca9532_setLeds (uint16_t ledOnMask, uint16_t ledOffMask) +{ + // turn off leds + _ledStateShadow &= (~(ledOffMask) & 0xffff); + + // ledOnMask has priority over ledOffMask + _ledStateShadow |= ledOnMask; + + // turn off blinking + _blink0Shadow &= (~(ledOffMask) & 0xffff); + _blink1Shadow &= (~(ledOffMask) & 0xffff); + + setLeds(); +} + +void EaLcdBoard::pca9532_setBlink0Period(uint8_t period) +{ + uint8_t buf[2]; + + buf[0] = LCDB_PCA9532_PSC0; + buf[1] = period; + + _i2c.write(LCDB_PCA9532_I2C_ADDR, (char*)buf, 2); +} + +void EaLcdBoard::pca9532_setBlink0Duty(uint8_t duty) +{ + uint8_t buf[2]; + uint32_t tmp = duty; + if (tmp > 100) { + tmp = 100; + } + + tmp = (255 * tmp)/100; + + buf[0] = LCDB_PCA9532_PWM0; + buf[1] = tmp; + + _i2c.write(LCDB_PCA9532_I2C_ADDR, (char*)buf, 2); +} + +void EaLcdBoard::pca9532_setBlink0Leds(uint16_t ledMask) +{ + _blink0Shadow |= ledMask; + setLeds(); +} + +void EaLcdBoard::set3V3Signal(bool enabled) { + if (enabled) { + pca9532_setLeds(LCDB_CTRL_3V3, 0); + } else { + pca9532_setLeds(0, LCDB_CTRL_3V3); + } +} + +void EaLcdBoard::set5VSignal(bool enabled) { + if (enabled) { + pca9532_setLeds(LCDB_CTRL_5V, 0); + } else { + pca9532_setLeds(0, LCDB_CTRL_5V); + } +} + +void EaLcdBoard::setDisplayEnableSignal(bool enabled) { + if (!enabled) { + pca9532_setLeds(LCDB_CTRL_DISP_EN, 0); + } else { + pca9532_setLeds(0, LCDB_CTRL_DISP_EN); + } +} + +void EaLcdBoard::setBacklightContrast(uint32_t value) { + + if (value > 100) return; + + pca9532_setBlink0Duty(100-value); + pca9532_setBlink0Period(0); + pca9532_setBlink0Leds(LCDB_CTRL_BL_C); +} + + +// convert string to integer +static uint32_t str_to_uint(char* str, uint32_t len) +{ + uint32_t val = 0; + + while(len > 0 && *str <= '9' && *str >= '0') { + val = (val * 10) + (*str - '0'); + str++; + len--; + } + + return val; +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EaLcdBoard.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,161 @@ + +#ifndef EALCDBOARD_H +#define EALCDBOARD_H + +#include "LcdController.h" + +/** An interface to Embedded Artists LCD Boards + * + */ +class EaLcdBoard { +public: + + enum Result { + Ok = 0, + InvalidCommandString, + InvalidArgument, + InvalidStorage, + BufferTooSmall, + VersionNotSupported, + LcdAccessError + }; + + enum Constants { + NameBufferSize = 30 + }; + + /** Create an interface to an Embedded Artists LCD Board + * + * @param sda I2C data line pin + * @param scl I2C clock line pin + */ + EaLcdBoard(PinName sda, PinName scl); + + /** Open the interface and start initialization. + * + * @param cfg initialize with a given LCD configuration. If this argument is + * NULL the LCD configuration will be retrieved from persistent + * storage on the LCD Board. + * @param initSeq the initialization string. If this argument is NULL the + * initialization string will be retrieved from persistent + * storage on the LCD Board. + * + * @returns the result of the operation + */ + Result open(LcdController::Config* cfg, char* initSeq); + + /** Close the interface + * + * @returns the result of the operation + */ + Result close(); + + /** Set and activate the address of the frame buffer to use. + * + * It is the content of the frame buffer that is shown on the + * display. All the drawing on the frame buffer can be done + * 'offline' and whenever it should be shown this function + * can be called with the address of the offline frame buffer. + * + * @param address Memory address of the frame buffer + * + * @returns the result of the operation + */ + Result setFrameBuffer(uint32_t address); + + /** Get the LCD configuration stored in persistent storage on the LCD Board + * + * @param cfg pointer to a configuration object. Parameters are copied to + * this object. + * + * @returns the result of the operation + */ + Result getLcdConfig(LcdController::Config* cfg); + + /** Get the display name stored in persistent storage on the LCD Board + * + * @param buf buffer to which the name will be copied + * @param len size of the buffer in bytes + * + * @returns the result of the operation + */ + Result getDisplayName(char* buf, int len); + + /** Get the display manufacturer stored in persistent storage on the + * LCD Board + * + * @param buf buffer to which the name will be copied + * @param len size of the buffer in bytes + * + * @returns the result of the operation + */ + Result getDisplayMfg(char* buf, int len); + + /** Get the initialization sequence stored in persistent storage on the + * LCD Board + * + * @param buf buffer to which the string will be copied + * @param len size of the buffer in bytes + * + * @returns the result of the operation + */ + Result getInitSeq(char* buf, int len); + + /** Get the power down sequence stored in persistent storage on the + * LCD Board + * + * @param buf buffer to which the string will be copied + * @param len size of the buffer in bytes + * + * @returns the result of the operation + */ + Result getPowerDownSeq(char* buf, int len); + + +private: + + typedef struct { + uint32_t magic; // magic number + uint8_t lcd_name[NameBufferSize]; // LCD name + uint8_t lcd_mfg[NameBufferSize]; // manufacturer name + uint16_t lcdParamOff; // offset to LCD parameters + uint16_t initOff; // offset to init sequence string + uint16_t pdOff; // offset to power down sequence string + uint16_t tsOff; // offset to touch parameters + uint16_t end; // end offset + } store_t; + + Result getStore(store_t* store); + + int eepromRead(uint8_t* buf, uint16_t offset, uint16_t len); + int eepromWrite(uint8_t* buf, uint16_t offset, uint16_t len); + + Result parseInitString(char* str, LcdController::Config* cfg); + Result checkVersion(char* v, uint32_t len); + Result execDelay(char* del, uint32_t len); + Result execSeqCtrl(char* cmd, uint32_t len); + + void setLsStates(uint16_t states, uint8_t* ls, uint8_t mode); + void setLeds(void); + void pca9532_setLeds (uint16_t ledOnMask, uint16_t ledOffMask); + void pca9532_setBlink0Period(uint8_t period); + void pca9532_setBlink0Duty(uint8_t duty); + void pca9532_setBlink0Leds(uint16_t ledMask); + + void set3V3Signal(bool enabled); + void set5VSignal(bool enabled); + void setDisplayEnableSignal(bool enabled); + void setBacklightContrast(uint32_t value); + + I2C _i2c; + LcdController::Config _cfg; + LcdController lcdCtrl; + uint16_t _blink0Shadow; + uint16_t _blink1Shadow; + uint16_t _ledStateShadow; + bool _lcdPwrOn; +}; + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GFXFb.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,94 @@ +#include "mbed.h" +#include "GFXFb.h" + + +GFXFb::GFXFb(uint16_t w, uint16_t h, uint16_t* fb) : Adafruit_GFX(w, h) { + _fb = fb; +} + + +void GFXFb::drawPixel(int16_t x, int16_t y, uint16_t color) { + if (_fb == 0) return; + + if (x < 0 || x >= width() || y < 0 || y >= height()) return; + + *(_fb + x + y*_width ) = color; +} + +void GFXFb::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + int16_t y2 = y + h - 1; + + if (y < 0) y = 0; + if (y2 >= _height) y2 = _height-1; + + if (_fb == 0) return; + if (x < 0 || x >= _width || y >= _height || y2 < y) return; + + uint16_t* f = (_fb + x + y*_width); + while(y <= y2) { + + *f = color; + f += _width; + y++; + } + +} + +void GFXFb::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + int16_t x2 = x + w - 1; + + if (x < 0) x = 0; + if (x2 >= _width) x2 = _width-1; + + if (_fb == 0) return; + if (x >= _width || x2 < x || y < 0 || y >= _height) return; + + uint16_t* f = (_fb + x + y*_width); + while(x <= x2) { + + *f++ = color; + x++; + } + +} + + +void GFXFb::fillScreen(uint16_t color) { + + if (_fb == 0) return; + + int len = _width*_height; + for (int i = 0; i < len; i++) { + *(_fb+i) = color; + } +} + +void GFXFb::writeString(const char* s) { + if (s == NULL) return; + + while(*s != 0) { + write(*s); + s++; + } +} + +int16_t GFXFb::getStringWidth(const char* s) { + // the default font in GFX is 6 pixels in width + int chWidth = 6*textsize; + int sz = 0; + + while(*s != 0) { + sz += chWidth; + s++; + } + + return sz; +} + +int16_t GFXFb::getStringHeight(const char* s) { + (void)s; + // the default font in GFX is 8 pixels in height + return 8; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GFXFb.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,100 @@ + +#ifndef GFXFB_H +#define GFXFB_H + +#include "Adafruit_GFX.h" + +#define BLACK 0x0000 +/* Light gray color, 565 mode */ +#define LIGHTGRAY 0X7BEF +/* Dark gray color, 565 mode */ +#define DARKGRAY 0x39E7 +/* White color, 565 mode */ +#define WHITE 0xffff +/* Red color, 565 mode */ +#define RED 0xF800 +/* Green color, 565 mode */ +#define GREEN 0x07E0 +/* Blue color, 565 mode */ +#define BLUE 0x001F +/* Magenta color, 565 mode */ +#define MAGENTA (RED | BLUE) +/* Cyan color, 565 mode */ +#define CYAN (GREEN | BLUE) +/* Yellow color, 565 mode */ +#define YELLOW (RED | GREEN) +/* Light red color, 565 mode */ +#define LIGHTRED 0x7800 +/* Light green color, 565 mode */ +#define LIGHTGREEN 0x03E0 +/* Light blue color, 565 mode */ +#define LIGHTBLUE 0x000F +/* Light magenta color, 565 mode */ +#define LIGHTMAGENTA (LIGHTRED | LIGHTBLUE) +/* Light cyan color, 565 mode */ +#define LIGHTCYAN (LIGHTGREEN | LIGHTBLUE) +/* Light yellow color, 565 mode */ +#define LIGHTYELLOW (LIGHTRED | LIGHTGREEN) + +/** + * Graphical library based on Adafruit's GFX using a Frame buffer. + */ +class GFXFb : public Adafruit_GFX { +public: + + /** Create an interface to the GFX graphical library + * + * @param w width of the display + * @param h height of the display + * @param fb frame buffer that will be used by the graphical library. Can + * be set by calling setFb instead. + */ + GFXFb(uint16_t w, uint16_t h, uint16_t* fb = 0); + + virtual void drawPixel(int16_t x, int16_t y, uint16_t color); + virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + virtual void fillScreen(uint16_t color); + + /** Associate a frame buffer with the graphical library. + * All drawing requests will be performed on the frame buffer. + * + * @param fb frame buffer that will be used by the graphical library + */ + void setFb(uint16_t* fb) {_fb = fb;} + + /** Get the frame buffer associated with the graphical library. + * + * @returns the frame buffer associated with the graphical library. + */ + uint16_t* getFb() {return _fb;} + + /** Write a null-terminated string + * + * @param s the string to write on the display + */ + void writeString(const char* s); + + /** Get the width in pixels of the given string. + * + * @param s width will be calculated on this string + * @returns the width in pixels of the string + */ + int16_t getStringWidth(const char* s); + + /** Get the height in pixels of the given string. + * + * @param s height will be calculated on this string + * @returns the height in pixels of the string + */ + int16_t getStringHeight(const char* s); + + +private: + + uint16_t* _fb; +}; + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LcdController.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,437 @@ + +#include "mbed.h" +#include "LcdController.h" + +#undef _SBF +#define _SBF(p,v) (((uint32_t)(v)) << (p)) + +#undef _BITMASK +#define _BITMASK(field_width) (_BIT(field_width) - 1) + +#undef _BIT +#define _BIT(p) (((uint32_t)(1)) << (p)) + +/*********************************************************************** + * Color LCD controller horizontal axis plane control register definitions + **********************************************************************/ + +/* LCD controller horizontal axis plane control register pixels per line */ +#define CLCDC_LCDTIMING0_PPL_WIDTH 6 +#define CLCDC_LCDTIMING0_PPL(n) _SBF(2, (((n) / 16) - 1) & _BITMASK(CLCDC_LCDTIMING0_PPL_WIDTH)) +/* LCD controller horizontal axis plane control register HSYNC pulse width */ +#define CLCDC_LCDTIMING0_HSW_WIDTH 8 +#define CLCDC_LCDTIMING0_HSW(n) _SBF(8, ((n) - 1) & _BITMASK(CLCDC_LCDTIMING0_HSW_WIDTH)) +/* LCD controller horizontal axis plane control register horizontal front porch */ +#define CLCDC_LCDTIMING0_HFP_WIDTH 8 +#define CLCDC_LCDTIMING0_HFP(n) _SBF(16, ((n) - 1) & _BITMASK(CLCDC_LCDTIMING0_HFP_WIDTH)) +/* LCD controller horizontal axis plane control register horizontal back porch */ +#define CLCDC_LCDTIMING0_HBP_WIDTH 8 +#define CLCDC_LCDTIMING0_HBP(n) _SBF(24, ((n) - 1) & _BITMASK(CLCDC_LCDTIMING0_HBP_WIDTH)) + +/*********************************************************************** + * Color LCD controller vertical axis plane control register definitions + **********************************************************************/ + +/* LCD controller vertical axis plane control register lines per panel */ +#define CLCDC_LCDTIMING1_LPP_WIDTH 10 +#define CLCDC_LCDTIMING1_LPP(n) _SBF(0, ((n) - 1) & _BITMASK(CLCDC_LCDTIMING1_LPP_WIDTH)) +/* LCD controller vertical axis plane control register VSYNC pulse width */ +#define CLCDC_LCDTIMING1_VSW_WIDTH 6 +#define CLCDC_LCDTIMING1_VSW(n) _SBF(10, ((n) - 1) & _BITMASK(CLCDC_LCDTIMING1_VSW_WIDTH)) +/* LCD controller vertical axis plane control register vertical front porch */ +#define CLCDC_LCDTIMING1_VFP_WIDTH 8 +#define CLCDC_LCDTIMING1_VFP(n) _SBF(16, (n) & _BITMASK(CLCDC_LCDTIMING1_VFP_WIDTH)) +/* LCD controller vertical axis plane control register vertical back porch */ +#define CLCDC_LCDTIMING1_VBP_WIDTH 8 +#define CLCDC_LCDTIMING1_VBP(n) _SBF(24, (n) & _BITMASK(CLCDC_LCDTIMING1_VBP_WIDTH)) + +/*********************************************************************** + * Color LCD controller clock and signal polarity control register definitions + **********************************************************************/ + +/* LCD controller clock and signal polarity control register panel clock divisor low*/ +#define CLCDC_LCDTIMING2_PCD_LO_WIDTH 5 +#define CLCDC_LCDTIMING2_PCD_LO(n) _SBF(0, ((n) - 2) & _BITMASK(CLCDC_LCDTIMING2_PCD_LO_WIDTH)) +/* LCD controller clock and signal polarity control register clock select */ +#define CLCDC_LCDTIMING2_CLKSEL _BIT(5) +/* LCD controller clock and signal polarity control register AC bias pin frequency */ +#define CLCDC_LCDTIMING2_ACB_WIDTH 5 +#define CLCDC_LCDTIMING2_ACB(n) _SBF(6, ((n) - 1) & _BITMASK(CLCDC_LCDTIMING2_ACB_WIDTH)) +/* LCD controller clock and signal polarity control register invert VSYNC */ +#define CLCDC_LCDTIMING2_IVS _BIT(11) +/* LCD controller clock and signal polarity control register invert HSYNC */ +#define CLCDC_LCDTIMING2_IHS _BIT(12) +/* LCD controller clock and signal polarity control register invert plane clock */ +#define CLCDC_LCDTIMING2_IPC _BIT(13) +/* LCD controller clock and signal polarity control register invert output enable */ +#define CLCDC_LCDTIMING2_IOE _BIT(14) +/* LCD controller clock and signal polarity control register clocks per line */ +#define CLCDC_LCDTIMING2_CPL_WIDTH 10 +#define CLCDC_LCDTIMING2_CPL(n) _SBF(16, (n) & _BITMASK(CLCDC_LCDTIMING2_CPL_WIDTH)) +/* LCD controller clock and signal polarity control register bypass pixel clock divider */ +#define CLCDC_LCDTIMING2_BCD _BIT(26) +/* LCD controller clock and signal polarity control register panel clock divisor high*/ +#define CLCDC_LCDTIMING2_PCD_HI_WIDTH 5 +#define CLCDC_LCDTIMING2_PCD_HI(n) _SBF((27 - CLCDC_LCDTIMING2_PCD_LO_WIDTH), ((n) - 2) & _SBF(CLCDC_LCDTIMING2_PCD_LO_WIDTH, _BITMASK(CLCDC_LCDTIMING2_PCD_HI_WIDTH))) + + +/*********************************************************************** + * Color LCD controller control register definitions + **********************************************************************/ + +/* LCD control enable bit */ +#define CLCDC_LCDCTRL_ENABLE (1<<0) +/* LCD control 1 bit per pixel bit field */ +#define CLCDC_LCDCTRL_BPP1 (0 << 1) +/* LCD control 2 bits per pixel bit field */ +#define CLCDC_LCDCTRL_BPP2 (1 << 1) +/* LCD control 4 bits per pixel bit field */ +#define CLCDC_LCDCTRL_BPP4 (2 << 1) +/* LCD control 8 bits per pixel bit field */ +#define CLCDC_LCDCTRL_BPP8 (3 << 1) +/* LCD control 16 bits per pixel bit field */ +#define CLCDC_LCDCTRL_BPP16 (4 << 1) +/* LCD control 24 bits per pixel bit field */ +#define CLCDC_LCDCTRL_BPP24 (5 << 1) +/* LCD control 16 bits (5:6:5 mode) per pixel bit field */ +#define CLCDC_LCDCTRL_BPP16_565_MODE (6 << 1) +/* LCD control 12 bits (4:4:4 mode) per pixel bit field */ +#define CLCDC_LCDCTRL_BPP12_444_MODE (7 << 1) +/* LCD control mono select bit */ +#define CLCDC_LCDCTRL_BW_COLOR (0 << 4) +#define CLCDC_LCDCTRL_BW_MONO (1 << 4) +/* LCD controler TFT select bit */ +#define CLCDC_LCDCTRL_TFT (1 << 5) +/* LCD control monochrome LCD has 4-bit/8-bit select bit */ +#define CLCDC_LCDCTRL_MON8 (1 << 6) +/* LCD control dual panel select bit */ +#define CLCDC_LCDCTRL_DUAL (1 << 7) +/* LCD control RGB or BGR format select bit */ +#define CLCDC_LCDCTRL_RGB (0 << 8) +#define CLCDC_LCDCTRL_BGR (1 << 8) +/* LCD control big-endian byte order select bit */ +#define CLCDC_LCDCTRL_BEBO (1 << 9) +/* LCD control big-endian pixel order within a byte select bit */ +#define CLCDC_LCDCTRL_BEPO (1 << 10) +/* LCD control power enable bit */ +#define CLCDC_LCDCTRL_PWR (1 << 11) +/* LCD control VCOMP interrupt is start of VSYNC */ +#define CLCDC_LCDCTRL_VCOMP_VS (0 << 12) +/* LCD control VCOMP interrupt is start of back porch */ +#define CLCDC_LCDCTRL_VCOMP_BP (1 << 12) +/* LCD control VCOMP interrupt is start of active video */ +#define CLCDC_LCDCTRL_VCOMP_AV (2 << 12) +/* LCD control VCOMP interrupt is start of front porch */ +#define CLCDC_LCDCTRL_VCOMP_FP (3 << 12) +/* LCD control watermark level is 8 or more words free bit */ +#define CLCDC_LCDCTRL_WATERMARK (1 << 16) + + + +bool LcdController::_lcdControllerUsed = false; + +LcdController::LcdController() { + _opened = false; +} + +int LcdController::open(LcdController::Config* cfg) { + if (_lcdControllerUsed) return 1; + if (cfg == NULL) return 1; + + // enable power for LCD controller + LPC_SC->PCONP |= 0x00000001; + + pinConfig(); + init(cfg); + + // only one instance at a time is allowed to be used + _lcdControllerUsed = true; + _opened = true; + + return 0; +} + +int LcdController::close() { + + if (!_opened) return 1; + + if (_lcdControllerUsed) { + + // disable power for LCD controller + LPC_SC->PCONP &= ~(0x00000001); + + _lcdControllerUsed = false; + _opened = false; + } + + + return 0; +} + +int LcdController::setFrameBuffer(uint32_t address) { + if (!_opened) return 1; + + LPC_LCD->UPBASE = address; + + return 0; +} + +int LcdController::setPower(bool on) { + if (!_opened) return 1; + + if (on) { + LPC_LCD->CTRL |= CLCDC_LCDCTRL_ENABLE; + LPC_LCD->CTRL |= CLCDC_LCDCTRL_PWR; + } + else { + LPC_LCD->CTRL &= ~CLCDC_LCDCTRL_PWR; + LPC_LCD->CTRL &= ~CLCDC_LCDCTRL_ENABLE; + } + + return 0; +} + +void LcdController::init(LcdController::Config* cfg) { + uint32_t tmp, i; + + // Disable the display in case it is on + LPC_LCD->CTRL &= ~CLCDC_LCDCTRL_ENABLE; + + // Generate the horizontal axis plane control word + tmp = (CLCDC_LCDTIMING0_PPL(cfg->width) | + CLCDC_LCDTIMING0_HSW(cfg->hsync) | + CLCDC_LCDTIMING0_HFP(cfg->horizontalFrontPorch) | + CLCDC_LCDTIMING0_HBP(cfg->horizontalBackPorch)); + LPC_LCD->TIMH = tmp; + + // Generate the vertical axis plane control word + tmp = (CLCDC_LCDTIMING1_LPP(cfg->height) | + CLCDC_LCDTIMING1_VSW(cfg->vsync) | + CLCDC_LCDTIMING1_VFP(cfg->verticalFrontPorch) | + CLCDC_LCDTIMING1_VBP(cfg->verticalBackPorch)); + LPC_LCD->TIMV = tmp; + + // Generate the clock and signal polarity control word + if(cfg->acBias != 0) + { + /* STN panel has AC bias value */ + tmp = CLCDC_LCDTIMING2_ACB(cfg->acBias); + } + else + { + tmp = 0; + } + + if (cfg->invertOutputEnable) + { + tmp |= CLCDC_LCDTIMING2_IOE; + } + + if (cfg->invertPanelClock) + { + tmp |= CLCDC_LCDTIMING2_IPC; + } + + if (cfg->invertHsync) + { + tmp |= CLCDC_LCDTIMING2_IHS; + } + + if (cfg->invertVsync) + { + tmp |= CLCDC_LCDTIMING2_IVS; + } + + // Compute clocks per line based on panel type + switch (cfg->panelType) + { + case Mono_4Bit: + // Clocks per line is a quarter of pixels per line + tmp = tmp | CLCDC_LCDTIMING2_CPL((cfg->width / 4) - 1); + break; + + case Mono_8Bit: + // Clocks per line is an eighth of pixels per line + tmp = tmp | CLCDC_LCDTIMING2_CPL((cfg->width / 8) - 1); + break; + + case ColorStn: + // CSTN Clocks per line (* 3 / 8) + tmp = tmp | CLCDC_LCDTIMING2_CPL(((cfg->width * 3) / 8) - 1); + break; + + case Tft: + case AdTft: + case HrTft: + default: + // Clocks per line and pixels per line are the same + tmp = tmp | CLCDC_LCDTIMING2_CPL(cfg->width - 1); + break; + } + + // clock + tmp = tmp | getClockDivisor(cfg->optimalClock); + + LPC_LCD->POL = tmp; + + // Skip line end control word - just set to 0x0 + LPC_LCD->LE = 0x00000000; + + // Default with all interrupts of + LPC_LCD->INTMSK = 0x00000000; + + + switch(cfg->bpp) { + case Bpp_1: + tmp = CLCDC_LCDCTRL_BPP1; + break; + case Bpp_2: + tmp = CLCDC_LCDCTRL_BPP2; + break; + case Bpp_4: + tmp = CLCDC_LCDCTRL_BPP4; + break; + case Bpp_8: + tmp = CLCDC_LCDCTRL_BPP8; + break; + case Bpp_16: + tmp = CLCDC_LCDCTRL_BPP16; + break; + case Bpp_24: + tmp = CLCDC_LCDCTRL_BPP24; + break; + case Bpp_16_565: + tmp = CLCDC_LCDCTRL_BPP16_565_MODE; + break; + case Bpp_12_444: + tmp = CLCDC_LCDCTRL_BPP12_444_MODE; + break; + default: + tmp = CLCDC_LCDCTRL_BPP16_565_MODE; + break; + } + + // red and blue swapped + tmp |= CLCDC_LCDCTRL_BGR; + + switch (cfg->panelType) + { + case AdTft: + case HrTft: + case Tft: + tmp |= CLCDC_LCDCTRL_TFT; + break; + + case Mono_4Bit: + tmp |= CLCDC_LCDCTRL_BW_MONO; + break; + + case Mono_8Bit: + tmp |= (CLCDC_LCDCTRL_MON8 | CLCDC_LCDCTRL_BW_MONO); + break; + + case ColorStn: + ; + break; + + default: + // Unsupported panel type + break; + } + + // Dual panel operation + if (cfg->dualPanel) + { + tmp |= CLCDC_LCDCTRL_DUAL; + } + + LPC_LCD->CTRL = tmp; + + // clear the palette (color is black ) + for (i = 0; i < sizeof(LPC_LCD->PAL)/sizeof(LPC_LCD->PAL[0]); i++) + { + LPC_LCD->PAL[i] = 0; + } + + LPC_SC->LCD_CFG = 0x0; + +} + +void LcdController::pinConfig() { + + LPC_IOCON->P0_4 |= 7; /* LCD_VD_0 @ P0.4 */ + LPC_IOCON->P0_5 |= 7; /* LCD_VD_1 @ P0.5 */ + LPC_IOCON->P0_6 |= 7; /* LCD_VD_8 @ P0.6 */ + LPC_IOCON->P0_7 |= 7; /* LCD_VD_9 @ P0.7 */ + LPC_IOCON->P0_8 |= 7; /* LCD_VD_16 @ P0.8 */ + LPC_IOCON->P0_9 |= 7; /* LCD_VD_17 @ P0.9 */ + LPC_IOCON->P0_10 |= 7; /* LCD_VD_5 @ P0.10 */ /* LPC4088 */ + +#ifdef LPC4088_OEM + LPC_IOCON->P1_20 |= 7; /* LCD_VD_10 @ P1.20 */ + LPC_IOCON->P1_23 |= 7; /* LCD_VD_13 @ P1.23 */ + LPC_IOCON->P1_24 |= 7; /* LCD_VD_14 @ P1.24 */ +#else + LPC_IOCON->P0_11 |= 7; /* LCD_VD_10 @ P0.11 */ + LPC_IOCON->P0_19 |= 7; /* LCD_VD_13 @ P0.19 */ + LPC_IOCON->P0_20 |= 7; /* LCD_VD_14 @ P0.20 */ +#endif + + LPC_IOCON->P1_21 |= 7; /* LCD_VD_11 @ P1.21 */ + LPC_IOCON->P1_22 |= 7; /* LCD_VD_12 @ P1.22 */ + + LPC_IOCON->P1_25 |= 7; /* LCD_VD_15 @ P1.25 */ + LPC_IOCON->P1_26 |= 7; /* LCD_VD_20 @ P1.26 */ + LPC_IOCON->P1_27 |= 7; /* LCD_VD_21 @ P1.27 */ + LPC_IOCON->P1_28 |= 7; /* LCD_VD_22 @ P1.28 */ + LPC_IOCON->P1_29 |= 7; /* LCD_VD_23 @ P1.29 */ + + LPC_IOCON->P2_0 |= 7; /* LCD_PWR @ P2.0 */ + LPC_IOCON->P2_1 |= 7; /* LCD_LE @ P2.1 */ + LPC_IOCON->P2_2 |= 7; /* LCD_DCLK @ P2.2 */ + LPC_IOCON->P2_3 |= 7; /* LCD_FP @ P2.3 */ + LPC_IOCON->P2_4 |= 7; /* LCD_ENAB_M @ P2.4 */ + LPC_IOCON->P2_5 |= 7; /* LCD_LP @ P2.5 */ + LPC_IOCON->P2_6 |= 7; /* LCD_VD_4 @ P2.6 */ + //LPC_IOCON->P2_7 |= 7; /* LCD_VD_5 @ P2.7 */ /* LPC4088 */ + LPC_IOCON->P2_8 |= 7; /* LCD_VD_6 @ P2.8 */ + LPC_IOCON->P2_9 |= 7; /* LCD_VD_7 @ P2.9 */ + + LPC_IOCON->P2_11 |= 7; /* LCD_CLKIN @ P2.11 */ + LPC_IOCON->P2_12 |= 5; /* LCD_VD_3 @ P2.12 Signal marked as LCD_VD_18 on base board, but shall carry the LCD_VD_3 signal */ + LPC_IOCON->P2_13 |= 7; /* LCD_VD_19 @ P2.13 */ +} + +uint32_t LcdController::getClockDivisor(int clock) { + uint32_t pixel_div, tmp = 0; + uint32_t clk; + + clk = SystemCoreClock; + + // Find closest clock divider to get clock rate + pixel_div = 1; + while (((clk / pixel_div) > clock) && (pixel_div <= 0x3F)) + { + pixel_div++; + } + + if (pixel_div <= 1) + { + // Pixel clock divider is 1, skip divider logic + tmp = CLCDC_LCDTIMING2_BCD; + } + else + { + // Add in new divider + pixel_div -= 2; + + tmp |= (((pixel_div >> 0) & 0x1F) + | (((pixel_div >> 5) & 0x1F) << 27)); + } + + return tmp; +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LcdController.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,215 @@ + +#ifndef LCDCONTROLLER_H +#define LCDCONTROLLER_H + +/** + * A LCD controller interface + * + * @code + * #include "mbed.h" + * #include "LcdController.h" + * + * LcdController::Config innolux( + * 45, + * 17, + * 2, + * 800, + * 22, + * 22, + * 2, + * 480, + * false, + * false, + * true, + * true, + * true, + * LcdController::Bpp_16_565, + * 36000000, + * LcdController::Tft, + * false); + * + * int main(void) { + * LcdController lcd; + * + * lcd.open(&innolux); + * lcd.setFrameBuffer(frameBuffer); + * lcd.setPower(true); + * + * // draw on the frame buffer + * ... + * } + * @endcode + */ +class LcdController { +public: + + enum LcdPanel { + Tft = 0, // standard TFT + AdTft, // advanced TFT + HrTft, // highly reflective TFT + Mono_4Bit, // 4-bit mono + Mono_8Bit, // 8-bit mono + ColorStn // Color STN + }; + + enum BitsPerPixel { + Bpp_1 = 0, + Bpp_2, + Bpp_4, + Bpp_8, + Bpp_16, + Bpp_24, + Bpp_16_565, + Bpp_12_444 + }; + + /** + * LCD configuration + */ + class Config { + public: + + /** Create an empty LCD configuration object + * + */ + Config() { + } + + /** Create a LCD configuration object with specified values + * + * @param horizontalBackPorch Horizontal back porch in clocks + * @param horizontalFrontPorch Horizontal front porch in clocks + * @param hsync HSYNC pulse width in clocks + * @param width width of display in pixels + * @param verticalBackPorch vertical back porch in clocks + * @param verticalFrontPorch vertical front porch in clocks + * @param vsync VSYNC pulse width in clocks + * @param height height of display in pixels + * @param invertOutputEnable true to invert output enable + * @param invertPanelClock true to invert panel clock + * @param invertHsync true to invert HSYNC + * @param invertVsync true to invert VSYNC + * @param acBias AC bias frequency in clocks + * @param bpp bits per pixel + * @param optimalClock optimal clock rate (Hz) + * @param panelType LCD panel type + * @param dualPanel true if it is a dual panel display + */ + Config(int horizontalBackPorch, + int horizontalFrontPorch, + int hsync, + int width, + int verticalBackPorch, + int verticalFrontPorch, + int vsync, + int height, + bool invertOutputEnable, + bool invertPanelClock, + bool invertHsync, + bool invertVsync, + int acBias, + BitsPerPixel bpp, + int optimalClock, + LcdPanel panelType, + bool dualPanel) { + + this->horizontalBackPorch = horizontalBackPorch; + this->horizontalFrontPorch = horizontalFrontPorch; + this->hsync = hsync; + this->width = width; + this->verticalBackPorch = verticalBackPorch; + this->verticalFrontPorch = verticalFrontPorch; + this->vsync = vsync; + this->height = height; + this->invertOutputEnable = invertOutputEnable; + this->invertPanelClock = invertPanelClock; + this->invertHsync = invertHsync; + this->invertVsync = invertVsync; + this->acBias = acBias; + this->bpp = bpp; + this->optimalClock = optimalClock; + this->panelType = panelType; + this->dualPanel = dualPanel; + } + + int horizontalBackPorch; + int horizontalFrontPorch; + int hsync; + int width; + int verticalBackPorch; + int verticalFrontPorch; + int vsync; + int height; + bool invertOutputEnable; + bool invertPanelClock; + bool invertHsync; + bool invertVsync; + int acBias; + BitsPerPixel bpp; + int optimalClock; + LcdPanel panelType; + bool dualPanel; + }; + + /** Create a LCD controller interface + * + */ + LcdController(); + + /** Open and initialize the LCD controller with the given LCD configuration + * + * @param cfg LCD configuration + * + * @returns + * 0 on success + * 1 on failure + */ + int open(LcdController::Config* cfg); + + /** Close the LCD controller + * + * @returns + * 0 on success + * 1 on failure + */ + int close(); + + /** Set and activate the address of the frame buffer to use. + * + * It is the content of the frame buffer that is shown on the + * display. All the drawing on the frame buffer can be done + * 'offline' and whenever it should be shown this function + * can be called with the address of the offline frame buffer. + * + * @param address Memory address of the frame buffer + * + * @returns + * 0 on success + * 1 on failure + */ + int setFrameBuffer(uint32_t address); + + /** Turn on/off the power to the display. + * + * @param on true to turn on, false to turn off + * + * @returns + * 0 on success + * 1 on failure + */ + int setPower(bool on); + + +private: + + bool _opened; + static bool _lcdControllerUsed; + + void init(LcdController::Config* cfg); + void pinConfig(); + uint32_t getClockDivisor(int clock); +}; + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MCIFileSystem.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,1810 @@ + +/****************************************************************************** + * Includes + *****************************************************************************/ + +#include "MCIFileSystem.h" +#include "mbed_debug.h" + +#include "diskio.h" //STA_* defines +#include "gpdma.h" + +/****************************************************************************** + * Defines and typedefs + *****************************************************************************/ + +#define MCI_DBG 0 + +#define CMD_TIMEOUT (0x10000) + +#define DATA_TIMER_VALUE_R (SDC_TRAN_CLOCK_RATE / 4) // 250ms +#define DATA_TIMER_VALUE_W (SDC_TRAN_CLOCK_RATE) // 1000ms +#define ACQUIRE_DELAY (0.100f) /*!< inter-command acquire oper condition delay in seconds */ + +#define SYSCTL_CLOCK_SDC (1<<28) + +/** + * @brief SDC Clear Register bit definitions + */ +/** Clear all status flag*/ +#define SDC_CLEAR_ALL ((uint32_t) 0x7FF) + +/* + * SDMMC Card bus clock rate definitions + */ +/** Card bus clock in Card Identification Mode */ +#define SDC_IDENT_CLOCK_RATE (400000) /* 400KHz */ +/** Card bus clock in Data Transfer Mode */ +#define SDC_TRAN_CLOCK_RATE (20000000) /* 20MHz */ + +/** + * @brief SDC Power Control Register bit definitions + */ +/** SD_CMD Output Control */ +#define SDC_PWR_OPENDRAIN (((uint32_t) 1) << 6) + +/** + * @brief SDC Command Register bit definitions + */ +/** SDC Command Register Bitmask */ +#define SDC_COMMAND_BITMASK ((uint32_t) 0x7FF) +/** SDC Command Index Bitmask */ +#define SDC_COMMAND_INDEX_BITMASK ((uint32_t) 0x3F) +/** Set SDC Command Index */ +#define SDC_COMMAND_INDEX(n) ((uint32_t) n & 0x3F) +/** No response is expected */ +#define SDC_COMMAND_NO_RSP (((uint32_t) 0 ) << 6) +/** Short response is expected */ +#define SDC_COMMAND_SHORT_RSP (((uint32_t) 1 ) << 6) +/** Long response is expected */ +#define SDC_COMMAND_LONG_RSP (((uint32_t) 3 ) << 6) +/** Response bit mask */ +#define SDC_COMMAND_RSP_BITMASK (((uint32_t) 3 ) << 6) +/** Mark that command timer is disabled and CPSM waits for interrupt request */ +#define SDC_COMMAND_INTERRUPT (((uint32_t) 1 ) << 8) +/** Mark that CPSM waits for CmdPend before starting sending a command*/ +#define SDC_COMMAND_PENDING (((uint32_t) 1 ) << 9) +/** Enable CPSM */ +#define SDC_COMMAND_ENABLE (((uint32_t) 1 ) << 10) + +/** + * @brief SDC Command Response Register bit definitions + */ +/** SDC Command Response value */ +#define SDC_RESPCOMMAND_VAL(n) ((uint32_t) n & 0x3F) + +/* + * SD/MMC Response type definitions + */ +#define CMDRESP_NONE_TYPE (SDC_COMMAND_NO_RSP) +#define CMDRESP_R1_TYPE (SDC_COMMAND_SHORT_RSP) +#define CMDRESP_R1b_TYPE (SDC_COMMAND_SHORT_RSP) +#define CMDRESP_R2_TYPE (SDC_COMMAND_LONG_RSP) +#define CMDRESP_R3_TYPE (SDC_COMMAND_SHORT_RSP) +#define CMDRESP_R6_TYPE (SDC_COMMAND_SHORT_RSP) +#define CMDRESP_R7_TYPE (SDC_COMMAND_SHORT_RSP) + +/* + * SD command values (Command Index, Response) + */ +#define SD_GO_IDLE_STATE (SDC_COMMAND_INDEX(MMC_GO_IDLE_STATE) | CMDRESP_NONE_TYPE | SDC_COMMAND_INTERRUPT) /*!< GO_IDLE_STATE(MMC) or RESET(SD) */ +#define SD_CMD1_SEND_OP_COND (SDC_COMMAND_INDEX(MMC_SEND_OP_COND) | CMDRESP_R3_TYPE | 0) /*!< SEND_OP_COND(MMC) or ACMD41(SD) */ +#define SD_CMD2_ALL_SEND_CID (SDC_COMMAND_INDEX(MMC_ALL_SEND_CID) | CMDRESP_R2_TYPE | 0) /*!< ALL_SEND_CID */ +#define SD_CMD3_SET_RELATIVE_ADDR (SDC_COMMAND_INDEX(MMC_SET_RELATIVE_ADDR) | CMDRESP_R1_TYPE | 0) /*!< SET_RELATE_ADDR */ +#define SD_CMD3_SEND_RELATIVE_ADDR (SDC_COMMAND_INDEX(SD_SEND_RELATIVE_ADDR) | CMDRESP_R6_TYPE | 0) /*!< SEND_RELATE_ADDR */ +#define SD_CMD7_SELECT_CARD (SDC_COMMAND_INDEX(MMC_SELECT_CARD) | CMDRESP_R1b_TYPE | 0) /*!< SELECT/DESELECT_CARD */ +#define SD_CMD8_SEND_IF_COND (SDC_COMMAND_INDEX(SD_CMD8) | CMDRESP_R7_TYPE | 0) /*!< SEND_IF_COND */ +#define SD_CMD9_SEND_CSD (SDC_COMMAND_INDEX(MMC_SEND_CSD) | CMDRESP_R2_TYPE | 0) /*!< SEND_CSD */ +#define SD_CMD12_STOP_TRANSMISSION (SDC_COMMAND_INDEX(MMC_STOP_TRANSMISSION) | CMDRESP_R1_TYPE | 0) /*!< STOP_TRANSMISSION */ +#define SD_CMD13_SEND_STATUS (SDC_COMMAND_INDEX(MMC_SEND_STATUS) | CMDRESP_R1_TYPE | 0) /*!< SEND_STATUS */ + +/* Block-Oriented Read Commands (class 2) */ +#define SD_CMD16_SET_BLOCKLEN (SDC_COMMAND_INDEX(MMC_SET_BLOCKLEN) | CMDRESP_R1_TYPE | 0) /*!< SET_BLOCK_LEN */ +#define SD_CMD17_READ_SINGLE_BLOCK (SDC_COMMAND_INDEX(MMC_READ_SINGLE_BLOCK) | CMDRESP_R1_TYPE | 0) /*!< READ_SINGLE_BLOCK */ +#define SD_CMD18_READ_MULTIPLE_BLOCK (SDC_COMMAND_INDEX(MMC_READ_MULTIPLE_BLOCK) | CMDRESP_R1_TYPE | 0) /*!< READ_MULTIPLE_BLOCK */ + +/* Block-Oriented Write Commands (class 4) */ +#define SD_CMD24_WRITE_BLOCK (SDC_COMMAND_INDEX(MMC_WRITE_BLOCK) | CMDRESP_R1_TYPE | 0) /*!< WRITE_BLOCK */ +#define SD_CMD25_WRITE_MULTIPLE_BLOCK (SDC_COMMAND_INDEX(MMC_WRITE_MULTIPLE_BLOCK) | CMDRESP_R1_TYPE | 0) /*!< WRITE_MULTIPLE_BLOCK */ + +/* Erase Commands (class 5) */ +#define SD_CMD32_ERASE_WR_BLK_START (SDC_COMMAND_INDEX(SD_ERASE_WR_BLK_START) | CMDRESP_R1_TYPE | 0) /*!< ERASE_WR_BLK_START */ +#define SD_CMD33_ERASE_WR_BLK_END (SDC_COMMAND_INDEX(SD_ERASE_WR_BLK_END) | CMDRESP_R1_TYPE | 0) /*!< ERASE_WR_BLK_END */ +#define SD_CMD38_ERASE (SDC_COMMAND_INDEX(SD_ERASE) | CMDRESP_R1b_TYPE | 0) /*!< ERASE */ + +/* Application-Specific Commands (class 8) */ +#define SD_CMD55_APP_CMD (SDC_COMMAND_INDEX(MMC_APP_CMD) | CMDRESP_R1_TYPE | 0) /*!< APP_CMD */ +#define SD_ACMD6_SET_BUS_WIDTH (SDC_COMMAND_INDEX(SD_APP_SET_BUS_WIDTH) | CMDRESP_R1_TYPE | 0) /*!< SET_BUS_WIDTH */ +#define SD_ACMD13_SEND_SD_STATUS (SDC_COMMAND_INDEX(MMC_SEND_STATUS) | CMDRESP_R1_TYPE | 0) /*!< SEND_SD_STATUS */ +#define SD_ACMD41_SD_SEND_OP_COND (SDC_COMMAND_INDEX(SD_APP_OP_COND) | CMDRESP_R3_TYPE | 0) /*!< SD_SEND_OP_COND */ + +/** + * @brief SDC Interrupt Mask Register bit definitions + */ +/** Mask CmdCrcFail flag.*/ +#define SDC_MASK0_CMDCRCFAIL (((uint32_t) 1 ) << 0) +/** Mask DataCrcFail flag. */ +#define SDC_MASK0_DATACRCFAIL (((uint32_t) 1 ) << 1) +/** Mask CmdTimeOut flag. */ +#define SDC_MASK0_CMDTIMEOUT (((uint32_t) 1 ) << 2) +/** Mask DataTimeOut flag. */ +#define SDC_MASK0_DATATIMEOUT (((uint32_t) 1 ) << 3) +/** Mask TxUnderrun flag. */ +#define SDC_MASK0_TXUNDERRUN (((uint32_t) 1 ) << 4) +/** Mask RxOverrun flag. */ +#define SDC_MASK0_RXOVERRUN (((uint32_t) 1 ) << 5) +/** Mask CmdRespEnd flag. */ +#define SDC_MASK0_CMDRESPEND (((uint32_t) 1 ) << 6) +/** Mask CmdSent flag.*/ +#define SDC_MASK0_CMDSENT (((uint32_t) 1 ) << 7) +/** Mask DataEnd flag.*/ +#define SDC_MASK0_DATAEND (((uint32_t) 1 ) << 8) +/** Mask StartBitErr flag.*/ +#define SDC_MASK0_STARTBITERR (((uint32_t) 1 ) << 9) +/** Mask DataBlockEnd flag.*/ +#define SDC_MASK0_DATABLOCKEND (((uint32_t) 1 ) << 10) +/** Mask CmdActive flag.*/ +#define SDC_MASK0_CMDACTIVE (((uint32_t) 1 ) << 11) +/** Mask TxActive flag.*/ +#define SDC_MASK0_TXACTIVE (((uint32_t) 1 ) << 12) +/** Mask RxActive flag.*/ +#define SDC_MASK0_RXACTIVE (((uint32_t) 1 ) << 13) +/** Mask TxFifoHalfEmpty flag.*/ +#define SDC_MASK0_TXFIFOHALFEMPTY (((uint32_t) 1 ) << 14) +/** Mask RxFifoHalfFull flag.*/ +#define SDC_MASK0_RXFIFOHALFFULL (((uint32_t) 1 ) << 15) +/** Mask TxFifoFull flag.*/ +#define SDC_MASK0_TXFIFOFULL (((uint32_t) 1 ) << 16) +/** Mask RxFifoFull flag.*/ +#define SDC_MASK0_RXFIFOFULL (((uint32_t) 1 ) << 17) +/** Mask TxFifoEmpty flag.*/ +#define SDC_MASK0_TXFIFOEMPTY (((uint32_t) 1 ) << 18) +/** Mask RxFifoEmpty flag.*/ +#define SDC_MASK0_RXFIFOEMPTY (((uint32_t) 1 ) << 19) +/** Mask TxDataAvlbl flag.*/ +#define SDC_MASK0_TXDATAAVLBL (((uint32_t) 1 ) << 20) +/** Mask RxDataAvlbl flag.*/ +#define SDC_MASK0_RXDATAAVLBL (((uint32_t) 1 ) << 21) +/** CMD error interrupt mask */ +#define SDC_MASK0_CMDERR (SDC_MASK0_CMDCRCFAIL | SDC_MASK0_CMDTIMEOUT | SDC_MASK0_STARTBITERR) +/** Data Transmit Error interrupt mask */ +#define SDC_MASK0_TXDATAERR (SDC_MASK0_DATACRCFAIL | SDC_MASK0_DATATIMEOUT | SDC_MASK0_TXUNDERRUN | SDC_MASK0_STARTBITERR) +/** Data Receive Error interrupt mask */ +#define SDC_MASK0_RXDATAERR (SDC_MASK0_DATACRCFAIL | SDC_MASK0_DATATIMEOUT | SDC_MASK0_RXOVERRUN | SDC_MASK0_STARTBITERR) +/** Data Transfer interrupt mask*/ +#define SDC_MASK0_DATA (SDC_MASK0_DATAEND | SDC_MASK0_DATABLOCKEND ) + +/** + * @brief SDC Clock Control Register bit definitions + */ +/** SDC Clock Control Register Bitmask */ +#define SDC_CLOCK_BITMASK ((uint32_t) 0xFFF) +/** SDC Clock Divider Bitmask */ +#define SDC_CLOCK_CLKDIV_BITMASK (((uint32_t) 0xFF ) << 0) +/** Set SDC Clock Divide value */ +#define SDC_CLOCK_CLKDIV(n) (((uint32_t) (n & 0x0FF)) << 0) + +/** + * @brief SDC Status Register bit definitions + */ +/** Command Response received (CRC check failed) */ +#define SDC_STATUS_CMDCRCFAIL (((uint32_t) 1 ) << 0) +/** Data block sent/received (CRC check failed). */ +#define SDC_STATUS_DATACRCFAIL (((uint32_t) 1 ) << 1) +/** Command response timeout.. */ +#define SDC_STATUS_CMDTIMEOUT (((uint32_t) 1 ) << 2) +/** Data timeout. */ +#define SDC_STATUS_DATATIMEOUT (((uint32_t) 1 ) << 3) +/** Transmit FIFO underrun error. */ +#define SDC_STATUS_TXUNDERRUN (((uint32_t) 1 ) << 4) +/** Receive FIFO overrun error. */ +#define SDC_STATUS_RXOVERRUN (((uint32_t) 1 ) << 5) +/** Command response received (CRC check passed). */ +#define SDC_STATUS_CMDRESPEND (((uint32_t) 1 ) << 6) +/** Command sent (no response required).*/ +#define SDC_STATUS_CMDSENT (((uint32_t) 1 ) << 7) +/** Data end (data counter is zero).*/ +#define SDC_STATUS_DATAEND (((uint32_t) 1 ) << 8) +/** Start bit not detected on all data signals in wide bus mode..*/ +#define SDC_STATUS_STARTBITERR (((uint32_t) 1 ) << 9) +/** Data block sent/received (CRC check passed).*/ +#define SDC_STATUS_DATABLOCKEND (((uint32_t) 1 ) << 10) +/** Command transfer in progress.*/ +#define SDC_STATUS_CMDACTIVE (((uint32_t) 1 ) << 11) +/** Data transmit in progress.*/ +#define SDC_STATUS_TXACTIVE (((uint32_t) 1 ) << 12) +/** Data receive in progress.*/ +#define SDC_STATUS_RXACTIVE (((uint32_t) 1 ) << 13) +/** Transmit FIFO half empty.*/ +#define SDC_STATUS_TXFIFOHALFEMPTY (((uint32_t) 1 ) << 14) +/** Receive FIFO half full.*/ +#define SDC_STATUS_RXFIFOHALFFULL (((uint32_t) 1 ) << 15) +/** Transmit FIFO full.*/ +#define SDC_STATUS_TXFIFOFULL (((uint32_t) 1 ) << 16) +/** Receive FIFO full.*/ +#define SDC_STATUS_RXFIFOFULL (((uint32_t) 1 ) << 17) +/** Transmit FIFO empty.*/ +#define SDC_STATUS_TXFIFOEMPTY (((uint32_t) 1 ) << 18) +/** Receive FIFO empty.*/ +#define SDC_STATUS_RXFIFOEMPTY (((uint32_t) 1 ) << 19) +/** Data available in transmit FIFO.*/ +#define SDC_STATUS_TXDATAAVLBL (((uint32_t) 1 ) << 20) +/** Data available in receive FIFO.*/ +#define SDC_STATUS_RXDATAAVLBL (((uint32_t) 1 ) << 21) +/** Command Error Status */ +#define SDC_STATUS_CMDERR (SDC_STATUS_CMDCRCFAIL | SDC_STATUS_CMDTIMEOUT | SDC_STATUS_STARTBITERR) +/** Data Error Status */ +#define SDC_STATUS_DATAERR (SDC_STATUS_DATACRCFAIL | SDC_STATUS_DATATIMEOUT | SDC_STATUS_TXUNDERRUN \ + | SDC_STATUS_RXOVERRUN | SDC_STATUS_STARTBITERR) +/** FIFO Status*/ +#define SDC_STATUS_FIFO (SDC_STATUS_TXFIFOHALFEMPTY | SDC_STATUS_RXFIFOHALFFULL \ + | SDC_STATUS_TXFIFOFULL | SDC_STATUS_RXFIFOFULL \ + | SDC_STATUS_TXFIFOEMPTY | SDC_STATUS_RXFIFOEMPTY \ + | SDC_STATUS_DATABLOCKEND) + +/** Data Transfer Status*/ +#define SDC_STATUS_DATA (SDC_STATUS_DATAEND ) + +/** + * @brief SDC Data Control Register bit definitions + */ +/** SDC Data Control Register Bitmask */ +#define SDC_DATACTRL_BITMASK ((uint32_t) 0xFF) +/** Enable Data Transfer */ +#define SDC_DATACTRL_ENABLE (((uint32_t) 1 ) << 0) +/** Mark that Data is transfer from card to controller */ +#define SDC_DATACTRL_DIR_FROMCARD (((uint32_t) 1 ) << 1) +/** Mark that Data is transfer from controller to card */ +#define SDC_DATACTRL_DIR_TOCARD ((uint32_t) 0) +/** Mark that the transfer mode is Stream Data Transfer */ +#define SDC_DATACTRL_XFER_MODE_STREAM (((uint32_t) 1 ) << 2) +/** Mark that the transfer mode is Block Data Transfer */ +#define SDC_DATACTRL_XFER_MODE_BLOCK ((uint32_t) 0) +/** Enable DMA */ +#define SDC_DATACTRL_DMA_ENABLE (((uint32_t) 1 ) << 3) +/** Set Data Block size */ +#define SDC_DATACTRL_BLOCKSIZE(n) (((uint32_t) (n & 0x0F) ) << 4) +/** Get Data Block size value */ +#define SDC_DATACTRL_BLOCKSIZE_VAL(n) (((uint32_t) 1) << n) + +/** + * @brief OCR Register definitions + */ +/** Support voltage range 2.7-3.6 */ +#define SDC_OCR_27_36 ((uint32_t) 0x00FF8000) +/** Card power up status bit */ +#define SDC_OCR_IDLE (((uint32_t) 1) << 31) +#define SDC_OCR_BUSY (((uint32_t) 0) << 31) + + +/* SD/MMC commands - this matrix shows the command, response types, and + supported card type for that command. + Command Number Resp SD MMC + ----------------------- ------ ----- --- --- + Reset (go idle) CMD0 NA x x + Send op condition CMD1 R3 x + All send CID CMD2 R2 x x + Send relative address CMD3 R1 x + Send relative address CMD3 R6 x + Program DSR CMD4 NA x + Select/deselect card CMD7 R1b x + Select/deselect card CMD7 R1 x + Send CSD CMD9 R2 x x + Send CID CMD10 R2 x x + Read data until stop CMD11 R1 x x + Stop transmission CMD12 R1/b x x + Send status CMD13 R1 x x + Go inactive state CMD15 NA x x + Set block length CMD16 R1 x x + Read single block CMD17 R1 x x + Read multiple blocks CMD18 R1 x x + Write data until stop CMD20 R1 x + Setblock count CMD23 R1 x + Write single block CMD24 R1 x x + Write multiple blocks CMD25 R1 x x + Program CID CMD26 R1 x + Program CSD CMD27 R1 x x + Set write protection CMD28 R1b x x + Clear write protection CMD29 R1b x x + Send write protection CMD30 R1 x x + Erase block start CMD32 R1 x + Erase block end CMD33 R1 x + Erase block start CMD35 R1 x + Erase block end CMD36 R1 x + Erase blocks CMD38 R1b x + Fast IO CMD39 R4 x + Go IRQ state CMD40 R5 x + Lock/unlock CMD42 R1b x + Application command CMD55 R1 x + General command CMD56 R1b x + + *** SD card application commands - these must be preceded with *** + *** MMC CMD55 application specific command first *** + Set bus width ACMD6 R1 x + Send SD status ACMD13 R1 x + Send number WR blocks ACMD22 R1 x + Set WR block erase cnt ACMD23 R1 x + Send op condition ACMD41 R3 x + Set clear card detect ACMD42 R1 x + Send CSR ACMD51 R1 x */ + +/** + * @brief Possible SDMMC card state types + */ +typedef enum { + SDMMC_IDLE_ST = 0, /*!< Idle state */ + SDMMC_READY_ST, /*!< Ready state */ + SDMMC_IDENT_ST, /*!< Identification State */ + SDMMC_STBY_ST, /*!< standby state */ + SDMMC_TRAN_ST, /*!< transfer state */ + SDMMC_DATA_ST, /*!< Sending-data State */ + SDMMC_RCV_ST, /*!< Receive-data State */ + SDMMC_PRG_ST, /*!< Programming State */ + SDMMC_DIS_ST /*!< Disconnect State */ +} SDMMC_STATE_T; + + +/** + * @brief SD/MMC commands, arguments and responses + * Standard SD/MMC commands (3.1) type argument response + */ +/* class 1 */ +#define MMC_GO_IDLE_STATE 0 /* bc */ +#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ +#define MMC_ALL_SEND_CID 2 /* bcr R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +#define MMC_SET_DSR 4 /* bc [31:16] RCA */ +#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ +#define MMC_SEND_EXT_CSD 8 /* bc R1 */ +#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ +#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ +#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ +#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ + +/* class 2 */ +#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ +#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ +#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ + +/* class 3 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + +/* class 4 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ +#define MMC_PROGRAM_CID 26 /* adtc R1 */ +#define MMC_PROGRAM_CSD 27 /* adtc R1 */ + +/* class 6 */ +#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ +#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */ +#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */ + +/* class 5 */ +#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ +#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ +#define MMC_ERASE 37 /* ac R1b */ +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ +#define SD_ERASE 38 /* ac R1b */ + +/* class 9 */ +#define MMC_FAST_IO 39 /* ac <Complex> R4 */ +#define MMC_GO_IRQ_STATE 40 /* bcr R5 */ + +/* class 7 */ +#define MMC_LOCK_UNLOCK 42 /* adtc R1b */ + +/* class 8 */ +#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ +#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1b */ + +/* SD commands type argument response */ +/* class 8 */ +/* This is basically the same command as for MMC with some quirks. */ +#define SD_SEND_RELATIVE_ADDR 3 /* ac R6 */ +#define SD_CMD8 8 /* bcr [31:0] OCR R3 */ + +/* Application commands */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R1 (R4) */ +#define SD_APP_SEND_SCR 51 /* adtc R1 */ + + +/** + * @brief MMC status in R1<br> + * Type<br> + * e : error bit<br> + * s : status bit<br> + * r : detected and set for the actual command response<br> + * x : detected and set during command execution. the host must poll + * the card by sending status command in order to read these bits. + * Clear condition<br> + * a : according to the card state<br> + * b : always related to the previous command. Reception of + * a valid command will clear it (with a delay of one command)<br> + * c : clear by read<br> + */ + +#define R1_OUT_OF_RANGE (1UL << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_APP_CMD (1 << 5) /* sr, c */ + + +/** + * @brief SD/MMC card OCR register bits + */ +#define OCR_ALL_READY (1UL << 31) /* Card Power up status bit */ +#define OCR_HC_CCS (1 << 30) /* High capacity card */ +#define OCR_VOLTAGE_RANGE_MSK (0x00FF8000) + +#define SD_SEND_IF_ARG 0x000001AA +#define SD_SEND_IF_ECHO_MSK 0x000000FF +#define SD_SEND_IF_RESP 0x000000AA + +/** + * @brief R3 response definitions + */ +#define CMDRESP_R3_OCR_VAL(n) (((uint32_t) n) & 0xFFFFFF) +#define CMDRESP_R3_S18A (((uint32_t) 1 ) << 24) +#define CMDRESP_R3_HC_CCS (((uint32_t) 1 ) << 30) +#define CMDRESP_R3_INIT_COMPLETE (((uint32_t) 1 ) << 31) + +/** + * @brief R6 response definitions + */ +#define CMDRESP_R6_RCA_VAL(n) (((uint32_t) (n >> 16)) & 0xFFFF) +#define CMDRESP_R6_CARD_STATUS(n) (((uint32_t) (n & 0x1FFF)) | \ + ((n & (1 << 13)) ? (1 << 19) : 0) | \ + ((n & (1 << 14)) ? (1 << 22) : 0) | \ + ((n & (1 << 15)) ? (1 << 23) : 0)) + +/** + * @brief R7 response definitions + */ +/** Echo-back of check-pattern */ +#define CMDRESP_R7_CHECK_PATTERN(n) (((uint32_t) n ) & 0xFF) +/** Voltage accepted */ +#define CMDRESP_R7_VOLTAGE_ACCEPTED (((uint32_t) 1 ) << 8) + +/** + * @brief CMD3 command definitions + */ +/** Card Address */ +#define CMD3_RCA(n) (((uint32_t) (n & 0xFFFF) ) << 16) + +/** + * @brief CMD7 command definitions + */ +/** Card Address */ +#define CMD7_RCA(n) (((uint32_t) (n & 0xFFFF) ) << 16) + +/** + * @brief CMD8 command definitions + */ +/** Check pattern */ +#define CMD8_CHECKPATTERN(n) (((uint32_t) (n & 0xFF) ) << 0) +/** Recommended pattern */ +#define CMD8_DEF_PATTERN (0xAA) +/** Voltage supplied.*/ +#define CMD8_VOLTAGESUPPLIED_27_36 (((uint32_t) 1 ) << 8) + +/** + * @brief CMD9 command definitions + */ +#define CMD9_RCA(n) (((uint32_t) (n & 0xFFFF) ) << 16) + +/** + * @brief CMD13 command definitions + */ +#define CMD13_RCA(n) (((uint32_t) (n & 0xFFFF) ) << 16) + +/** + * @brief APP_CMD command definitions + */ +#define CMD55_RCA(n) (((uint32_t) (n & 0xFFFF) ) << 16) + +/** + * @brief ACMD41 command definitions + */ +#define ACMD41_OCR(n) (((uint32_t) n) & 0xFFFFFF) +#define ACMD41_S18R (((uint32_t) 1 ) << 24) +#define ACMD41_XPC (((uint32_t) 1 ) << 28) +#define ACMD41_HCS (((uint32_t) 1 ) << 30) + +/** + * @brief ACMD6 command definitions + */ +#define ACMD6_BUS_WIDTH(n) ((uint32_t) n & 0x03) +#define ACMD6_BUS_WIDTH_1 (0) +#define ACMD6_BUS_WIDTH_4 (2) + +/** @brief Card type defines + */ +#define CARD_TYPE_SD (1 << 0) +#define CARD_TYPE_4BIT (1 << 1) +#define CARD_TYPE_8BIT (1 << 2) +#define CARD_TYPE_HC (OCR_HC_CCS)/*!< high capacity card > 2GB */ + +/** + * @brief SD/MMC sector size in bytes + */ +#define MMC_SECTOR_SIZE 512 + +/****************************************************************************** + * External global variables + *****************************************************************************/ + +/****************************************************************************** + * Local variables + *****************************************************************************/ + +static MCIFileSystem* pUglyForIRQ = NULL; + +/****************************************************************************** + * Local Functions + *****************************************************************************/ + +static void mymciirq() +{ + pUglyForIRQ->mci_MCIIRQHandler(); +} + +static void mydmairq() +{ + pUglyForIRQ->mci_DMAIRQHandler(); +} + +/****************************************************************************** + * Public Functions + *****************************************************************************/ + +MCIFileSystem::MCIFileSystem(const char* name, PinName cd) : + FATFileSystem(name) +{ + pUglyForIRQ = this; + + _Stat = STA_NOINIT; + memset(&_sdCardInfo, 0, sizeof(SDMMC_CARD_T)); + _eventReceived = false; + _eventSuccess = false; + + initMCI(); + + if (cd != NC) + { + _cardDetect = new DigitalIn(cd); + } +} + +MCIFileSystem::~MCIFileSystem() +{ + if (_cardDetect != NULL) + { + delete _cardDetect; + } +} +void MCIFileSystem::initMCI() +{ + // Pinsel for MCI + LPC_IOCON->P1_2 = 2; /* SD_CLK @ P1.2 */ + LPC_IOCON->P1_3 = 2; /* SD_CMD @ P1.3 */ + LPC_IOCON->P1_5 = 2 | (1<<7); /* SD_PWR @ P1.5 - digital mode */ + LPC_IOCON->P1_6 = 2 | (1<<7); /* SD_DAT[0] @ P1.6 - digital mode */ + LPC_IOCON->P1_7 = 2 | (1<<7); /* SD_DAT[1] @ P1.7 - digital mode */ + LPC_IOCON->P1_11 = 2; /* SD_DAT[2] @ P1.11 */ + LPC_IOCON->P1_12 = 2; /* SD_DAT[3] @ P1.12 */ + + LPC_SC->PCONP |= SYSCTL_CLOCK_SDC; + LPC_SC->RSTCON0 = (1<<28); + LPC_SC->RSTCON0 &= ~(1<<28); + + /* Initialize GPDMA controller */ + gpdma_init(); + + /* Initialize SDC peripheral */ + LPC_SC->PCONP |= SYSCTL_CLOCK_SDC; + + /* Disable SD_CLK */ + mci_ClockControl(SDC_CLOCK_ENABLE, false); + + /* Power-off */ + mci_PowerControl(PowerOff, 0); + mci_WriteDelay(); + + /* Disable all interrupts */ + LPC_MCI->MASK0 = 0; + + /*Setting for timeout problem */ + LPC_MCI->DATATMR = 0x1FFFFFFF; + + LPC_MCI->COMMAND = 0; + mci_WriteDelay(); + + LPC_MCI->DATACTRL = 0; + mci_WriteDelay(); + + /* clear all pending interrupts */ + LPC_MCI->CLEAR = SDC_CLEAR_ALL; + + /* Power-up SDC Peripheral */ + mci_PowerControl(PowerUp, 0); + + /* delays for the supply output is stable*/ + for (uint32_t i = 0; i < 0x80000; i++ ) {} + + mci_SetClock(SDC_IDENT_CLOCK_RATE); + mci_ClockControl(SDC_CLOCK_ENABLE, true); + + /* Power-on SDC Interface */ + mci_PowerControl(PowerOn, 0); + + NVIC_SetVector(MCI_IRQn, (uint32_t) mymciirq); + NVIC_EnableIRQ(MCI_IRQn); + + NVIC_SetVector(DMA_IRQn, (uint32_t) mydmairq); + NVIC_EnableIRQ(DMA_IRQn); +} + +int MCIFileSystem::disk_initialize() { + + debug_if(MCI_DBG, "mcifs:disk_initialize(), _Stat = %#x\n", _Stat); + + if (!cardInserted()) { + /* No card in the socket */ + _Stat = STA_NODISK | STA_NOINIT; + } + + if (_Stat != STA_NOINIT) { + return _Stat; /* card is already enumerated */ + } + + //rtc_init(); + + /* Initialize the Card Data Strucutre */ + memset(&_sdCardInfo, 0, sizeof(SDMMC_CARD_T)); + + /* Reset */ + _Stat = STA_NOINIT; + + /* Enumerate the card once detected. Note this function may block for a little while. */ + int ret = mci_Acquire(); + if (ret != 1) { + debug("Card Acquire failed... got %d, but expected 1\r\n", ret); + return 1;//Stat; + } + + _Stat &= ~STA_NOINIT; + return _Stat; +} + +int MCIFileSystem::disk_write(const uint8_t *buffer, uint64_t block_number) { + debug_if(MCI_DBG, "mcifs:disk_write(%#x, %llu), _Stat = %#x\n", (uint32_t)buffer, block_number, _Stat); + if (_Stat & STA_NOINIT) { + // not ready + return 1; + } + if (mci_WriteBlocks((void*)buffer, block_number, 1) == SDC_RET_OK) { + return 0; + } + + return 1; +} + +int MCIFileSystem::disk_read(uint8_t *buffer, uint64_t block_number) { + debug_if(MCI_DBG, "mcifs:disk_read(%#x, %llu), _Stat = %#x\n", (uint32_t)buffer, block_number, _Stat); + if (_Stat & STA_NOINIT) { + // not ready + return _Stat; + } + if (mci_ReadBlocks(buffer, block_number, 1) == SDC_RET_OK) { + return 0; + } + + return 1; +} + +int MCIFileSystem::disk_status() +{ + debug_if(MCI_DBG, "mcifs:disk_status(), _Stat = %#x\n", _Stat); + return _Stat; +} + +int MCIFileSystem::disk_sync() +{ + debug_if(MCI_DBG, "mcifs:disk_sync(), _Stat = %#x\n", _Stat); + uint32_t end = us_ticker_read() + 50*1000; // 50ms + while (us_ticker_read() < end) + { + if (mci_GetCardStatus() & R1_READY_FOR_DATA) + { + // card is ready + return 0; + } + } + // timeout while waiting for card to get ready + return 1; +} + +uint64_t MCIFileSystem::disk_sectors() +{ + debug_if(MCI_DBG, "mcifs:disk_sectors(), _Stat = %#x, returning %llu\n", _Stat, _sdCardInfo.blocknr); + return _sdCardInfo.blocknr; +} + +void MCIFileSystem::mci_MCIIRQHandler() +{ + int32_t Ret; + + Ret = mci_IRQHandler(NULL, 0, NULL, 0); + if(Ret < 0) { + _eventSuccess = false; + _eventReceived = true; + } +} + +void MCIFileSystem::mci_DMAIRQHandler() +{ + _eventSuccess = gpdma_interrupt(_eventDmaChannel); + _eventReceived = true; + NVIC_DisableIRQ(DMA_IRQn); +} + +/****************************************************************************** + * Private Functions + *****************************************************************************/ + +bool MCIFileSystem::cardInserted() const +{ + // If no card detect pin is given, then assume that a card is inserted. + // If a pin is specified then use that to determing the presence of a card. + return ((_cardDetect == NULL) || (_cardDetect->read() == 0)); +} + + + +int32_t MCIFileSystem::mci_Acquire() +{ + int32_t Ret; + + /* Initialize card info */ + _sdCardInfo.speed = SDC_TRAN_CLOCK_RATE; + _sdCardInfo.card_type = 0; + + /* During identification phase, the clock should be less than + 400Khz. Once we pass this phase, the normal clock can be set up + to 25Mhz on SD card and 20Mhz on MMC card. */ + mci_SetClock(SDC_IDENT_CLOCK_RATE); + + /* Clear Open Drain output control for SD */ + mci_PowerControl(PowerOn, 0); + + /* Card Reset */ + Ret = mci_ExecuteCmd(SD_GO_IDLE_STATE, 0, NULL); + if (Ret != 0) { + return Ret; + } + + wait(ACQUIRE_DELAY); + + /* Send interface operation condiftion */ + Ret = mci_SendIfCond(); + if (Ret == SDC_RET_BAD_PARAMETERS) { + return Ret; /* Non-compatible voltage range or check pattern is not correct */ + + } + /* Get Card Type */ + if (Ret == SDC_RET_OK) {/* Ver2.00 or later SD Memory Card*/ + bool CCS; + uint32_t OCR = SDC_OCR_27_36; + _sdCardInfo.card_type |= CARD_TYPE_SD; + Ret = mci_SendAppOpCond(0, true, &OCR, &CCS); + if (CCS) { /* High Capacity or Extended Capacity SD Memory Card */ + _sdCardInfo.card_type |= CARD_TYPE_HC; + } + } + else { /*Ver2.00 or later SD Memory Card(voltage mismatch) or Ver1.X SD Memory Card + or not SD Memory Card*/ + bool CCS; + uint32_t OCR = SDC_OCR_27_36; + Ret = mci_SendAppOpCond(0, false, &OCR, &CCS); + if (Ret == SDC_RET_OK) { + _sdCardInfo.card_type |= CARD_TYPE_SD; + } + else if (Ret == SDC_RET_BAD_PARAMETERS) { + return Ret; + } + else { /* MMC Card setup */ + uint32_t OCR; + /* Enter to Open Drain mode */ + mci_PowerControl(PowerOn, SDC_PWR_OPENDRAIN); + wait(ACQUIRE_DELAY); + Ret = mci_SendOpCond(&OCR); + if (Ret != SDC_RET_OK) { + return Ret; + } + + } + } + + /* Read CID */ + mci_GetCID(_sdCardInfo.cid); + + /* RCA send, for SD get RCA */ + if (_sdCardInfo.card_type & CARD_TYPE_SD) { + mci_GetAddr(&_sdCardInfo.rca); + } + else { + _sdCardInfo.rca = 1; + mci_SetAddr(_sdCardInfo.rca); + mci_PowerControl(PowerOn, 0); /* enter to push-pull mode */ + } + + /* Get CSD */ + mci_GetCSD(_sdCardInfo.rca, _sdCardInfo.csd); + + /* Compute card size, block size and no. of blocks based on CSD response recived. */ + if (_sdCardInfo.cid[0]) { + mci_ProcessCSD(); + + if (mci_SetTranState(_sdCardInfo.rca) != SDC_RET_OK) { + return 0; + } + + if (mci_GetCardState() != SDMMC_TRAN_ST) { + return 0; + } + + if (mci_SetCardParams() != 0) { + return 0; + } + } + + return (_sdCardInfo.cid[0]) ? 1 : 0; +} + +uint32_t MCIFileSystem::mci_GetCardStatus() const +{ + uint32_t Status; + mci_GetStatus(_sdCardInfo.rca, &Status); + return Status; +} + +MCIFileSystem::CardState MCIFileSystem::mci_GetCardState() const +{ + uint32_t Status; + volatile int32_t Ret; + + /* get current state of the card */ + Ret = mci_GetStatus(_sdCardInfo.rca, &Status); + + /* check card state in response */ + return (CardState) R1_CURRENT_STATE(Status); +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_StopTransmission(uint32_t rca) const +{ + uint32_t Status; + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + Ret = mci_GetStatus(rca, &Status); + if (Ret != SDC_RET_OK) { + return SDC_RET_ERR_STATE; + } + + if (R1_CURRENT_STATE(Status) == SDMMC_TRAN_ST) { + return SDC_RET_OK; + } + + if ((R1_CURRENT_STATE(Status) != SDMMC_DATA_ST) && + (R1_CURRENT_STATE(Status) != SDMMC_RCV_ST)) { + return SDC_RET_ERR_STATE; + } + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD12_STOP_TRANSMISSION, 0, &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + if (Ret != SDC_RET_OK) { + return Ret; + } + Ret = mci_GetStatus(rca, &Status); + if ((R1_CURRENT_STATE(Status) == SDMMC_TRAN_ST) || (R1_CURRENT_STATE(Status) == SDMMC_PRG_ST)) { + return SDC_RET_OK; + } + return SDC_RET_ERR_STATE; + } + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_ReadBlocks(void *buffer, int32_t startBlock, int32_t blockNum) +{ + ReturnCode Ret = SDC_RET_FAILED; + uint8_t dmaChannel; + int32_t ByteNum = blockNum * MMC_SECTOR_SIZE; + + do + { + /* if card is not acquired return immediately */ + if (( startBlock < 0) || ( (startBlock + blockNum) > _sdCardInfo.blocknr) ) { + Ret = SDC_RET_NOT_READY; + break; + } + + /* Put to tran state */ + Ret = mci_SetTranState(_sdCardInfo.rca); + if (Ret != SDC_RET_OK) { + break; + } + + LPC_MCI->MASK0 = SDC_MASK0_DATA | SDC_MASK0_RXDATAERR; + + /* DMA Setup */ + gpdma_getFreeChannel(&dmaChannel); + gpdma_transfer_from_mci(dmaChannel, (uint32_t)buffer, ByteNum); + mci_SetupEventWakeup(dmaChannel); + + /* set transfer information */ + mci_SetDataTransfer(blockNum, true, DATA_TIMER_VALUE_R); + + Ret = _readBlocks(_sdCardInfo.card_type, startBlock, blockNum); + if (Ret == SDC_RET_OK) { + /* Wait for transfer Finish */ + if (mci_WaitForEvent() != 0) { + Ret = SDC_RET_FAILED; + } + } else { + Ret = SDC_RET_FAILED; + } + + gpdma_stop(dmaChannel); + + if ((blockNum > 1) || (mci_GetCardState() == SDMMC_DATA_ST)) { + /* Send Stop transmission command */ + mci_StopTransmission(_sdCardInfo.rca); + } + + /* Wait for card to enter tran state */ + while (mci_GetCardState() != SDMMC_TRAN_ST) {} + + } while(false); + + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_WriteBlocks(void *buffer, int32_t startBlock, int32_t blockNum) +{ + ReturnCode Ret = SDC_RET_FAILED; + uint8_t dmaChannel; + + do + { + /* if card is not acquired return immediately */ + if (( startBlock < 0) || ( (startBlock + blockNum) > _sdCardInfo.blocknr) ) { + Ret = SDC_RET_NOT_READY; + break; + } + + /* Put to tran state */ + Ret = mci_SetTranState(_sdCardInfo.rca); + if (Ret != SDC_RET_OK) { + break; + } + + Ret = _writeBlocks(_sdCardInfo.card_type, startBlock, blockNum); + if (Ret != SDC_RET_OK) { + break; + } + + /*Wait for card enter to rcv state*/ + while (mci_GetCardState() != SDMMC_RCV_ST) {} + + LPC_MCI->MASK0 = SDC_MASK0_DATA | SDC_MASK0_TXDATAERR; + + /* DMA Setup */ + gpdma_getFreeChannel(&dmaChannel); + gpdma_transfer_to_mci(dmaChannel, (uint32_t)buffer, blockNum*MMC_SECTOR_SIZE); + mci_SetupEventWakeup(dmaChannel); + + /* set transfer information */ + mci_SetDataTransfer(blockNum, false, DATA_TIMER_VALUE_W); + + /* Wait for transfer done */ + if (mci_WaitForEvent() != 0) { + Ret = SDC_RET_FAILED; + } + gpdma_stop(dmaChannel); + + if ((blockNum > 1) || (mci_GetCardState() == SDMMC_RCV_ST)) { + /* Send Stop transmission command */ + mci_StopTransmission(_sdCardInfo.rca); + } + + /* Wait for card to enter tran state */ + while (mci_GetCardState() != SDMMC_TRAN_ST) {} + + } while (false); + + return Ret; +} + +void MCIFileSystem::mci_SetClock(uint32_t freq) const +{ + uint32_t PClk; + uint32_t ClkValue = 0; + + PClk = PeripheralClock; + + ClkValue = (PClk + 2 * freq - 1) / (2 * freq); + if (ClkValue > 0) { + ClkValue -= 1; + } + uint32_t temp; + temp = (LPC_MCI->CLOCK & (~SDC_CLOCK_CLKDIV_BITMASK)); + LPC_MCI->CLOCK = temp | (SDC_CLOCK_CLKDIV(ClkValue)); + mci_WriteDelay(); +} + +void MCIFileSystem::mci_ClockControl(ClockControl ctrlType, bool enable) const +{ + if (enable) { + LPC_MCI->CLOCK |= (1 << ctrlType); + } + else { + LPC_MCI->CLOCK &= (~(1 << ctrlType)); + } + mci_WriteDelay(); +} + +void MCIFileSystem::mci_PowerControl(power_ctrl_t powerMode, uint32_t flag) const +{ + LPC_MCI->POWER = (powerMode & 0x3) | flag; + mci_WriteDelay(); +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_ExecuteCmd(uint32_t Command, uint32_t Arg, response_t* pResp) const +{ + ReturnCode Ret = SDC_RET_FAILED; + + /* Send Command to card */ + Ret = mci_SendCmd(Command, Arg, CMD_TIMEOUT); + if (Ret != SDC_RET_OK) { + return Ret; + } + + /* Get response (if any) */ + if ((Command & SDC_COMMAND_RSP_BITMASK) != SDC_COMMAND_NO_RSP) { + + mci_GetResp(pResp); + + /* If the response is not R1, in the response field, the Expected Cmd data + won't be the same as the CMD data in SendCmd(). Below four cmds have + R2 or R3 response. We don't need to check if MCI_RESP_CMD is the same + as the Expected or not. */ + if ((SDC_COMMAND_INDEX(Command) != MMC_SEND_OP_COND) && + (SDC_COMMAND_INDEX(Command) != SD_APP_OP_COND) && + (SDC_COMMAND_INDEX(Command) != MMC_ALL_SEND_CID) && + (SDC_COMMAND_INDEX(Command) != MMC_SEND_CSD) && + (pResp->CmdIndex != SDC_COMMAND_INDEX(Command))) { + return SDC_RET_CMD_FAILED; + } + } + + return SDC_RET_OK; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SendIfCond() const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD8_SEND_IF_COND, (CMD8_VOLTAGESUPPLIED_27_36 | CMD8_CHECKPATTERN( + CMD8_DEF_PATTERN)), &Response); + if (Ret == SDC_RET_OK) { + if ((Response.Data[0] & CMDRESP_R7_VOLTAGE_ACCEPTED) && + (CMDRESP_R7_CHECK_PATTERN(Response.Data[0]) == CMD8_DEF_PATTERN)) { + return SDC_RET_OK; + } + return SDC_RET_BAD_PARAMETERS; + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SendOpCond(uint32_t *pOCR) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 0x200; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD1_SEND_OP_COND, SDC_OCR_27_36, &Response); + if (Ret == SDC_RET_OK) { + *pOCR = Response.Data[0]; + if (*pOCR & SDC_OCR_IDLE) { + if ((Response.Data[0] & SDC_OCR_27_36) != SDC_OCR_27_36) { + return SDC_RET_BAD_PARAMETERS; + } + return SDC_RET_OK; + } + } + RetryCnt--; + } + return SDC_RET_FAILED; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SendAppOpCond(uint16_t rca, bool hcs, uint32_t *pOcr, bool *pCCS) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t Argument; + uint32_t RetryCnt = 0x2000; /* The host repeatedly issues ACMD41 for at least 1 second or + until the busy bit are set to 1 */ + + Argument = ACMD41_OCR(*pOcr); + if (hcs) { + Argument |= ACMD41_HCS; + } + + while (RetryCnt > 0) { + Ret = mci_SendAppCmd(rca); + if (Ret == SDC_RET_OK) { + Ret = mci_ExecuteCmd(SD_ACMD41_SD_SEND_OP_COND, Argument, &Response); + if (Ret == SDC_RET_OK) { + if (Response.Data[0] & CMDRESP_R3_INIT_COMPLETE) { + if (*pOcr == 0) { + *pOcr = CMDRESP_R3_OCR_VAL(Response.Data[0]); + return SDC_RET_OK; + } + if ((CMDRESP_R3_OCR_VAL(Response.Data[0]) & *pOcr) != *pOcr) { + return SDC_RET_BAD_PARAMETERS; + } + *pCCS = (Response.Data[0] & CMDRESP_R3_HC_CCS) ? true : false; + return SDC_RET_OK; + } + } + } + else { + //If we abort here then some cards will go undetected, better to keep retrying + //return Ret; + } + RetryCnt--; + } + return SDC_RET_FAILED; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_GetCID(uint32_t *pCID) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD2_ALL_SEND_CID, 0, &Response); + if (Ret == SDC_RET_OK) { + pCID[3] = Response.Data[0]; + pCID[2] = Response.Data[1]; + pCID[1] = Response.Data[2]; + pCID[0] = Response.Data[3]; + return SDC_RET_OK; + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SetAddr(uint16_t addr) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD3_SET_RELATIVE_ADDR, CMD3_RCA(addr), &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + return Ret; + } + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_GetAddr(uint16_t *pRCA) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + *pRCA = 0; + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD3_SEND_RELATIVE_ADDR, 0, &Response); + if (Ret == SDC_RET_OK) { + if (!(CMDRESP_R6_CARD_STATUS(Response.Data[0]) & R1_READY_FOR_DATA)) { + Ret = SDC_RET_NOT_READY; + } + else if (R1_CURRENT_STATE(CMDRESP_R6_CARD_STATUS(Response.Data[0])) != SDMMC_STBY_ST) { + Ret = SDC_RET_ERR_STATE; + } + else { + *pRCA = CMDRESP_R6_RCA_VAL(Response.Data[0]); + return SDC_RET_OK; + } + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_GetCSD(uint16_t rca, uint32_t *pCSD) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD9_SEND_CSD, CMD9_RCA(rca), &Response); + if (Ret == SDC_RET_OK) { + pCSD[3] = Response.Data[0]; + pCSD[2] = Response.Data[1]; + pCSD[1] = Response.Data[2]; + pCSD[0] = Response.Data[3]; + return Ret; + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SelectCard(uint16_t addr) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD7_SELECT_CARD, CMD7_RCA(addr), &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + return Ret; + } + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_GetStatus(uint16_t rca, uint32_t *pStatus) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + *pStatus = (uint32_t) -1; + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD13_SEND_STATUS, CMD13_RCA(rca), &Response); + if (Ret == SDC_RET_OK) { + mci_CheckR1Response(Response.Data[0], &Ret); + *pStatus = Response.Data[0]; + return Ret; + } + RetryCnt--; + } + return Ret; +} + +void MCIFileSystem::mci_ProcessCSD() +{ + int32_t CSize = 0; + int32_t CSizeMult = 0; + int32_t Mult = 0; + + /* compute block length based on CSD response */ + _sdCardInfo.block_len = 1 << mci_GetBits(80, 83, _sdCardInfo.csd); + + if ((_sdCardInfo.card_type & CARD_TYPE_HC) && (_sdCardInfo.card_type & CARD_TYPE_SD)) { + /* See section 5.3.3 CSD Register (CSD Version 2.0) of SD2.0 spec an explanation for the calculation of these values */ + CSize = mci_GetBits(48, 63, (uint32_t *) _sdCardInfo.csd) + 1; + _sdCardInfo.blocknr = CSize << 10; /* 512 byte blocks */ + } + else { + /* See section 5.3 of the 4.1 revision of the MMC specs for an explanation for the calculation of these values */ + CSize = mci_GetBits(62, 73, (uint32_t *) _sdCardInfo.csd); + CSizeMult = mci_GetBits(47, 49, (uint32_t *) _sdCardInfo.csd); + Mult = 1 << (CSizeMult + 2); + _sdCardInfo.blocknr = (CSize + 1) * Mult; + + /* adjust blocknr to 512/block */ + if (_sdCardInfo.block_len > MMC_SECTOR_SIZE) { + _sdCardInfo.blocknr = _sdCardInfo.blocknr * (_sdCardInfo.block_len >> 9); + } + } + + _sdCardInfo.device_size = _sdCardInfo.blocknr << 9; /* blocknr * 512 */ +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SetBusWidth(uint16_t rca, uint8_t width) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint8_t RetryCnt = 0x20; + + while (RetryCnt > 0) { + Ret = mci_SendAppCmd(rca); + if (Ret == SDC_RET_OK) { + Ret = mci_ExecuteCmd(SD_ACMD6_SET_BUS_WIDTH, ACMD6_BUS_WIDTH(width), &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + return Ret; + } + } + } + RetryCnt--; + } + return SDC_RET_FAILED; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SetTranState(uint16_t rca) const +{ + ReturnCode Ret = SDC_RET_OK; + uint32_t status = 0; + SDMMC_STATE_T state; + + /* get current state of the card */ + Ret = mci_GetStatus(rca, &status); + if (Ret != SDC_RET_OK) { + /* unable to get the card state. So return immediatly. */ + return Ret; + } + + /* check card state in response */ + state = (SDMMC_STATE_T) R1_CURRENT_STATE(status); + switch (state) { + case SDMMC_STBY_ST: + /* put card in 'Trans' state */ + Ret = mci_SelectCard(rca); + if (Ret != SDC_RET_OK) { + /* unable to put the card in Trans state. So return immediatly. */ + return Ret; + } + mci_GetStatus(rca, &status); + if (((SDMMC_STATE_T) R1_CURRENT_STATE(status)) != SDMMC_TRAN_ST) { + return SDC_RET_ERR_STATE; + } + break; + + case SDMMC_TRAN_ST: + /*do nothing */ + break; + + default: + /* card shouldn't be in other states so return */ + return SDC_RET_ERR_STATE; + } + + return SDC_RET_OK; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SetBlockLength(uint32_t rca, uint32_t block_len) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint8_t RetryCnt = 0x20; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD16_SET_BLOCKLEN, block_len, &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + return Ret; + } + } + RetryCnt--; + } + return SDC_RET_FAILED; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SetCardParams() const +{ + ReturnCode Ret; + + mci_SetClock(SDC_TRAN_CLOCK_RATE); + if (_sdCardInfo.card_type & CARD_TYPE_SD) { + mci_ClockControl(SDC_CLOCK_WIDEBUS_MODE, true); + Ret = mci_SetBusWidth(_sdCardInfo.rca, ACMD6_BUS_WIDTH_4); + if (Ret != SDC_RET_OK) { + return Ret; + } + } + else { + mci_ClockControl(SDC_CLOCK_WIDEBUS_MODE, false); + } + + /* set block length */ + Ret = mci_SetBlockLength(_sdCardInfo.rca, MMC_SECTOR_SIZE); + return Ret; +} + +bool MCIFileSystem::mci_CheckR1Response(uint32_t resp, ReturnCode* pCheckResult) const +{ + bool Ret = true; + + if (!(resp & R1_READY_FOR_DATA)) { + *pCheckResult = SDC_RET_NOT_READY; + Ret = false; + } + else if (R1_STATUS(resp)) { + *pCheckResult = SDC_RET_FAILED; + } + else { + *pCheckResult = SDC_RET_OK; + } + return Ret; +} + +void MCIFileSystem::mci_WriteDelay() const +{ +// volatile uint8_t i; +// for ( i = 0; i < 0x10; i++ ) { /* delay 3MCLK + 2PCLK */ +// } + wait(0.00001f); /* delay 10 us */ +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SendCmd(uint32_t Command, uint32_t Arg, uint32_t timeout) const +{ + ReturnCode ret = SDC_RET_TIMEOUT; + uint32_t Status; + + /* Set Command Info */ + mci_SetCommand(Command, Arg); + + while (timeout) { + + Status = LPC_MCI->STATUS; + + /* check if command was sent */ + if (((Command & SDC_COMMAND_RSP_BITMASK) == SDC_COMMAND_NO_RSP) && (Status & SDC_STATUS_CMDSENT)) { + ret = SDC_RET_OK; + break; + } + /* check if response was received */ + if (Status & SDC_STATUS_CMDRESPEND) { + ret = SDC_RET_OK; + break; + } + + /* check command sending status */ + if (Status & SDC_STATUS_CMDERR) { + if (Status & SDC_STATUS_CMDCRCFAIL) { + if ((SDC_COMMAND_INDEX(Command) == MMC_SEND_OP_COND) || + (SDC_COMMAND_INDEX(Command) == SD_APP_OP_COND) || + (SDC_COMMAND_INDEX(Command) == MMC_STOP_TRANSMISSION)) { + ret = SDC_RET_OK; /* ignore CRC error if it's a resp for SEND_OP_COND or STOP_TRANSMISSION. */ + break; + } + } + ret = SDC_RET_CMD_FAILED; + break; + } + + timeout--; + } + + mci_ResetCommand(); + + return ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::mci_SendAppCmd(uint16_t rca) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t RetryCnt = 20; + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(SD_CMD55_APP_CMD, CMD55_RCA(rca), &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + if (Ret != SDC_RET_OK) { + return Ret; + } + if (Response.Data[0] & R1_APP_CMD) { + return SDC_RET_OK; + } + else { + Ret = SDC_RET_FAILED; + } + } + } + RetryCnt--; + } + return SDC_RET_FAILED; +} + +void MCIFileSystem::mci_SetDataTransfer(uint16_t BlockNum, bool DirFromCard, uint32_t Timeout) const +{ + uint32_t DataCtrl = 0; + LPC_MCI->DATATMR = Timeout; + LPC_MCI->DATALEN = BlockNum * 512; + + DataCtrl = SDC_DATACTRL_ENABLE; + // DataCtrl mode=block, block size=512byte + DataCtrl |= (0x9 << 4); + if (DirFromCard) { + DataCtrl |= (0x1 << 1); + } + DataCtrl |= SDC_DATACTRL_DMA_ENABLE; + LPC_MCI->DATACTRL = DataCtrl; + mci_WriteDelay(); +} + +void MCIFileSystem::mci_GetResp(response_t* pResp) const +{ + pResp->CmdIndex = SDC_RESPCOMMAND_VAL(LPC_MCI->RESP_CMD); + pResp->Data[0] = LPC_MCI->RESP0; + if (CardStatusNumBytes == 4) { + pResp->Data[1] = LPC_MCI->RESP1; + pResp->Data[2] = LPC_MCI->RESP2; + pResp->Data[3] = LPC_MCI->RESP3; + } +} + +uint32_t MCIFileSystem::mci_GetBits(int32_t start, int32_t end, uint32_t *data) const +{ + uint32_t v; + uint32_t i = end >> 5; + uint32_t j = start & 0x1f; + + if (i == (start >> 5)) { + v = (data[i] >> j); + } + else { + v = ((data[i] << (32 - j)) | (data[start >> 5] >> j)); + } + + return v & ((1 << (end - start + 1)) - 1); +} + +void MCIFileSystem::mci_SetCommand(uint32_t Cmd, uint32_t Arg) const +{ + /* Clear status register */ + LPC_MCI->CLEAR = SDC_CLEAR_ALL; + + /* Set the argument first, finally command */ + LPC_MCI->ARGUMENT = Arg; + + /* Write command value, enable the command */ + LPC_MCI->COMMAND = Cmd | SDC_COMMAND_ENABLE; + + mci_WriteDelay(); +} + +void MCIFileSystem::mci_ResetCommand() const +{ + LPC_MCI->CLEAR = SDC_CLEAR_ALL; + + LPC_MCI->ARGUMENT = 0xFFFFFFFF; + + LPC_MCI->COMMAND = 0; + + mci_WriteDelay(); +} + +int32_t MCIFileSystem::mci_IRQHandler(uint8_t *txBuf, uint32_t *txCnt, uint8_t *rxBuf, uint32_t *rxCnt) +{ + uint32_t Status; + + Status = LPC_MCI->STATUS; + + if ( Status & SDC_STATUS_DATAERR) { + LPC_MCI->CLEAR = SDC_STATUS_DATAERR; + return -1; /* Data transfer error */ + } + + if ( Status & SDC_STATUS_DATAEND) { + LPC_MCI->CLEAR = SDC_STATUS_DATAEND; + LPC_MCI->MASK0 = 0; + return 0; + } + + if ( Status & SDC_STATUS_DATABLOCKEND) { + LPC_MCI->CLEAR = SDC_STATUS_DATABLOCKEND; + return 1; + } + + if (Status & SDC_STATUS_FIFO) { + return mci_FIFOIRQHandler(txBuf, txCnt, rxBuf, rxCnt); + } + + return 1; +} + +int32_t MCIFileSystem::mci_FIFOIRQHandler(uint8_t *txBuf, uint32_t *txCnt, uint8_t *rxBuf, uint32_t *rxCnt) +{ + uint32_t Status; + Status = LPC_MCI->STATUS; + + if (txBuf) { + if (Status & SDC_STATUS_TXFIFOHALFEMPTY) { + if (*txCnt % 64) { + mci_WriteFIFO((uint32_t *) &txBuf[*txCnt], false); + } + else { + mci_WriteFIFO((uint32_t *) &txBuf[*txCnt], true); + } + *txCnt += 32; + } + } + + if (rxBuf) { + if (Status & SDC_STATUS_RXFIFOHALFFULL) { + if (*rxCnt % 64) { + mci_ReadFIFO((uint32_t *) &rxBuf[*rxCnt], false); + } + else { + mci_ReadFIFO((uint32_t *) &rxBuf[*rxCnt], true); + } + *rxCnt += 32; + } + } + + LPC_MCI->CLEAR = SDC_STATUS_FIFO; + + return 1; +} + +void MCIFileSystem::mci_ReadFIFO(uint32_t *pDst, bool bFirstHalf) const +{ + uint8_t start = 0, end = 7; + + if (!bFirstHalf) { + start += 8; + end += 8; + } + for (; start <= end; start++) { + *pDst = LPC_MCI->FIFO[start]; + pDst++; + } +} + +void MCIFileSystem::mci_WriteFIFO(uint32_t *pSrc, bool bFirstHalf) const +{ + uint8_t start = 0, end = 7; + if (!bFirstHalf) { + start += 8; + end += 8; + } + for (; start <= end; start++) { + LPC_MCI->FIFO[start] = *pSrc; + pSrc++; + } +} + +void MCIFileSystem::mci_SetupEventWakeup(uint8_t dmaChannel) +{ + /* Wait for IRQ - for an RTOS, you would pend on an event here with a IRQ based wakeup. */ + NVIC_ClearPendingIRQ(DMA_IRQn); + + _eventDmaChannel = dmaChannel; + _eventReceived = false; + _eventSuccess = false; + + NVIC_EnableIRQ(DMA_IRQn); +} + +uint32_t MCIFileSystem::mci_WaitForEvent() const +{ + /* Wait for the event (DMA or MCI interrupt) for a maximum of 2 seconds */ + uint32_t end = us_ticker_read() + 2*1000*1000; + while ((us_ticker_read() < end) && (!_eventReceived)) + { + wait(0.01); + } + + if (_eventReceived && _eventSuccess) { + return 0; + } + + return 1; +} + +MCIFileSystem::ReturnCode MCIFileSystem::_readBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t Command, Argument; + uint8_t RetryCnt = 0x20; + + if (blockNum == 1) { + Command = SD_CMD17_READ_SINGLE_BLOCK; + } + else { + Command = SD_CMD18_READ_MULTIPLE_BLOCK; + } + + /* Select single or multiple read based on number of blocks */ + /* if high capacity card use block indexing */ + if (card_type & CARD_TYPE_HC) { + Argument = startBlock; + } + else { /*fix at 512 bytes*/ + Argument = startBlock << 9; + } + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(Command, Argument, &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + return Ret; + } + } + RetryCnt--; + } + return Ret; +} + +MCIFileSystem::ReturnCode MCIFileSystem::_writeBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const +{ + ReturnCode Ret = SDC_RET_FAILED; + response_t Response; + uint32_t Command, Argument; + uint8_t RetryCnt = 0x20; + + if (blockNum == 1) { + Command = SD_CMD24_WRITE_BLOCK; + } + else { + Command = SD_CMD25_WRITE_MULTIPLE_BLOCK; + } + + /* if high capacity card use block indexing */ + if (card_type & CARD_TYPE_HC) { + Argument = startBlock; + } + else { /*fix at 512 bytes*/ + Argument = startBlock << 9; + + } + + while (RetryCnt > 0) { + Ret = mci_ExecuteCmd(Command, Argument, &Response); + if (Ret == SDC_RET_OK) { + if (mci_CheckR1Response(Response.Data[0], &Ret)) { + return Ret; + } + } + RetryCnt--; + } + return Ret; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MCIFileSystem.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,185 @@ +#ifndef MCIFILESYSTEM_H +#define MCIFILESYSTEM_H + +#include "mbed.h" +#include "FATFileSystem.h" + +/** Access the filesystem on an SD Card using MCI + * + * @code + * #include "mbed.h" + * #include "MCIFileSystem.h" + * + * MCIFileSystem mcifs("mci"); + * + * int main() { + * printf("Please insert a SD/MMC card\n"); + * while(!mcifs.cardInserted()) { + * wait(0.5); + * } + * + * FILE *fp = fopen("/mci/myfile.txt", "w"); + * if (fp != NULL) { + * fprintf(fp, "Hello World!\n"); + * fclose(fp); + * } + * } + * @endcode + */ +class MCIFileSystem : public FATFileSystem { +public: + + /** Create the File System for accessing an SD/MMC Card using MCI + * + * @param name The name used to access the virtual filesystem + * @param cd The pin connected to the CardDetect line + */ + MCIFileSystem(const char* name, PinName cd = p38); + + virtual ~MCIFileSystem(); + + virtual int disk_initialize(); + virtual int disk_status(); + virtual int disk_read(uint8_t * buffer, uint64_t block_number); + virtual int disk_write(const uint8_t * buffer, uint64_t block_number); + virtual int disk_sync(); + virtual uint64_t disk_sectors(); + + void mci_MCIIRQHandler(); + void mci_DMAIRQHandler(); + + /** Tests if a SD/MMC card is inserted or not. + * + * @returns + * True if a card has been inserted, + * False if no card is inserted or if the card detect pin is unavailable + */ + bool cardInserted() const; + +private: + + typedef enum { + CardStatusNumBytes = 4, + } Constants; + + typedef enum { + PowerOff = 0, + PowerUp = 2, + PowerOn = 3, + } power_ctrl_t; + + typedef enum { + SDMMC_IDLE_ST = 0, /*!< Idle state */ + SDMMC_READY_ST, /*!< Ready state */ + SDMMC_IDENT_ST, /*!< Identification State */ + SDMMC_STBY_ST, /*!< standby state */ + SDMMC_TRAN_ST, /*!< transfer state */ + SDMMC_DATA_ST, /*!< Sending-data State */ + SDMMC_RCV_ST, /*!< Receive-data State */ + SDMMC_PRG_ST, /*!< Programming State */ + SDMMC_DIS_ST /*!< Disconnect State */ + } CardState; + + + typedef struct { + uint8_t CmdIndex; + uint32_t Data[CardStatusNumBytes]; + } response_t; + + /** + * @brief SDC Clock Control Options + */ + typedef enum { + SDC_CLOCK_ENABLE = 8, /*!< Enable SD Card Bus Clock */ + SDC_CLOCK_POWER_SAVE = 9, /*!< Disable SD_CLK output when bus is idle */ + SDC_CLOCK_DIVIDER_BYPASS = 10, /*!< Enable bypass of clock divide logic */ + SDC_CLOCK_WIDEBUS_MODE = 11, /*!< Enable wide bus mode (SD_DAT[3:0] is used instead of SD_DAT[0]) */ + } ClockControl; + + /** + * @brief SD/MMC Card specific setup data structure + */ + typedef struct { + uint32_t response[4]; /*!< Most recent response */ + uint32_t cid[4]; /*!< CID of acquired card */ + uint32_t csd[4]; /*!< CSD of acquired card */ + uint32_t ext_csd[512 / 4]; /*!< Ext CSD */ + uint32_t card_type; /*!< Card Type */ + uint16_t rca; /*!< Relative address assigned to card */ + uint32_t speed; /*!< Speed */ + uint32_t block_len; /*!< Card sector size */ + uint32_t device_size; /*!< Device Size */ + uint32_t blocknr; /*!< Block Number */ + uint32_t clk_rate; /*!< Clock rate */ + } SDMMC_CARD_T; + + typedef enum { + SDC_RET_OK = 0, + SDC_RET_CMD_FAILED = -1, + SDC_RET_BAD_PARAMETERS = -2, + SDC_RET_BUS_NOT_IDLE = -3, + SDC_RET_TIMEOUT = -4, + SDC_RET_ERR_STATE = -5, + SDC_RET_NOT_READY = -6, + SDC_RET_FAILED = -7, + } ReturnCode; + + void initMCI(); + + int32_t mci_Acquire(); + uint32_t mci_GetCardStatus() const; + CardState mci_GetCardState() const; + ReturnCode mci_ReadBlocks(void *buffer, int32_t startBlock, int32_t blockNum); + ReturnCode mci_WriteBlocks(void *buffer, int32_t startBlock, int32_t blockNum); + void mci_SetClock(uint32_t freq) const; + void mci_ClockControl(ClockControl ctrlType, bool enable) const; + void mci_PowerControl(power_ctrl_t powerMode, uint32_t flag) const; + ReturnCode mci_ExecuteCmd(uint32_t Command, uint32_t Arg, response_t* pResp) const; + ReturnCode mci_SendIfCond() const; + ReturnCode mci_SendOpCond(uint32_t *pOCR) const; + ReturnCode mci_SendAppOpCond(uint16_t rca, bool hcs, uint32_t *pOcr, bool *pCCS) const; + ReturnCode mci_GetCID(uint32_t *pCID) const; + ReturnCode mci_SetAddr(uint16_t addr) const; + ReturnCode mci_GetAddr(uint16_t *pRCA) const; + ReturnCode mci_GetCSD(uint16_t rca, uint32_t *pCSD) const; + ReturnCode mci_SelectCard(uint16_t addr) const; + ReturnCode mci_GetStatus(uint16_t rca, uint32_t *pStatus) const; + void mci_ProcessCSD(); + ReturnCode mci_SetBusWidth(uint16_t rca, uint8_t width) const; + ReturnCode mci_SetTranState(uint16_t rca) const; + ReturnCode mci_SetBlockLength(uint32_t rca, uint32_t block_len) const; + ReturnCode mci_SetCardParams() const; + ReturnCode mci_StopTransmission(uint32_t rca) const; + bool mci_CheckR1Response(uint32_t resp, ReturnCode* pCheckResult) const; + void mci_WriteDelay() const; + ReturnCode mci_SendCmd(uint32_t Command, uint32_t Arg, uint32_t timeout) const; + ReturnCode mci_SendAppCmd(uint16_t rca) const; + void mci_SetDataTransfer(uint16_t BlockNum, bool DirFromCard, uint32_t Timeout) const; + void mci_GetResp(response_t* pResp) const; + uint32_t mci_GetBits(int32_t start, int32_t end, uint32_t *data) const; + void mci_SetCommand(uint32_t Cmd, uint32_t Arg) const; + void mci_ResetCommand() const; + int32_t mci_IRQHandler(uint8_t *txBuf, uint32_t *txCnt, uint8_t *rxBuf, uint32_t *rxCnt); + int32_t mci_FIFOIRQHandler(uint8_t *txBuf, uint32_t *txCnt, uint8_t *rxBuf, uint32_t *rxCnt); + void mci_ReadFIFO(uint32_t *pDst, bool bFirstHalf) const; + void mci_WriteFIFO(uint32_t *pSrc, bool bFirstHalf) const; + + void mci_SetupEventWakeup(uint8_t dmaChannel); + uint32_t mci_WaitForEvent() const; + + ReturnCode _readBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const; + ReturnCode _writeBlocks(uint32_t card_type, uint32_t startBlock, uint32_t blockNum) const; + + + uint32_t _Stat; + SDMMC_CARD_T _sdCardInfo; + + DigitalIn* _cardDetect; + + uint8_t _eventDmaChannel; /*!< DMA Channel used for transfer data */ + volatile bool _eventReceived; + volatile bool _eventSuccess; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QSPIFileSystem.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,1499 @@ +#include "QSPIFileSystem.h" +#include "mbed_debug.h" + +#include "spifi_rom_api.h" + +/****************************************************************************** + * Defines and typedefs + *****************************************************************************/ + +#define QSPI_DBG 0 + +/* + * The SPIFI_ROM_PTR (0x1FFF1FF8) points to an area where the pointers to + * different drivers in ROM are stored. + */ +typedef struct { + /*const*/ unsigned p_usbd; // USBROMD + /*const*/ unsigned p_clib; + /*const*/ unsigned p_cand; + /*const*/ unsigned p_pwrd; // PWRROMD + /*const*/ unsigned p_promd; // DIVROMD + /*const*/ SPIFI_RTNS *pSPIFID; // SPIFIROMD + /*const*/ unsigned p_dev3; + /*const*/ unsigned p_dev4; +} ROM; + +#define ROM_DRIVERS_PTR ((ROM *)(*((unsigned int *)SPIFI_ROM_PTR))) +#define IS_ADDR_IN_SPIFI(__addr) ( (((uint32_t)(__addr)) & 0xff000000) == SPIFI_MEM_BASE ) + +#define MEM_SIZE (memInfo.memSize) //(8*1024*1024) +#define ERASE_SIZE (memInfo.eraseBlockSize) //(64*1024) +#define NUM_BLOCKS (memInfo.numEraseBlocks) //(MEM_SIZE/ERASE_SIZE) + +typedef uint32_t toc_entry_t; + +#define TOC_BLOCK_ADDR (memInfo.tocBlockAddr) //(SPIFI_MEM_BASE + (NUM_BLOCKS - 1)*ERASE_SIZE) +#define TOC_SIZE (memInfo.tocSizeInBytes) //(sizeof(toc_entry_t) * NUM_BLOCKS) +#define NUM_TOCS (memInfo.numTocs) //((int)(ERASE_SIZE/TOC_SIZE)) +#define NUM_TOC_BLOCKS ((NUM_TOCS * TOC_SIZE) / ERASE_SIZE) +#define NUM_TOC_ENTRIES ((int)(TOC_SIZE/sizeof(toc_entry_t))) + +#define TOC_UNUSED (0xffffffff) +#define TOC_MAX (NUM_BLOCKS - 1) +#define TOC_VALID_MASK (1UL<<31) +#define TOC_RESERVED_MASK (1UL<<30) +#define TOC_USED_MASK (1UL<<29) +#define TOC_FILE_MASK (1UL<<28) +#define TOC_FSIZE_MASK (0x3ffff) +#define TOC_MANDAT_SET_MASK (0x0ffc0000) + +#define MANDATORY_BITS_SET(__v) (((__v)&TOC_MANDAT_SET_MASK) == TOC_MANDAT_SET_MASK) + +#define VALID_TOC_ENTRY(__v) (((__v)&TOC_VALID_MASK) == 0) +#define USED_TOC_ENTRY(__v) (VALID_TOC_ENTRY(__v) && (((__v)&TOC_USED_MASK) == 0)) +#define TOC_IS_FILE(__v) (USED_TOC_ENTRY(__v) && (((__v)&TOC_FILE_MASK) == 0)) +#define TOC_IS_RESERVED(__v) (VALID_TOC_ENTRY(__v) && (((__v)&TOC_RESERVED_MASK) == 0)) +#define FILESIZE(__v) ((__v) & 0x3ffff) + +#define FS_MIN(__a, __b) (((__a) < (__b)) ? (__a) : (__b)) + +// Mask to compare the different access modes. In LPCXpresso this was defined +// but not in uVision +#ifndef O_ACCMODE +#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) +#endif + + +/* + * The file header currently only consists of the filename (including path) + * and the string terminating character, but by separating the file name + * length from the size of the header in the code it allows future additions + * to the header without too much code modification. + */ +#define HEADER_DNAME_MAXLEN (250) +#define HEADER_FNAME_STRLEN (HEADER_DNAME_MAXLEN + 5) +#define HEADER_FNAME_LEN (HEADER_FNAME_STRLEN + 1) +#define HEADER_LEN (HEADER_FNAME_LEN) // only filename in header for now + +typedef enum +{ + FS_OK, + FS_ERR_NOT_FORMATTED, + FS_ERR_NO_FILE, + FS_ERR_FILE_EXIST, + FS_ERR_INVALID_PARAM, + FS_ERR_DISK_FULL, + FS_ERR_SPIFI, + FS_ERR_MALLOC, + + // FS_ERR_SPIFI_* return codes are listed in the User's Manual + // as possible return values from spifi_init(), spifi_program() + // and spifi_erase() calls. + FS_ERR_SPIFI_INTERNAL_ERROR = 0x20002, // 0x20002, Internal error in API code + FS_ERR_SPIFI_TIMEOUT = 0x20003, // 0x20003, Time-out waiting for program or erase to begin: protection could not be removed. + FS_ERR_SPIFI_OPERAND = 0x20004, // 0x20004, Operand error (i.e. invalid params) + FS_ERR_SPIFI_STATUS = 0x20005, // 0x20005, Device status error + FS_ERR_SPIFI_EXT_DEVICE_ID = 0x20006, // 0x20006, Unknown extended device ID value + FS_ERR_SPIFI_DEVICE_ID = 0x20007, // 0x20007, Unknown device ID code + FS_ERR_SPIFI_DEVICE_TYPE = 0x20008, // 0x20008, Unknown device type code + FS_ERR_SPIFI_MANUFACTURER = 0x20009, // 0x20009, Unknown manufacturer code + FS_ERR_SPIFI_INVALID_JDEC_ID = 0x2000A, // 0x2000A, No operative serial flash (JEDEC ID all zeroes or all ones) + FS_ERR_SPIFI_ERASE_CONFLICT = 0x2000B, // 0x2000B, S_CALLER_ERASE is included in options, and erasure is required. + FS_ERR_SPIFI_VERIFICATION, // other, Other non-zero values can occur if options selects verification. + // They will be the address in the SPIFI memory area at which the first discrepancy was found. +} fresult; + +// The number of times to re-attempt a spifi_program() or spifi_erase() +// if the last one reported a verification error. +#define NUM_VERIFICATION_ATTEMPTS (1) + +typedef struct +{ + uint32_t memSize; + uint32_t eraseBlockSize; + uint32_t numEraseBlocks; + uint32_t tocBlockAddr; + uint32_t numTocs; + uint32_t tocSizeInBytes; + char memName[30]; +} meminfo_t; + +typedef struct +{ + int tocIdx; + uint32_t size; + uint16_t lastBlock; +} fileHandle_t; + +/****************************************************************************** + * Local variables + *****************************************************************************/ + +static toc_entry_t* TOC = NULL;//[NUM_BLOCKS]; +static int activeTOC = -1; + +static const SPIFI_RTNS *spifi = NULL; +static SPIFIobj obj; +static SPIFIopers opers; + +static char addr_conflict_buff[PROG_SIZE]; + +static meminfo_t memInfo = {0,0,0,0,0,0,{0}}; + +/****************************************************************************** + * Forward Declarations of Local Functions + *****************************************************************************/ +static fresult qspifs_init(); +static fresult qspifs_translateSpifiError(int rc); +static fresult qspifs_readTOC(void); +static fresult qspifs_saveTOC(void); +static fresult qspifs_findFile(const char* filename, fileHandle_t* fh); +static fresult qspifs_fileSize(int tocIdx, uint32_t* pSize); +static fresult qspifs_eraseBlock(int block); +static fresult qspifs_allocateFile(const char* filename, int neededBlocks, int* pTocIdx); +static void qspifs_deleteFile(fileHandle_t* fh); +static fresult qspifs_allocateSpace(fileHandle_t* fh, uint32_t size); +static fresult qspifs_format(unsigned int minReservedBytes); +static fresult qspifs_write(fileHandle_t* fh, const uint8_t * const pData, uint32_t size); +static bool qspifs_startsWith(const char* prefix, const char* str); + +/****************************************************************************** + * Local Functions + *****************************************************************************/ + +/****************************************************************************** + * + * Description: + * Initializes spifi, identifies the chip and reads the file system's + * table of content. + * + * Params: + * None + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_init() +{ + if (spifi == NULL) { + + // Turn on SPIFI block as it is disabled on reset + LPC_SC->PCONP |= 0x00010000; + + // pinsel for SPIFI + LPC_IOCON->P2_7 = 5; /* SPIFI_CSN @ P2.7 */ + LPC_IOCON->P0_22 = 5; /* SPIFI_CLK @ P0.22 */ + LPC_IOCON->P0_15 = 5; /* SPIFI_IO2 @ P0.15 */ + LPC_IOCON->P0_16 = 5; /* SPIFI_IO3 @ P0.16 */ + LPC_IOCON->P0_17 = 5; /* SPIFI_IO1 @ P0.17 */ + LPC_IOCON->P0_18 = 5; /* SPIFI_IO0 @ P0.18 */ + + uint32_t spifi_clk_div = (*((volatile uint32_t*)0x400FC1B4)) & 0x1f; + uint32_t spifi_clk_mhz = (SystemCoreClock / spifi_clk_div) / 1000000; + + spifi = ROM_DRIVERS_PTR->pSPIFID; + + /* Typical time tCS is 20 ns min, we give 200 ns to be on safer side */ + int rc = spifi->spifi_init (&obj, spifi_clk_mhz/5, S_FULLCLK+S_RCVCLK, spifi_clk_mhz); + if (rc) { + spifi = NULL; + return qspifs_translateSpifiError(rc); + } + + /* Make sure it is a tested flash module */ + if ((obj.mfger == 1) && (obj.devType == 0x2) && (obj.devID == 0x15) && (obj.memSize > 0x100000)) + { + /* For the Spansion memory the TOC occupies 256bytes and the TOC block will + hold 256 TOCs. */ + strcpy(memInfo.memName, "Spansion S25FL032"); + memInfo.memSize = obj.memSize; + memInfo.eraseBlockSize = 64*1024; + memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; + memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; + memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; + memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); + } + else if ((obj.mfger == 0xef) && (obj.devType == 0x40) && (obj.devID == 0x17) && (obj.memSize > 0x100000)) + { + /* For the Winbond memory the TOC occupies 8192 bytes and that is bigger than + one erase block (which is 4096 bytes). It is possible to either keep only + one TOC or to create a couple to reduce wear on the memory. In this case + the multiple TOCs option is used. */ + strcpy(memInfo.memName, "Winbond W25Q64FV"); + memInfo.memSize = obj.memSize; + memInfo.eraseBlockSize = 4*1024; + memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; + memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; + memInfo.numTocs = 8; + memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); + } + else + { + debug("INIT: Memory is unknown and may not work as expected\n"); + + // Asuming it has 64Kb erase blocks (i.e. same setup as the Spansion S25FL032 + strcpy(memInfo.memName, "Unknown - check ID"); + memInfo.memSize = obj.memSize; + memInfo.eraseBlockSize = 64*1024; + memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; + memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; + memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; + memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); + + /* + * If this happens, check the manufacturer and device information + * and compare with the data sheet for your chip. Also make sure + * that the sector sizes are the same (i.e. 64KB) for your chip. + * If everything is the same then add an exception for your chip. + */ + } + debug_if(QSPI_DBG, "INIT: Found %dMB %s\n", memInfo.memSize/0x100000, memInfo.memName); + + if (TOC != NULL) { + delete TOC; + } + TOC = (toc_entry_t*)malloc(TOC_SIZE); + if (TOC == NULL) { + debug_if(QSPI_DBG, "INIT: Failed to allocate memory for TOC\n"); + spifi = NULL; + return FS_ERR_MALLOC; + } + } + if (activeTOC == -1) + { + return qspifs_readTOC(); + } + return FS_OK; +} + +/****************************************************************************** + * + * Description: + * Converts the return value from one of the spifi_init(), spifi_program() + * or spifi_erase() calls into a FS_* error code to simplify it for the + * fs_qspi API user. + * This function also attempts to detect the verification failure error. + * When a verification error occurs the spifi_* functions returns the + * conflicting address and not an error code. As this can be any address + * it is difficult to test but this function converts it into the + * FS_ERR_SPIFI_VERIFICATION error code which can be tested against. + * + * Params: + * [in] rc - The return code from any of the spifi_* functions + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_translateSpifiError(int rc) +{ + fresult res; + if (rc == 0) + { + res = FS_OK; + } + else if ((rc >= FS_ERR_SPIFI_INTERNAL_ERROR) && (rc <= FS_ERR_SPIFI_ERASE_CONFLICT)) + { + // This is a known error code + res = (fresult)rc; + } + else if (opers.options & (S_VERIFY_PROG | S_VERIFY_ERASE)) + { + // As verification was selected and rc is not in the list of known + // codes this falls into this category in the User's Manual: + // + // "Other non-zero values can occur if options selects verification. + // They will be the address in the SPIFI memory area at which the + // first discrepancy was found." + res = FS_ERR_SPIFI_VERIFICATION; + } + else + { + // Should never happen :-) as all listed error codes are covered but + // to be on the safe side and not interpret this as a success, a generic + // error is set. + res = FS_ERR_SPIFI; + } + return res; +} + +/****************************************************************************** + * + * Description: + * Reads the table of contents (TOC). The TOC is stored in the last erase + * block on the QSPI flash. As the QSPI flash is not exactly RW (might + * require erasing before writing) the TOC is relocated inside the erase + * block everytime it is saved (see saveTOC()). The currently valid TOC + * is allways the last one stored. + * + * Params: + * None + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_readTOC(void) +{ + int i, j; + toc_entry_t* p; + uint8_t invalid = 0; + int lastValid = -1; + + // Search for the first unused TOC, keeping track of the valid + // ones as we go. + for (i = 0; (i < NUM_TOCS) && !invalid; i++) + { + p = (toc_entry_t*)(TOC_BLOCK_ADDR + i*TOC_SIZE); + for (j = 0; j < NUM_BLOCKS; j++) + { + if (!VALID_TOC_ENTRY(*p) || !MANDATORY_BITS_SET(*p)) + { + // invalid TOC entry, stop looking + invalid = 1; + break; + } + p++; + } + + if (!invalid) + { + // this TOC was ok, but perhaps there is a newer one? + lastValid = i; + } + } + + if (lastValid == -1) + { + // no valid TOCs on the flash + return FS_ERR_NOT_FORMATTED; + } + else + { + // previous entry was ok so use that + activeTOC = lastValid; + p = (toc_entry_t*)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE); + memcpy(TOC, p, TOC_SIZE); + return FS_OK; + } +} + +/****************************************************************************** + * + * Description: + * Saves the table of contents (TOC). The TOC is stored in the last erase + * block on the QSPI flash. As the QSPI flash is not exactly RW (might + * require erasing before writing) the TOC is first compared with what is + * stored in the QSPI flash and if there are no changes or all changes + * only require bit changes 1->0 then the current TOC can be overwritten. + * If bit value changes 0->1 are required then the current stored TOC + * cannot be overwritten and the new TOC is instead stored in the next + * available space. If the entire last block is filled then it is erased + * and the new TOC is placed at the start of it. + * + * Params: + * None + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_saveTOC(void) +{ + int i, rc = 0; + uint32_t* pSrc; + uint32_t* pDest; + uint32_t tmp; + uint8_t identical = 1; + + // active TOC same as the one we want to save? + pSrc = (uint32_t*)TOC; + pDest = (uint32_t*)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE); + for (i = 0; i < NUM_TOC_ENTRIES; i++) + { + if (*pSrc != *pDest) + { + identical = 0; + tmp = ((*pDest) ^ (*pSrc)) & (*pSrc); + if (tmp > 0) + { + // found a change that contains 0->1 bit modification which + // requires erasing or a new location + activeTOC = (activeTOC + 1)%NUM_TOCS; + if (activeTOC == 0) + { + // no more free TOCs so an erase is needed +#if 0 + opers.options &= ~S_CALLER_ERASE; + opers.options |= S_FORCE_ERASE; +#else + opers.dest = (char *) TOC_BLOCK_ADDR; + opers.length = TOC_SIZE * NUM_TOCS; + opers.scratch = NULL; + opers.protect = 0; + opers.options = S_NO_VERIFY; + rc = spifi->spifi_erase(&obj, &opers); + if (rc) { + return qspifs_translateSpifiError(rc); + } +#endif + } + break; + } + } + pSrc++; + pDest++; + } + + if (!identical) + { + opers.length = FS_MIN(TOC_SIZE, PROG_SIZE); + opers.scratch = NULL; + opers.protect = 0; + opers.options = S_VERIFY_PROG | S_CALLER_ERASE; + for (int i = 0; i < (TOC_SIZE / PROG_SIZE); i++) + { + opers.dest = (char *)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE + i*PROG_SIZE); + rc = spifi->spifi_program(&obj, ((char*)TOC)+i*PROG_SIZE, &opers); + if (rc) + { + break; + } + } + return qspifs_translateSpifiError(rc); + } + return FS_OK; +} + +/****************************************************************************** + * + * Description: + * Searches the file system for a file with the specified name and + * (if found) returns the file's position in the TOC. + * + * Note that the content of fh is only valid if FS_OK is returned. + * + * Params: + * [in] filename - The name of the file to find + * [out] fh - The handle with the file information + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_findFile(const char* filename, fileHandle_t* fh) +{ + int i; + + if (activeTOC == -1) + { + return FS_ERR_NOT_FORMATTED; + } + + // Look at all blocks except for the reserved ones + for (i = 0; i < NUM_BLOCKS; i++) + { + if (TOC_IS_FILE(TOC[i]) && !TOC_IS_RESERVED(TOC[i])) + { + // found a file, see if name matches + char* p = (char*)(SPIFI_MEM_BASE + i*ERASE_SIZE); + if (strncmp(filename, p, HEADER_FNAME_LEN) == 0) + { + // found a matching name + fh->tocIdx = i; + fresult res = qspifs_fileSize(fh->tocIdx, &fh->size); + if (res == FS_OK) { + fh->lastBlock = fh->tocIdx + ((fh->size + HEADER_LEN)/ ERASE_SIZE); + } + return FS_OK; + } + } + } + return FS_ERR_NO_FILE; +} + +/****************************************************************************** + * + * Description: + * Calculates and returns the file's size. + * + * Note that the content of pSize is only valid if FS_OK is returned. + * + * Params: + * [in] tocIdx - The file's position in the TOC + * [out] pSize - The file's size + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_fileSize(int tocIdx, uint32_t* pSize) +{ + int i; + + if (tocIdx < 0 || tocIdx > NUM_BLOCKS || !TOC_IS_FILE(TOC[tocIdx])) + { + return FS_ERR_NO_FILE; + } + + *pSize = 0; + + // A file is always stored in sequential blocks so start with the files + // first block and as long as it is full continue sum up the occupied + // block sizes. As soon as a non-full block is found that must be the + // file's last block. + for (i = tocIdx; i < NUM_BLOCKS; i++) + { + *pSize += FILESIZE(TOC[i]); + if (FILESIZE(TOC[i]) < ERASE_SIZE) + { + // last block in chain + break; + } + } + + // Remove the filename header from the file's size + *pSize -= HEADER_LEN; + + return FS_OK; +} + +/****************************************************************************** + * + * Description: + * Erases everything in one block on the QSPI flash. + * + * Params: + * [in] block - The block's number + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_eraseBlock(int block) +{ + opers.dest = (char *)(block * ERASE_SIZE); + opers.length = ERASE_SIZE; + opers.scratch = NULL; + opers.protect = 0; + opers.options = S_NO_VERIFY; + return qspifs_translateSpifiError(spifi->spifi_erase (&obj, &opers)); +} + +/****************************************************************************** + * + * Description: + * Creates a new file if there is enough space for it on the file system. + * The TOC is searched for a unused sequence of blocks of at least the + * needed size. That block is marked as used and the file's name is stored + * in the first bytes of the file's first block. + * + * Note: The filename will not be tested for uniqueness. + * Note: The value of pTocIdx will only be valid if FS_OK is returned. + * + * Params: + * [in] filename - The name of the new file + * [in] neededBlocks - The number of blocks (in sequence) to allocate + * [out] pTocIdx - The new file's position in the TOC + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_allocateFile(const char* filename, int neededBlocks, int* pTocIdx) +{ + int i, rc; + + if (activeTOC == -1) + { + return FS_ERR_NOT_FORMATTED; + } + + // Look at all blocks except for the reserved ones + for (i = 0; i < NUM_BLOCKS; i++) + { + //TODO: Improve search to use gaps to avoid having to move files + // that are written to + if (!USED_TOC_ENTRY(TOC[i]) && !TOC_IS_RESERVED(TOC[i])) + { + int j; + for (j = 1; j < neededBlocks; j++) + { + if (USED_TOC_ENTRY(TOC[i+j]) || TOC_IS_RESERVED(TOC[i+j])) + { + // not enough free blocks in sequence, skip past these + // tested entries and continue searching + i += j; + break; + } + } + + if (j == neededBlocks) + { + const char* pSrc = filename; + if (IS_ADDR_IN_SPIFI(filename)) + { + // The SPIFI ROM driver cannot write data from SPIFI into + // SPIFI (i.e. cannot read and write at the same time). + // The workaround is to copy the source data into a buffer + // in local memory and use that as source for the write + // instead. + memcpy(addr_conflict_buff, filename, strlen(filename)+1); + pSrc = addr_conflict_buff; + } + + // Erase the new file's first block and store the filename at the + // start of it + opers.length = strlen(pSrc)+1; + opers.scratch = NULL; + opers.protect = 0; + opers.options = S_VERIFY_PROG | S_FORCE_ERASE;// S_CALLER_ERASE; + opers.dest = (char *)(i*ERASE_SIZE); + rc = spifi->spifi_program(&obj, (char*)pSrc, &opers); + if (rc) { + return qspifs_translateSpifiError(rc); + } + + TOC[i] &= ~(TOC_VALID_MASK | TOC_USED_MASK | TOC_FILE_MASK | TOC_FSIZE_MASK); + TOC[i] |= HEADER_LEN; + + *pTocIdx = i; + return FS_OK; + } + } + } + return FS_ERR_DISK_FULL; +} + +/****************************************************************************** + * + * Description: + * Deletes the specified file by marking all its blocks as unused in + * the TOC. + * + * Note: The deleted blocks are not erased here - that is done when they + * are allocated the next time. + * + * Params: + * [in] fh - The file handle with information about what to delete + * + * Returns: + * None + * + *****************************************************************************/ +static void qspifs_deleteFile(fileHandle_t* fh) +{ + int i; + + for (i = fh->lastBlock; i >= fh->tocIdx; i--) + { + TOC[i] = ~TOC_VALID_MASK; + } +} + +/****************************************************************************** + * + * Description: + * Ensures that the specified file can grow to the wanted size. + * If the file size will increase enough to need one or more new blocks + * and there isn't enough space then an attempt is made to move the + * current file to a large enough space somewhere else. + * + * If there are more free block(s) at the end of the file then it is not + * moved and instead those blocks are marked as used. + * + * Note: The filename will not be tested for uniqueness. + * Note: The value of pTocIdx will only be valid if FS_OK is returned. + * + * Params: + * [in/out] fh - The current file handle, might be updated after a move + * [in] size - The wanted new size + * + * Returns: + * FS_OK or one of the FS_ERR_* error codes + * + *****************************************************************************/ +static fresult qspifs_allocateSpace(fileHandle_t* fh, uint32_t size) +{ + uint16_t oldNumBlocks = (fh->size + HEADER_LEN) / ERASE_SIZE; + uint16_t newNumBlocks = (fh->size + HEADER_LEN + size) / ERASE_SIZE; + uint16_t numNeeded = newNumBlocks - oldNumBlocks; + fresult res = FS_OK; + + if (numNeeded > 0) + { + uint16_t i; + for (i = 0; i < numNeeded; i++) + { + if (USED_TOC_ENTRY(TOC[fh->tocIdx + oldNumBlocks + 1 + i]) || + TOC_IS_RESERVED(TOC[fh->tocIdx + oldNumBlocks + 1 + i])) + { + fileHandle_t fhNew; + + // have to move the chain + char* filename = (char*)(SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE); + res = qspifs_allocateFile(filename, newNumBlocks, &(fhNew.tocIdx)); + if (res == FS_OK) + { + // copy data + fhNew.lastBlock = fhNew.tocIdx; + fhNew.size = 0; + res = qspifs_write(&fhNew, (uint8_t*)(SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE + HEADER_LEN), fh->size); + } + if (res == FS_OK) + { + // remove old entries + qspifs_deleteFile(fh); + + // modify old handle to point to new information + fh->lastBlock = fhNew.lastBlock; + fh->size = fhNew.size; + fh->tocIdx = fhNew.tocIdx; + } + if (res != FS_OK) + { + // not possible to relocate the file => abort + return res; + } + break; + } + } + + // have space that is unused, so mark as used + for (i = 0; i < numNeeded; i++) + { + int tocIdx = fh->tocIdx + oldNumBlocks + 1 + i; + TOC[tocIdx] &= ~TOC_USED_MASK; + qspifs_eraseBlock(tocIdx); + } + } + + return res; +} + +/****************************************************************************** + * + * Description: + * Adds a file system to the QSPI flash. The entire flash will be erase + * except for the minReservedBytes first bytes. That reserved area (rounded + * up to the closest even multiple of the erase block size) can be used + * for anything and will never be touched by the file system. That area is + * typically used for executing programs from when the internal flash is + * full. + * + * The file system will have a table of content (TOC) placed at the start + * of the last erase block on the flash. + * + * Params: + * [in] minReservedBytes - The number of bytes to ignore at the start of + * the flash. + * + * Returns: + * FS_OK on success or one of the FS_ERR_* on failure + * + *****************************************************************************/ +static fresult qspifs_format(unsigned int minReservedBytes) +{ + int i, rc; + int numReserved = 0; + + if (minReservedBytes > 0) { + numReserved = (minReservedBytes + ERASE_SIZE - 1) / ERASE_SIZE; + if (numReserved >= (NUM_BLOCKS - 2)) { + // Too many of the erase blocks are reserved - not even room for one file + return FS_ERR_INVALID_PARAM; + } + } + +#if 0 // works but is really slow + // Erase all non-reserved blocks + for (i = numReserved; i < NUM_BLOCKS; i++) { + opers.dest = (char *) (i * ERASE_SIZE); + opers.length = ERASE_SIZE; + opers.scratch = NULL; + opers.protect = 0; + opers.options = S_NO_VERIFY; + rc = spifi->spifi_erase(&obj, &opers); + if (rc) { + return qspifs_translateSpifiError(rc); + } + } +#else + // Erase all non-reserved blocks + opers.dest = (char *) (numReserved * ERASE_SIZE); + opers.length = MEM_SIZE - (numReserved * ERASE_SIZE); + opers.scratch = NULL; + opers.protect = 0; + opers.options = S_NO_VERIFY; + rc = spifi->spifi_erase(&obj, &opers); + if (rc) { + return qspifs_translateSpifiError(rc); + } +#endif + + // Create the TOC, mark requested blocks as reserved and mark the TOC's + // block(s) as reserved as well. + for (i = 0; i < numReserved; i++) { + TOC[i] = ~(TOC_VALID_MASK | TOC_RESERVED_MASK); + } + for (; i < (NUM_BLOCKS - NUM_TOC_BLOCKS); i++) { + TOC[i] = ~TOC_VALID_MASK; + } + for (; i < NUM_BLOCKS; i++) { + TOC[i] = ~(TOC_VALID_MASK | TOC_RESERVED_MASK); + } + + // Save the TOC in the last block + activeTOC = 0; + fresult res = qspifs_saveTOC(); + if (res != FS_OK) { + activeTOC = -1; + return res; + } +// opers.dest = (char *) TOC_BLOCK_ADDR; +// opers.length = TOC_SIZE; +// opers.scratch = NULL; +// opers.protect = 0; +// opers.options = S_VERIFY_PROG | S_CALLER_ERASE; +// rc = spifi->spifi_program(&obj, (char*) TOC, &opers); +// if (rc) { +// return qspifs_translateSpifiError(rc); +// } + + // Read back TOC to be sure it worked + return qspifs_readTOC(); +} + +/****************************************************************************** + * + * Description: + * Deletes all files on the file system. This is a "quick format" that + * leaves all blocks untouched and only modifies the TOC. Any reserved + * blocks are kept reserved. + * + * The purpose of this function is to make it easy to clear the file system + * without going through a time consuming complete erase every time. + * + * Params: + * None + * + * Returns: + * FS_OK on success or one of the FS_ERR_* on failure + * + *****************************************************************************/ +// static fresult qspifs_deleteAllFiles(void) +// { +// for (int i = 0; i < NUM_BLOCKS; i++) +// { +// if (!TOC_IS_RESERVED(TOC[i])) { +// TOC[i] = ~TOC_VALID_MASK; +// } +// } +// +// return qspifs_saveTOC(); +// } + +/****************************************************************************** + * + * Description: + * Appends the data to the end of the file. + * + * Params: + * [in] fh - The handle to the file as returned from fs_open_append() + * [in] pData - The data to save + * [in] size - Number of bytes to save + * + * Returns: + * FS_OK on success or one of the FS_ERR_* on failure + * + *****************************************************************************/ +static fresult qspifs_write(fileHandle_t* fh, const uint8_t * const pData, uint32_t size) +{ + uint32_t left = size; + const uint8_t* pSrc = pData; + int rc, i; + fresult res; + int failed_attempts = 0; + + do { + res = qspifs_allocateSpace(fh, size); + if (res != FS_OK) { + break; + } + + opers.dest = (char *) (SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE + + HEADER_LEN + fh->size); + opers.scratch = NULL; + opers.protect = 0; + opers.options = S_VERIFY_PROG; // | S_FORCE_ERASE; + + while ((res == FS_OK) && (left > 0)) { + if (left >= PROG_SIZE) { + opers.length = PROG_SIZE; + } else { + opers.length = left; + } + if (IS_ADDR_IN_SPIFI(pData)) { + memcpy(addr_conflict_buff, pSrc, opers.length); + rc = spifi->spifi_program(&obj, addr_conflict_buff, &opers); + } else { + rc = spifi->spifi_program(&obj, (char*) pSrc, &opers); + } + res = qspifs_translateSpifiError(rc); + if ((res == FS_ERR_SPIFI_VERIFICATION) + && (++failed_attempts <= NUM_VERIFICATION_ATTEMPTS)) { + // The verification process failed. + // In all the observed occasions re-running the exact same + // spifi_program command again yielded a 0 as a return value + // the second time. + // The quick'N'dirty fix is to re-run that program instruction + // NUM_VERIFICATION_ATTEMPTS more time(s) when this happens. + res = FS_OK; + continue; + } + if (res != FS_OK) { + // Got an error but cannot exit this function here as parts of the data + // (previous loops?) may have been written so the TOC must be updated. + break; + } + pSrc += opers.length; + opers.dest += opers.length; + left -= opers.length; + failed_attempts = 0; + } + + // update file information + fh->size = fh->size + size - left; + fh->lastBlock = fh->tocIdx + ((fh->size + HEADER_LEN)/ ERASE_SIZE); + left = fh->size + HEADER_LEN; + for (i = 0; i <= (fh->lastBlock - fh->tocIdx); i++) { + TOC[fh->tocIdx + i] &= ~TOC_FSIZE_MASK; + TOC[fh->tocIdx + i] |= FS_MIN(ERASE_SIZE, left); + left -= FILESIZE(TOC[fh->tocIdx + i]); + } + + if (res == FS_OK) { + res = qspifs_saveTOC(); + } else { + // Want to save the TOC but not overwrite the previous error with + // a possibly successful TOC saving thus making it seem like there + // was no error + qspifs_saveTOC(); + } + } while (0); + + return res; +} + +/****************************************************************************** + * + * Description: + * Tests if str starts with prefix. A prefix of NULL or an empty string + * results in a positive result regardless of the content of str. + * + * Params: + * [in] prefix - The prefix to look for + * [in] str - The string to search for prefix + * + * Returns: + * True if the specified string starts with prefix + * + *****************************************************************************/ +static bool qspifs_startsWith(const char* prefix, const char* str) +{ + const char* pA = prefix; + const char* pB = str; + + if (pA == NULL) + { + return true; + } + for (; *pA != '\0'; pA++, pB++) + { + if (*pB != *pA) + { + return false; + } + } + + return true; +} + +/****************************************************************************** + * Class Declarations + *****************************************************************************/ + +class QSPIFileHandle : public FileHandle { + +public: + QSPIFileHandle(fileHandle_t* handle, int flags); + + virtual int close(); + + virtual ssize_t write(const void *buffer, size_t length); + + virtual ssize_t read(void *buffer, size_t length); + + virtual int isatty(); + + virtual off_t lseek(off_t position, int whence); + + virtual int fsync(); + + virtual off_t flen(); + +protected: + + fileHandle_t fh; + bool allowReading; + bool allowWriting; + uint32_t pos; +}; + +class QSPIDirHandle : public DirHandle { + +public: + static QSPIDirHandle* openDir(const char* dirname); + + virtual ~QSPIDirHandle(); + + virtual int closedir(); + virtual struct dirent *readdir(); + virtual void rewinddir(); + +private: + QSPIDirHandle(const char* dirname); + + int findFileWithPrefix(const char* prefix, int startTOCIdx, int maxTOCIdx) const; + +protected: + + char* dirname; + int nextTocIdx; + + bool isRoot; + + struct dirent cur_entry; +}; + +/****************************************************************************** + * Class Implementations + *****************************************************************************/ + +QSPIFileHandle::QSPIFileHandle(fileHandle_t* handle, int flags) +{ + fh = *handle; + int accmode = (flags & O_ACCMODE); + allowReading = (accmode == O_RDONLY) || (accmode == O_RDWR); + allowWriting = (accmode == O_WRONLY) || (accmode == O_RDWR) || (flags & O_APPEND); + pos = 0; +} + +int QSPIFileHandle::close() +{ + delete this; + return 0; +} + +ssize_t QSPIFileHandle::write(const void *buffer, size_t length) +{ + if (!allowWriting) { + return -1; + } + fresult res = qspifs_write(&fh, (const uint8_t*)buffer, length); + if (res == FS_OK) { + // A write is always 'append' in this file system so the file + // position is always end of file after a write + pos = fh.size; + return length; + } + return -1; +} + +ssize_t QSPIFileHandle::read(void *buffer, size_t length) +{ + if (!allowReading) { + return -1; + } + if (pos >= fh.size) { + return 0; + } + uint32_t len = FS_MIN(length, fh.size - pos); + const char* pData = (const char*)(SPIFI_MEM_BASE + fh.tocIdx*ERASE_SIZE + HEADER_LEN + pos); + memcpy(buffer, pData, len); + pos += len; + return len; +} + +int QSPIFileHandle::isatty() +{ + return 0; +} + +off_t QSPIFileHandle::lseek(off_t position, int whence) +{ + switch (whence) { + case SEEK_SET: + pos = position; + break; + + case SEEK_CUR: + pos += position; + break; + + case SEEK_END: + pos = fh.size + position; + break; + + default: + return -1; + } + return pos; +} + +int QSPIFileHandle::fsync() +{ + return 0; // always synced +} + +off_t QSPIFileHandle::flen() +{ + return fh.size; +} + +QSPIDirHandle::QSPIDirHandle(const char* dirname) { + size_t len = strlen(dirname); + this->dirname = (char*)malloc(len + 2); // null termination and possible ending '/' + if (this->dirname != NULL) { + if (len == 0 || ((len == 1) && (dirname[0] == '/'))) { + isRoot = true; + this->dirname[0] = '\0'; + } else { + isRoot = false; + memcpy(this->dirname, dirname, len+1); + if (dirname[len - 1] != '/') { + this->dirname[len] = '/'; + this->dirname[len+1] = '\0'; + } + } + cur_entry.d_name[HEADER_FNAME_STRLEN] = '\0'; + rewinddir(); + } +} + +QSPIDirHandle::~QSPIDirHandle() +{ + if (dirname != NULL) { + delete dirname; + dirname = NULL; + } +} + +QSPIDirHandle* QSPIDirHandle::openDir(const char* dirname) +{ + QSPIDirHandle* d = new QSPIDirHandle(dirname); + if (d->dirname == NULL) { + // failed to allocate memory for the folder name + delete d; + d = NULL; + } else if (!d->isRoot) { + if (d->findFileWithPrefix(d->dirname, 0, NUM_BLOCKS) == NUM_BLOCKS) { + // There are no files in this directory, i.e. it does not exist + delete d; + d = NULL; + } + } + return d; +} + +int QSPIDirHandle::closedir() { + delete this; + return 0; +} + +int QSPIDirHandle::findFileWithPrefix(const char* prefix, int startTOCIdx, int maxTOCIdx) const +{ + for (int i = startTOCIdx; i < maxTOCIdx; i++) { + if (TOC_IS_FILE(TOC[i])) { + const char* filename = (const char*) (SPIFI_MEM_BASE + i * ERASE_SIZE); + if (qspifs_startsWith(prefix, filename)) { + return i; + } + } + } + return NUM_BLOCKS; // no match +} + + +struct dirent *QSPIDirHandle::readdir() { + if (nextTocIdx < NUM_BLOCKS) { + for (int i = nextTocIdx; i < NUM_BLOCKS; i++) { + int possible = findFileWithPrefix(dirname, i, NUM_BLOCKS); + if (possible < NUM_BLOCKS) { + const char* fullfilename = (const char*) (SPIFI_MEM_BASE + possible * ERASE_SIZE); + const char* filename = fullfilename + strlen(dirname); + + if (strchr(filename, '/') == NULL) { + // file is not in any sub folder so it is truly in the wanted dir + nextTocIdx = possible + 1; + strcpy(cur_entry.d_name, filename); + return &cur_entry; + } + + // this is a file in a subfolder and should not be reported, + // but the folder name itself should + strcpy(cur_entry.d_name, fullfilename); + char* pSlash = strchr(cur_entry.d_name + strlen(dirname), '/'); + pSlash++; + *pSlash = '\0'; + + // now that cur_entry.d_name contains the folder's complete + // path with a trailing '/', see if it has occurred earlier + int older = findFileWithPrefix(cur_entry.d_name, 0, i); + if (older < possible) { + // already reported, move past this entry + i = possible; + } else { + // found a new subfolder + nextTocIdx = possible + 1; + strcpy(cur_entry.d_name, filename); + char* pSlash = strchr(cur_entry.d_name, '/'); +// pSlash++; //with ++ the returned dir name is "mydir/" without ++ "mydir" is returned + *pSlash = '\0'; + return &cur_entry; + } + } + } + } + return NULL; +} + +void QSPIDirHandle::rewinddir() { + nextTocIdx = 0; +} + + +QSPIFileSystem::QSPIFileSystem(const char* name) : + FileSystemLike(name) { + + // Turn on SPIFI block as it is disabled on reset + LPC_SC->PCONP |= 0x00010000; + + // pinsel for SPIFI + LPC_IOCON->P2_7 &= ~0x07; + LPC_IOCON->P2_7 |= 0x05; /* SPIFI_CSN @ P2.7 */ + LPC_IOCON->P0_22 &= ~0x07; + LPC_IOCON->P0_22 |= 0x05; /* SPIFI_CLK @ P0.22 */ + LPC_IOCON->P0_15 &= ~0x07; + LPC_IOCON->P0_15 |= 0x5; /* SPIFI_IO2 @ P0.15 */ + LPC_IOCON->P0_16 &= ~0x07; + LPC_IOCON->P0_16 |= 0x5; /* SPIFI_IO3 @ P0.16 */ + LPC_IOCON->P0_17 &= ~0x07; + LPC_IOCON->P0_17 |= 0x5; /* SPIFI_IO1 @ P0.17 */ + LPC_IOCON->P0_18 &= ~0x07; + LPC_IOCON->P0_18 |= 0x5; /* SPIFI_IO0 @ P0.18 */ + + activeTOC = -1; + spifi = NULL; +} + +// All modes are supported but: +// +// 1) All writes are treated as appends +// 2) Truncation is only to size 0, i.e. effectively a delete +// 3) File position operations work like this: +// ReadOnly - dictates where to read from +// WriteOnly - ignored, writes are always at the end +// ReadWrite - dictates where to read from, writes ignore it but +// sets the position to the end afterwards +// +FileHandle *QSPIFileSystem::open(const char *filename, int flags) +{ + fresult res = qspifs_init(); +// if (res == FS_OK) { +// if ((flags & O_ACCMODE) == O_RDONLY) { +// // ok +// } else if (flags & O_APPEND) { +// // ok +// } else { +// // not supported yet, this includes all combination of flags +// // allowing writing at specific positions in the file. This file system +// // only allows appending +// res = FS_ERR_INVALID_PARAM; +// } +// } + if (res == FS_OK) { + if (strlen(filename) > HEADER_FNAME_STRLEN) { + // Filename is too long + res = FS_ERR_INVALID_PARAM; + } + } + if (res == FS_OK) { + // Handle truncation by silently deleting the file before + // attempting to open it + if (flags & O_TRUNC) { + remove(filename); + } + } + if (res == FS_OK) { + fileHandle_t fh = {0,0,0}; + res = qspifs_findFile(filename, &fh); + if ((res == FS_ERR_NO_FILE) && (flags & O_CREAT)) { + res = qspifs_allocateFile(filename, 1, &fh.tocIdx); + } + if (res == FS_OK) { + res = qspifs_saveTOC(); + } + if (res == FS_OK) { + return new QSPIFileHandle(&fh, flags); + } + } + debug_if(QSPI_DBG, "QSPIFS: Failed to open: %d\n", res); + return NULL; +} + +int QSPIFileSystem::remove(const char *filename) +{ + fileHandle_t fh = {0,0,0}; + fresult res = qspifs_init(); + if (res == FS_OK) { + res = qspifs_findFile(filename, &fh); + } + if (res == FS_OK) { + qspifs_deleteFile(&fh); + res = qspifs_saveTOC(); + } + else if (res == FS_ERR_NO_FILE) { + // file does not exist so treat it as a successful deletion + res = FS_OK; + } + if (res != FS_OK) { + debug_if(QSPI_DBG, "QSPIFS: Failed to delete %s: %d\n", filename, res); + return -1; + } + return 0; +} + +int QSPIFileSystem::rename(const char *oldname, const char *newname) +{ + fileHandle_t fhOld = {0,0,0}; + fileHandle_t fhNew = {0,0,0}; + + fresult res = qspifs_init(); + if (res == FS_OK) { + res = qspifs_findFile(oldname, &fhOld); + } + if (res == FS_OK) { + // Make sure the destination file doesn't exist + res = qspifs_findFile(newname, &fhNew); + if (res == FS_OK) { + res = FS_ERR_FILE_EXIST; + } else if (res == FS_ERR_NO_FILE) { + res = FS_OK; + } + } + if (res == FS_OK) { + int numNeededBlocks = 1 + ((fhOld.size + HEADER_LEN) / ERASE_SIZE); + res = qspifs_allocateFile(newname, numNeededBlocks, &fhNew.tocIdx); + if (res == FS_OK) { + const uint8_t* pData = (const uint8_t*)(SPIFI_MEM_BASE + fhOld.tocIdx*ERASE_SIZE + HEADER_LEN); + res = qspifs_write(&fhNew, pData, fhOld.size); + if (res == FS_OK) { + qspifs_deleteFile(&fhOld); + } else { + qspifs_deleteFile(&fhNew); + } + } + qspifs_saveTOC(); + } + if (res != FS_OK) { + debug_if(QSPI_DBG, "QSPIFS: Failed to rename '%s' to '%s': %d\n", oldname, newname, res); + return -1; + } + return 0; +} + +DirHandle *QSPIFileSystem::opendir(const char *name) +{ + FileHandle* fh = open(name, O_RDONLY); + if (fh != NULL) { + // Attempting to open a file as a dir + delete fh; + return NULL; + } + +// printf("opendir: name '%s'\n", name); + if (strlen(name) <= HEADER_DNAME_MAXLEN) { + return QSPIDirHandle::openDir(name); + } + return NULL; +} + +int QSPIFileSystem::mkdir(const char *name, mode_t mode) +{ + // Creating folders is always successful as there are no folders in this filesystem + return 0; +} + +int QSPIFileSystem::format(unsigned int fsSizeInMB) +{ + fresult res = qspifs_init(); + if (res == FS_OK || res == FS_ERR_NOT_FORMATTED) { + if (((fsSizeInMB<<20) > memInfo.memSize) || (fsSizeInMB < 1)) { + debug_if(QSPI_DBG, "QSPIFS: Failed to format to size %d MByte: error %d\n", fsSizeInMB, res); + return -1; + } + activeTOC = -1; + res = qspifs_format(memInfo.memSize - (fsSizeInMB<<20)); + } + + if (res != FS_OK) { + debug_if(QSPI_DBG, "QSPIFS: Failed to format: %d\n", res); + return -1; + } + return 0; +} + +bool QSPIFileSystem::isformatted() +{ + fresult res = qspifs_init(); + if (res == FS_OK) { + return true; + } else if (res == FS_ERR_NOT_FORMATTED) { + return false; + } + debug_if(QSPI_DBG, "QSPIFS: Failed to detect status: %d\n", res); + return false; +} + +bool QSPIFileSystem::getMemoryBoundaries(uint32_t* pStartAddr, uint32_t* pEndAddr) +{ + if (isformatted()) + { + *pEndAddr = 0x28000000 + memInfo.memSize; + + // Look at all blocks except for the reserved ones + for (int i = 0; i < NUM_BLOCKS; i++) + { + if (!TOC_IS_RESERVED(TOC[i])) + { + // Found first non-reserved erase block, indicating the start of + // the file system. + *pStartAddr = SPIFI_MEM_BASE + i*ERASE_SIZE; + return true; + } + } + + // The entire file system seems to be reserved which should never happen + // but just in case, report it as beeing 1MB in size. + *pStartAddr = *pEndAddr - 1024*1024; + return true; + } + return false; +} + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QSPIFileSystem.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,81 @@ +#ifndef QSPIFILESYSTEM_H +#define QSPIFILESYSTEM_H + +#include "mbed.h" +#include "FileSystemLike.h" + +/** Access the filesystem on an QSPI flash using SPIFI + * + * @code + * #include "mbed.h" + * #include "QSPIFileSystem.h" + * + * QSPIFileSystem qspi("qspi"); + * + * int main() { + * if (!qspifs.isFormatted()) { + * qspifs.format(); + * } + * + * FILE *fp = fopen("/qspi/myfile.txt", "w"); + * if (fp != NULL) { + * fprintf(fp, "Hello World!\n"); + * fclose(fp); + * } + * } + * @endcode + */ +class QSPIFileSystem : public FileSystemLike { +public: + + /** Create the File System for accessing a QSPI Flash + * + * @param name The name used to access the virtual filesystem + */ + QSPIFileSystem(const char* name); + + virtual FileHandle *open(const char *filename, int flags); + virtual int remove(const char *filename); + virtual int rename(const char *oldname, const char *newname); + virtual DirHandle *opendir(const char *name); + virtual int mkdir(const char *name, mode_t mode); + + /** Creates a new file system on the QSPI flash. + * The file system will have the specified size and will always + * be positioned at the end of the QSPI flash. If the fsSizeInMB is + * less than the size of the QSPI flash the lower end of the flash + * will be left untouched. + * + * @param fsSizeInMB The size of the file system + * + * @returns + * 0 on success, + * -1 on failure. + */ + int format(unsigned int fsSizeInMB = 8); + + /** Tests if there is a file system present on the QSPI flash + * + * @returns + * True if a valid file system could be found, + * False on failure. + */ + bool isformatted(); + + /** Retrieves the start and end addresses for the file system. + * The pStartAddr and pEndAddr will only be assigned values if the + * function returns true. + * + * @param pStartAddr Will return the start of the file system area + * @param pEndAddr Will return the end of the file system area + * + * @returns + * True if there is a file system, + * False on failure. + */ + bool getMemoryBoundaries(uint32_t* pStartAddr, uint32_t* pEndAddr); +}; + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TSC2046.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,617 @@ + +#include "mbed.h" +#include "TSC2046.h" + +#ifndef ABS +#define ABS(x) ( ((int32_t)(x)) < 0 ? (-(x)) : (x)) +#endif + +#define ADS_START (1 << 7) +#define ADS_A2A1A0_d_y (1 << 4) /* differential */ +#define ADS_A2A1A0_d_z1 (3 << 4) /* differential */ +#define ADS_A2A1A0_d_z2 (4 << 4) /* differential */ +#define ADS_A2A1A0_d_x (5 << 4) /* differential */ +#define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */ +#define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */ +#define ADS_A2A1A0_vaux (6 << 4) /* non-differential */ +#define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */ +#define ADS_8_BIT (1 << 3) +#define ADS_12_BIT (0 << 3) +#define ADS_SER (1 << 2) /* non-differential */ +#define ADS_DFR (0 << 2) /* differential */ +#define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */ +#define ADS_PD10_ADC_ON (1 << 0) /* ADC on */ +#define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */ +#define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ + + +#define READ_12BIT_DFR(d, adc, vref) (ADS_START | d \ + | ADS_12_BIT | ADS_DFR | \ + (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0)) + +#define READ_Y(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_y, 1, vref)) +#define READ_Z1(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_z1, 1, vref)) +#define READ_Z2(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_z2, 1, vref)) +#define READ_X(vref) (READ_12BIT_DFR(ADS_A2A1A0_d_x, 1, vref)) +#define PWRDOWN (READ_12BIT_DFR(ADS_A2A1A0_d_y, 0, 0)) /* LAST */ + +/* single-ended samples need to first power up reference voltage; + * we leave both ADC and VREF powered + */ +#define READ_12BIT_SER(x) (ADS_START | x \ + | ADS_12_BIT | ADS_SER) + +#define REF_ON (READ_12BIT_DFR(ADS_A2A1A0_d_x, 1, 1)) +#define REF_OFF (READ_12BIT_DFR(ADS_A2A1A0_d_y, 0, 0)) + +#define DEBOUNCE_MAX 10 +#define DEBOUNCE_TOL 3 + +TSC2046::TSC2046(PinName mosi, PinName miso, PinName sck, PinName cs) : +_spi(mosi, miso, sck), _cs(cs) +{ + _cs = 1; // active low + + _spi.format(8, 3); + _spi.frequency(1500000); + _calibrated = false; + _initialized = false; +} + + +void TSC2046::read(touchCoordinate_t &coord) { + + touchCoordinate_t tmpCoord; + calibPoint_t displayPoint; + calibPoint_t screenSample; + + if (!_initialized) { + init(); + _initialized = true; + } + + readAndFilter(tmpCoord); + + _cs = 0; + _spi.write(PWRDOWN); + _cs = 1; + + coord.z = tmpCoord.z; + + if (_calibrated) { + screenSample.x = tmpCoord.x; + screenSample.y = tmpCoord.y; + + getDisplayPoint(&displayPoint, &screenSample, &_calibMatrix); + + coord.x = displayPoint.x; + coord.y = displayPoint.y; + } + else { + coord.x = tmpCoord.x; + coord.y = tmpCoord.y; + } + +} + +void TSC2046::calibrate(touchCoordinate_t &ref1, + touchCoordinate_t &ref2, + touchCoordinate_t &ref3, + touchCoordinate_t &scr1, + touchCoordinate_t &scr2, + touchCoordinate_t &scr3) { + + calibPoint_t disp[3]; + calibPoint_t scr[3]; + + disp[0].x = ref1.x; + disp[0].y = ref1.y; + disp[1].x = ref2.x; + disp[1].y = ref2.y; + disp[2].x = ref3.x; + disp[2].y = ref3.y; + + scr[0].x = scr1.x; + scr[0].y = scr1.y; + scr[1].x = scr2.x; + scr[1].y = scr2.y; + scr[2].x = scr3.x; + scr[2].y = scr3.y; + + setCalibrationMatrix(disp, scr, &_calibMatrix); + + _calibrated = true; + +} + +void TSC2046::uncalibrate() { + _calibrated = false; +} + + +void TSC2046::init() { + + _cs = 0; + + _spi.write(REF_ON); + _spi.write((READ_12BIT_SER(ADS_A2A1A0_vaux) | ADS_PD10_ALL_ON)); + _spi.write(PWRDOWN); + + _cs = 1; +} + +void TSC2046::readAndFilter(touchCoordinate_t &coord) +{ + int32_t ix, iy, iz1, iz2 = 0; + int32_t lastx, lasty, lastz1, lastz2 = 0; + int i = 0; + + coord.x = 0; + coord.y = 0; + coord.z = 0; + + lasty = getFilteredValue(READ_Y(0)); + lasty >>= 3; + if (lasty >= 4095) { + lasty = 0; + } + + lastx = getFilteredValue(READ_X(0)); + lastx >>= 3; + if (lastx >= 4095) { + lastx = 0; + } + + lastz1 = getFilteredValue(READ_Z1(0)); + lastz1 >>= 3; + + lastz2 = getFilteredValue(READ_Z2(0)); + lastz2 >>= 3; + + + if (lastx && lastz1) { + coord.z = (lastx * ABS(lastz2 - lastz1)) / lastz1; + } + else { + coord.z = 0; + } + + if (coord.z > 20000) { + coord.z = 0; + } + + if (coord.z == 0) { + return; + } + + for (i = 0; i < DEBOUNCE_MAX; i++) { + iy = getFilteredValue(READ_Y(0)); + iy >>= 3; + + if (ABS (lasty - iy) <= DEBOUNCE_TOL) { + break; + } + + lasty = iy; + } + + for (i = 0; i < DEBOUNCE_MAX; i++) { + ix = getFilteredValue(READ_X(0)); + ix >>= 3; + if (ix > 4095) { + ix = 0; + } + + if (ABS (lastx - ix) <= DEBOUNCE_TOL) { + break; + } + + lastx = ix; + } + + for (i = 0; i < DEBOUNCE_MAX; i++) { + iz1 = getFilteredValue(READ_Z1(0)); + iz1 >>= 3; + + if (ABS (lastz1 - iz1) <= DEBOUNCE_TOL) { + break; + } + + lastz1 = iz1; + } + + for (i = 0; i < DEBOUNCE_MAX; i++) { + iz2 = getFilteredValue(READ_Z2(0)); + iz2 >>= 3; + + if (ABS (lastz2 - iz2) <= DEBOUNCE_TOL) { + break; + } + + lastz2 = iz2; + } + + coord.x = ix; + coord.y = iy; + + if (ix && iz1) { + coord.z = (ix * ABS(iz2 - iz1)) / iz1; + } + else { + coord.z = 0; + } + + if (coord.z > 20000) { + coord.z = 0; + } + +} + +int32_t TSC2046::getFilteredValue(int cmd) +{ + int32_t a[7]; + int32_t tmp = 0; + int i = 0, j = 0; + + /* + * Median and averaging filter + * + * 1. Get 7 values + * 2. Sort these values + * 3. Take average of the 3 values in the middle + */ + + for (i = 0; i < 7; i++) { + a[i] = spiTransfer(cmd); + } + + // bubble sort + for (i = 0; i < 7; i++) { + for (j = 0; j < (7-(i+1)); j++) { + if (a[j] > a[j+1]) { + // swap + tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + + // average of 3 values in the middle + return ((a[2]+a[3]+a[4])/3); +} + +uint16_t TSC2046::spiTransfer(uint8_t cmd) +{ + uint8_t data[3]; + + _cs = 0; + + /*data[0] = */_spi.write(cmd); + data[0] = _spi.write(0xff); + data[1] = _spi.write(0xff); + + _cs = 1; + + return ((data[0] << 8) | data[1]); +} + + +// ############################################################################ +// >>>>>>>> Calibrate code >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// ############################################################################ + + +/* + * + * Copyright (c) 2001, Carlos E. Vidales. All rights reserved. + * + * This sample program was written and put in the public domain + * by Carlos E. Vidales. The program is provided "as is" + * without warranty of any kind, either expressed or implied. + * If you choose to use the program within your own products + * you do so at your own risk, and assume the responsibility + * for servicing, repairing or correcting the program should + * it prove defective in any manner. + * You may copy and distribute the program's source code in any + * medium, provided that you also include in each copy an + * appropriate copyright notice and disclaimer of warranty. + * You may also modify this program and distribute copies of + * it provided that you include prominent notices stating + * that you changed the file(s) and the date of any change, + * and that you do not charge any royalties or licenses for + * its use. + * + * + * + * File Name: calibrate.c + * + * + * This file contains functions that implement calculations + * necessary to obtain calibration factors for a touch screen + * that suffers from multiple distortion effects: namely, + * translation, scaling and rotation. + * + * The following set of equations represent a valid display + * point given a corresponding set of touch screen points: + * + * + * /- -\ + * /- -\ /- -\ | | + * | | | | | Xs | + * | Xd | | A B C | | | + * | | = | | * | Ys | + * | Yd | | D E F | | | + * | | | | | 1 | + * \- -/ \- -/ | | + * \- -/ + * + * + * where: + * + * (Xd,Yd) represents the desired display point + * coordinates, + * + * (Xs,Ys) represents the available touch screen + * coordinates, and the matrix + * + * /- -\ + * |A,B,C| + * |D,E,F| represents the factors used to translate + * \- -/ the available touch screen point values + * into the corresponding display + * coordinates. + * + * + * Note that for practical considerations, the utilitities + * within this file do not use the matrix coefficients as + * defined above, but instead use the following + * equivalents, since floating point math is not used: + * + * A = An/Divider + * B = Bn/Divider + * C = Cn/Divider + * D = Dn/Divider + * E = En/Divider + * F = Fn/Divider + * + * + * + * The functions provided within this file are: + * + * setCalibrationMatrix() - calculates the set of factors + * in the above equation, given + * three sets of test points. + * getDisplayPoint() - returns the actual display + * coordinates, given a set of + * touch screen coordinates. + * translateRawScreenCoordinates() - helper function to transform + * raw screen points into values + * scaled to the desired display + * resolution. + * + * + */ + + +/********************************************************************** + * + * Function: setCalibrationMatrix() + * + * Description: Calling this function with valid input data + * in the display and screen input arguments + * causes the calibration factors between the + * screen and display points to be calculated, + * and the output argument - matrixPtr - to be + * populated. + * + * This function needs to be called only when new + * calibration factors are desired. + * + * + * Argument(s): displayPtr (input) - Pointer to an array of three + * sample, reference points. + * screenPtr (input) - Pointer to the array of touch + * screen points corresponding + * to the reference display points. + * matrixPtr (output) - Pointer to the calibration + * matrix computed for the set + * of points being provided. + * + * + * From the article text, recall that the matrix coefficients are + * resolved to be the following: + * + * + * Divider = (Xs0 - Xs2)*(Ys1 - Ys2) - (Xs1 - Xs2)*(Ys0 - Ys2) + * + * + * + * (Xd0 - Xd2)*(Ys1 - Ys2) - (Xd1 - Xd2)*(Ys0 - Ys2) + * A = --------------------------------------------------- + * Divider + * + * + * (Xs0 - Xs2)*(Xd1 - Xd2) - (Xd0 - Xd2)*(Xs1 - Xs2) + * B = --------------------------------------------------- + * Divider + * + * + * Ys0*(Xs2*Xd1 - Xs1*Xd2) + + * Ys1*(Xs0*Xd2 - Xs2*Xd0) + + * Ys2*(Xs1*Xd0 - Xs0*Xd1) + * C = --------------------------------------------------- + * Divider + * + * + * (Yd0 - Yd2)*(Ys1 - Ys2) - (Yd1 - Yd2)*(Ys0 - Ys2) + * D = --------------------------------------------------- + * Divider + * + * + * (Xs0 - Xs2)*(Yd1 - Yd2) - (Yd0 - Yd2)*(Xs1 - Xs2) + * E = --------------------------------------------------- + * Divider + * + * + * Ys0*(Xs2*Yd1 - Xs1*Yd2) + + * Ys1*(Xs0*Yd2 - Xs2*Yd0) + + * Ys2*(Xs1*Yd0 - Xs0*Yd1) + * F = --------------------------------------------------- + * Divider + * + * + * Return: OK - the calibration matrix was correctly + * calculated and its value is in the + * output argument. + * NOT_OK - an error was detected and the + * function failed to return a valid + * set of matrix values. + * The only time this sample code returns + * NOT_OK is when Divider == 0 + * + * + * + * NOTE! NOTE! NOTE! + * + * setCalibrationMatrix() and getDisplayPoint() will do fine + * for you as they are, provided that your digitizer + * resolution does not exceed 10 bits (1024 values). Higher + * resolutions may cause the integer operations to overflow + * and return incorrect values. If you wish to use these + * functions with digitizer resolutions of 12 bits (4096 + * values) you will either have to a) use 64-bit signed + * integer variables and math, or b) judiciously modify the + * operations to scale results by a factor of 2 or even 4. + * + * + */ +int TSC2046::setCalibrationMatrix( calibPoint_t * displayPtr, + calibPoint_t * screenPtr, + calibMatrix_t * matrixPtr) +{ + int retValue = 0 ; + + + matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + if( matrixPtr->Divider == 0 ) + { + retValue = 1 ; + } + else + { + matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y - screenPtr[2].y)) ; + matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x - displayPtr[2].x)) - + ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x - screenPtr[2].x)) ; + matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x * displayPtr[2].x) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].x - screenPtr[2].x * displayPtr[0].x) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].x - screenPtr[0].x * displayPtr[1].x) * screenPtr[2].y ; + matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y - screenPtr[2].y)) - + ((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y - screenPtr[2].y)) ; + + matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y - displayPtr[2].y)) - + ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x - screenPtr[2].x)) ; + matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x * displayPtr[2].y) * screenPtr[0].y + + (screenPtr[0].x * displayPtr[2].y - screenPtr[2].x * displayPtr[0].y) * screenPtr[1].y + + (screenPtr[1].x * displayPtr[0].y - screenPtr[0].x * displayPtr[1].y) * screenPtr[2].y ; + } + + return( retValue ) ; +} + +/********************************************************************** + * + * Function: getDisplayPoint() + * + * Description: Given a valid set of calibration factors and a point + * value reported by the touch screen, this function + * calculates and returns the true (or closest to true) + * display point below the spot where the touch screen + * was touched. + * + * + * + * Argument(s): displayPtr (output) - Pointer to the calculated + * (true) display point. + * screenPtr (input) - Pointer to the reported touch + * screen point. + * matrixPtr (input) - Pointer to calibration factors + * matrix previously calculated + * from a call to + * setCalibrationMatrix() + * + * + * The function simply solves for Xd and Yd by implementing the + * computations required by the translation matrix. + * + * /- -\ + * /- -\ /- -\ | | + * | | | | | Xs | + * | Xd | | A B C | | | + * | | = | | * | Ys | + * | Yd | | D E F | | | + * | | | | | 1 | + * \- -/ \- -/ | | + * \- -/ + * + * It must be kept brief to avoid consuming CPU cycles. + * + * + * Return: OK - the display point was correctly calculated + * and its value is in the output argument. + * NOT_OK - an error was detected and the function + * failed to return a valid point. + * + * + * + * NOTE! NOTE! NOTE! + * + * setCalibrationMatrix() and getDisplayPoint() will do fine + * for you as they are, provided that your digitizer + * resolution does not exceed 10 bits (1024 values). Higher + * resolutions may cause the integer operations to overflow + * and return incorrect values. If you wish to use these + * functions with digitizer resolutions of 12 bits (4096 + * values) you will either have to a) use 64-bit signed + * integer variables and math, or b) judiciously modify the + * operations to scale results by a factor of 2 or even 4. + * + * + */ +int TSC2046::getDisplayPoint( calibPoint_t * displayPtr, + calibPoint_t * screenPtr, + calibMatrix_t * matrixPtr ) +{ + int retValue = 0 ; + + if( matrixPtr->Divider != 0 ) + { + /* Operation order is important since we are doing integer */ + /* math. Make sure you add all terms together before */ + /* dividing, so that the remainder is not rounded off */ + /* prematurely. */ + displayPtr->x = ( (matrixPtr->An * screenPtr->x) + + (matrixPtr->Bn * screenPtr->y) + + matrixPtr->Cn + ) / matrixPtr->Divider ; + displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) + + (matrixPtr->En * screenPtr->y) + + matrixPtr->Fn + ) / matrixPtr->Divider ; + } + else + { + retValue = 1 ; + } + return( retValue ) ; +} + + +// ############################################################################ +// <<<<<<<< Calibrate code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +// ############################################################################ + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TSC2046.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,99 @@ + +#ifndef TSC2046_H +#define TSC2046_H + + +/** + * Texas Instruments Touch Screen Controller (TSC2046). + */ +class TSC2046 { +public: + + typedef struct { + int32_t x; + int32_t y; + int32_t z; + } touchCoordinate_t; + + /** + * Constructor + * + * @param mosi SPI MOSI pin + * @param miso SPI MISO pin + * @param sck SPI SCK pin + * @param cs chip-select pin + */ + TSC2046(PinName mosi, PinName miso, PinName sck, PinName cs); + + /** + * Read coordinates from the touch panel. + * + * Before calibrate() is called this function will return uncalibrated + * values. If there is no touch active the coordinate values will be 0. + * + * @param coord pointer to coordinate object. The read coordinates will be + * written to this object. + */ + void read(touchCoordinate_t &coord); + + /** + * Calibrate touch screen based on three reference points and + * three actual readings. This means that the user must be presented + * with three points (one at a time) and asked to press on these points + * to get an actual reading. + */ + void calibrate(touchCoordinate_t &ref1, + touchCoordinate_t &ref2, + touchCoordinate_t &ref3, + touchCoordinate_t &scr1, + touchCoordinate_t &scr2, + touchCoordinate_t &scr3); + + /** + * Reset a previous calibration (in order to do a new calibration) + */ + void uncalibrate(); + + +private: + + typedef struct { + int64_t x; + int64_t y; + } calibPoint_t; + + typedef struct { + int64_t An; + int64_t Bn; + int64_t Cn; + int64_t Dn; + int64_t En; + int64_t Fn; + int64_t Divider; + } calibMatrix_t; + + SPI _spi; + DigitalOut _cs; + bool _calibrated; + bool _initialized; + calibMatrix_t _calibMatrix; + + void init(); + void readAndFilter(touchCoordinate_t &coord); + int32_t getFilteredValue(int cmd); + uint16_t spiTransfer(uint8_t cmd); + + + + int setCalibrationMatrix( calibPoint_t * displayPtr, + calibPoint_t * screenPtr, + calibMatrix_t * matrixPtr); + int getDisplayPoint( calibPoint_t * displayPtr, + calibPoint_t * screenPtr, + calibMatrix_t * matrixPtr ); + +}; + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/glcdfont.c Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,272 @@ +#ifndef FONT5X7_H +#define FONT5X7_H + +#ifdef __AVR__ +#include <avr/io.h> +#include <avr/pgmspace.h> +#else +#define PROGMEM +#endif + +// Standard ASCII 5x7 font + +static const unsigned char font[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, + 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, + 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, + 0x18, 0x3C, 0x7E, 0x3C, 0x18, + 0x1C, 0x57, 0x7D, 0x57, 0x1C, + 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, + 0x00, 0x18, 0x3C, 0x18, 0x00, + 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, + 0x00, 0x18, 0x24, 0x18, 0x00, + 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, + 0x30, 0x48, 0x3A, 0x06, 0x0E, + 0x26, 0x29, 0x79, 0x29, 0x26, + 0x40, 0x7F, 0x05, 0x05, 0x07, + 0x40, 0x7F, 0x05, 0x25, 0x3F, + 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, + 0x7F, 0x3E, 0x1C, 0x1C, 0x08, + 0x08, 0x1C, 0x1C, 0x3E, 0x7F, + 0x14, 0x22, 0x7F, 0x22, 0x14, + 0x5F, 0x5F, 0x00, 0x5F, 0x5F, + 0x06, 0x09, 0x7F, 0x01, 0x7F, + 0x00, 0x66, 0x89, 0x95, 0x6A, + 0x60, 0x60, 0x60, 0x60, 0x60, + 0x94, 0xA2, 0xFF, 0xA2, 0x94, + 0x08, 0x04, 0x7E, 0x04, 0x08, + 0x10, 0x20, 0x7E, 0x20, 0x10, + 0x08, 0x08, 0x2A, 0x1C, 0x08, + 0x08, 0x1C, 0x2A, 0x08, 0x08, + 0x1E, 0x10, 0x10, 0x10, 0x10, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, + 0x30, 0x38, 0x3E, 0x38, 0x30, + 0x06, 0x0E, 0x3E, 0x0E, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x07, 0x00, + 0x14, 0x7F, 0x14, 0x7F, 0x14, + 0x24, 0x2A, 0x7F, 0x2A, 0x12, + 0x23, 0x13, 0x08, 0x64, 0x62, + 0x36, 0x49, 0x56, 0x20, 0x50, + 0x00, 0x08, 0x07, 0x03, 0x00, + 0x00, 0x1C, 0x22, 0x41, 0x00, + 0x00, 0x41, 0x22, 0x1C, 0x00, + 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, + 0x08, 0x08, 0x3E, 0x08, 0x08, + 0x00, 0x80, 0x70, 0x30, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x60, 0x60, 0x00, + 0x20, 0x10, 0x08, 0x04, 0x02, + 0x3E, 0x51, 0x49, 0x45, 0x3E, + 0x00, 0x42, 0x7F, 0x40, 0x00, + 0x72, 0x49, 0x49, 0x49, 0x46, + 0x21, 0x41, 0x49, 0x4D, 0x33, + 0x18, 0x14, 0x12, 0x7F, 0x10, + 0x27, 0x45, 0x45, 0x45, 0x39, + 0x3C, 0x4A, 0x49, 0x49, 0x31, + 0x41, 0x21, 0x11, 0x09, 0x07, + 0x36, 0x49, 0x49, 0x49, 0x36, + 0x46, 0x49, 0x49, 0x29, 0x1E, + 0x00, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x40, 0x34, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x41, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x00, 0x41, 0x22, 0x14, 0x08, + 0x02, 0x01, 0x59, 0x09, 0x06, + 0x3E, 0x41, 0x5D, 0x59, 0x4E, + 0x7C, 0x12, 0x11, 0x12, 0x7C, + 0x7F, 0x49, 0x49, 0x49, 0x36, + 0x3E, 0x41, 0x41, 0x41, 0x22, + 0x7F, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x49, 0x49, 0x49, 0x41, + 0x7F, 0x09, 0x09, 0x09, 0x01, + 0x3E, 0x41, 0x41, 0x51, 0x73, + 0x7F, 0x08, 0x08, 0x08, 0x7F, + 0x00, 0x41, 0x7F, 0x41, 0x00, + 0x20, 0x40, 0x41, 0x3F, 0x01, + 0x7F, 0x08, 0x14, 0x22, 0x41, + 0x7F, 0x40, 0x40, 0x40, 0x40, + 0x7F, 0x02, 0x1C, 0x02, 0x7F, + 0x7F, 0x04, 0x08, 0x10, 0x7F, + 0x3E, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x09, 0x09, 0x09, 0x06, + 0x3E, 0x41, 0x51, 0x21, 0x5E, + 0x7F, 0x09, 0x19, 0x29, 0x46, + 0x26, 0x49, 0x49, 0x49, 0x32, + 0x03, 0x01, 0x7F, 0x01, 0x03, + 0x3F, 0x40, 0x40, 0x40, 0x3F, + 0x1F, 0x20, 0x40, 0x20, 0x1F, + 0x3F, 0x40, 0x38, 0x40, 0x3F, + 0x63, 0x14, 0x08, 0x14, 0x63, + 0x03, 0x04, 0x78, 0x04, 0x03, + 0x61, 0x59, 0x49, 0x4D, 0x43, + 0x00, 0x7F, 0x41, 0x41, 0x41, + 0x02, 0x04, 0x08, 0x10, 0x20, + 0x00, 0x41, 0x41, 0x41, 0x7F, + 0x04, 0x02, 0x01, 0x02, 0x04, + 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x03, 0x07, 0x08, 0x00, + 0x20, 0x54, 0x54, 0x78, 0x40, + 0x7F, 0x28, 0x44, 0x44, 0x38, + 0x38, 0x44, 0x44, 0x44, 0x28, + 0x38, 0x44, 0x44, 0x28, 0x7F, + 0x38, 0x54, 0x54, 0x54, 0x18, + 0x00, 0x08, 0x7E, 0x09, 0x02, + 0x18, 0xA4, 0xA4, 0x9C, 0x78, + 0x7F, 0x08, 0x04, 0x04, 0x78, + 0x00, 0x44, 0x7D, 0x40, 0x00, + 0x20, 0x40, 0x40, 0x3D, 0x00, + 0x7F, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x41, 0x7F, 0x40, 0x00, + 0x7C, 0x04, 0x78, 0x04, 0x78, + 0x7C, 0x08, 0x04, 0x04, 0x78, + 0x38, 0x44, 0x44, 0x44, 0x38, + 0xFC, 0x18, 0x24, 0x24, 0x18, + 0x18, 0x24, 0x24, 0x18, 0xFC, + 0x7C, 0x08, 0x04, 0x04, 0x08, + 0x48, 0x54, 0x54, 0x54, 0x24, + 0x04, 0x04, 0x3F, 0x44, 0x24, + 0x3C, 0x40, 0x40, 0x20, 0x7C, + 0x1C, 0x20, 0x40, 0x20, 0x1C, + 0x3C, 0x40, 0x30, 0x40, 0x3C, + 0x44, 0x28, 0x10, 0x28, 0x44, + 0x4C, 0x90, 0x90, 0x90, 0x7C, + 0x44, 0x64, 0x54, 0x4C, 0x44, + 0x00, 0x08, 0x36, 0x41, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, + 0x00, 0x41, 0x36, 0x08, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x02, + 0x3C, 0x26, 0x23, 0x26, 0x3C, + 0x1E, 0xA1, 0xA1, 0x61, 0x12, + 0x3A, 0x40, 0x40, 0x20, 0x7A, + 0x38, 0x54, 0x54, 0x55, 0x59, + 0x21, 0x55, 0x55, 0x79, 0x41, + 0x21, 0x54, 0x54, 0x78, 0x41, + 0x21, 0x55, 0x54, 0x78, 0x40, + 0x20, 0x54, 0x55, 0x79, 0x40, + 0x0C, 0x1E, 0x52, 0x72, 0x12, + 0x39, 0x55, 0x55, 0x55, 0x59, + 0x39, 0x54, 0x54, 0x54, 0x59, + 0x39, 0x55, 0x54, 0x54, 0x58, + 0x00, 0x00, 0x45, 0x7C, 0x41, + 0x00, 0x02, 0x45, 0x7D, 0x42, + 0x00, 0x01, 0x45, 0x7C, 0x40, + 0xF0, 0x29, 0x24, 0x29, 0xF0, + 0xF0, 0x28, 0x25, 0x28, 0xF0, + 0x7C, 0x54, 0x55, 0x45, 0x00, + 0x20, 0x54, 0x54, 0x7C, 0x54, + 0x7C, 0x0A, 0x09, 0x7F, 0x49, + 0x32, 0x49, 0x49, 0x49, 0x32, + 0x32, 0x48, 0x48, 0x48, 0x32, + 0x32, 0x4A, 0x48, 0x48, 0x30, + 0x3A, 0x41, 0x41, 0x21, 0x7A, + 0x3A, 0x42, 0x40, 0x20, 0x78, + 0x00, 0x9D, 0xA0, 0xA0, 0x7D, + 0x39, 0x44, 0x44, 0x44, 0x39, + 0x3D, 0x40, 0x40, 0x40, 0x3D, + 0x3C, 0x24, 0xFF, 0x24, 0x24, + 0x48, 0x7E, 0x49, 0x43, 0x66, + 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, + 0xFF, 0x09, 0x29, 0xF6, 0x20, + 0xC0, 0x88, 0x7E, 0x09, 0x03, + 0x20, 0x54, 0x54, 0x79, 0x41, + 0x00, 0x00, 0x44, 0x7D, 0x41, + 0x30, 0x48, 0x48, 0x4A, 0x32, + 0x38, 0x40, 0x40, 0x22, 0x7A, + 0x00, 0x7A, 0x0A, 0x0A, 0x72, + 0x7D, 0x0D, 0x19, 0x31, 0x7D, + 0x26, 0x29, 0x29, 0x2F, 0x28, + 0x26, 0x29, 0x29, 0x29, 0x26, + 0x30, 0x48, 0x4D, 0x40, 0x20, + 0x38, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x38, + 0x2F, 0x10, 0xC8, 0xAC, 0xBA, + 0x2F, 0x10, 0x28, 0x34, 0xFA, + 0x00, 0x00, 0x7B, 0x00, 0x00, + 0x08, 0x14, 0x2A, 0x14, 0x22, + 0x22, 0x14, 0x2A, 0x14, 0x08, + 0xAA, 0x00, 0x55, 0x00, 0xAA, + 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x10, 0x10, 0x10, 0xFF, 0x00, + 0x14, 0x14, 0x14, 0xFF, 0x00, + 0x10, 0x10, 0xFF, 0x00, 0xFF, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x14, 0x14, 0x14, 0xFC, 0x00, + 0x14, 0x14, 0xF7, 0x00, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x14, 0x14, 0xF4, 0x04, 0xFC, + 0x14, 0x14, 0x17, 0x10, 0x1F, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0x1F, 0x00, + 0x10, 0x10, 0x10, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0xF0, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0xFF, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x14, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x1F, 0x10, 0x17, + 0x00, 0x00, 0xFC, 0x04, 0xF4, + 0x14, 0x14, 0x17, 0x10, 0x17, + 0x14, 0x14, 0xF4, 0x04, 0xF4, + 0x00, 0x00, 0xFF, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0xF7, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x17, 0x14, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0xF4, 0x14, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x00, 0x00, 0x1F, 0x10, 0x1F, + 0x00, 0x00, 0x00, 0x1F, 0x14, + 0x00, 0x00, 0x00, 0xFC, 0x14, + 0x00, 0x00, 0xF0, 0x10, 0xF0, + 0x10, 0x10, 0xFF, 0x10, 0xFF, + 0x14, 0x14, 0x14, 0xFF, 0x14, + 0x10, 0x10, 0x10, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x10, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x38, 0x44, 0x44, 0x38, 0x44, + 0x7C, 0x2A, 0x2A, 0x3E, 0x14, + 0x7E, 0x02, 0x02, 0x06, 0x06, + 0x02, 0x7E, 0x02, 0x7E, 0x02, + 0x63, 0x55, 0x49, 0x41, 0x63, + 0x38, 0x44, 0x44, 0x3C, 0x04, + 0x40, 0x7E, 0x20, 0x1E, 0x20, + 0x06, 0x02, 0x7E, 0x02, 0x02, + 0x99, 0xA5, 0xE7, 0xA5, 0x99, + 0x1C, 0x2A, 0x49, 0x2A, 0x1C, + 0x4C, 0x72, 0x01, 0x72, 0x4C, + 0x30, 0x4A, 0x4D, 0x4D, 0x30, + 0x30, 0x48, 0x78, 0x48, 0x30, + 0xBC, 0x62, 0x5A, 0x46, 0x3D, + 0x3E, 0x49, 0x49, 0x49, 0x00, + 0x7E, 0x01, 0x01, 0x01, 0x7E, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x44, 0x44, 0x5F, 0x44, 0x44, + 0x40, 0x51, 0x4A, 0x44, 0x40, + 0x40, 0x44, 0x4A, 0x51, 0x40, + 0x00, 0x00, 0xFF, 0x01, 0x03, + 0xE0, 0x80, 0xFF, 0x00, 0x00, + 0x08, 0x08, 0x6B, 0x6B, 0x08, + 0x36, 0x12, 0x36, 0x24, 0x36, + 0x06, 0x0F, 0x09, 0x0F, 0x06, + 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x00, + 0x30, 0x40, 0xFF, 0x01, 0x01, + 0x00, 0x1F, 0x01, 0x01, 0x1E, + 0x00, 0x19, 0x1D, 0x17, 0x12, + 0x00, 0x3C, 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#endif // FONT5X7_H + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpdma.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,312 @@ + +/****************************************************************************** + * Includes + *****************************************************************************/ + +#include "gpdma.h" + +/****************************************************************************** + * Defines and typedefs + *****************************************************************************/ + +#define NUM_GPDMA_CHANNELS 8 + +#define GPDMACH(__x) ((LPC_GPDMACH_TypeDef*)(LPC_GPDMACH0_BASE + (0x20 * (__x)))) + +#define CH_MASK(__ch) (((1UL << (__ch)) & 0xFF)) + +/** + * @brief GPDMA request connections + */ +#define GPDMA_CONN_MEMORY ((0UL)) +#define GPDMA_CONN_SDC ((1UL)) /*!< SD card */ + +/** + * @brief Macro defines for DMA channel control registers + */ +#define GPDMA_DMACCxControl_TransferSize(n) (((n & 0xFFF) << 0)) /*!< Transfer size*/ +#define GPDMA_DMACCxControl_SBSize(n) (((n & 0x07) << 12)) /*!< Source burst size*/ +#define GPDMA_DMACCxControl_DBSize(n) (((n & 0x07) << 15)) /*!< Destination burst size*/ +#define GPDMA_DMACCxControl_SWidth(n) (((n & 0x07) << 18)) /*!< Source transfer width*/ +#define GPDMA_DMACCxControl_DWidth(n) (((n & 0x07) << 21)) /*!< Destination transfer width*/ +#define GPDMA_DMACCxControl_SI ((1UL << 26)) /*!< Source increment*/ +#define GPDMA_DMACCxControl_DI ((1UL << 27)) /*!< Destination increment*/ +#define GPDMA_DMACCxControl_SrcTransUseAHBMaster1 0 +#define GPDMA_DMACCxControl_DestTransUseAHBMaster1 0 +#define GPDMA_DMACCxControl_Prot1 ((1UL << 28)) /*!< Indicates that the access is in user mode or privileged mode*/ +#define GPDMA_DMACCxControl_Prot2 ((1UL << 29)) /*!< Indicates that the access is bufferable or not bufferable*/ +#define GPDMA_DMACCxControl_Prot3 ((1UL << 30)) /*!< Indicates that the access is cacheable or not cacheable*/ +#define GPDMA_DMACCxControl_I ((1UL << 31)) /*!< Terminal count interrupt enable bit */ + +/** + * @brief GPDMA Burst size in Source and Destination definitions + */ +#define GPDMA_BSIZE_1 ((0UL)) /*!< Burst size = 1 */ +#define GPDMA_BSIZE_4 ((1UL)) /*!< Burst size = 4 */ +#define GPDMA_BSIZE_8 ((2UL)) /*!< Burst size = 8 */ +#define GPDMA_BSIZE_16 ((3UL)) /*!< Burst size = 16 */ +#define GPDMA_BSIZE_32 ((4UL)) /*!< Burst size = 32 */ +#define GPDMA_BSIZE_64 ((5UL)) /*!< Burst size = 64 */ +#define GPDMA_BSIZE_128 ((6UL)) /*!< Burst size = 128 */ +#define GPDMA_BSIZE_256 ((7UL)) /*!< Burst size = 256 */ + +/** + * @brief Width in Source transfer width and Destination transfer width definitions + */ +#define GPDMA_WIDTH_BYTE ((0UL)) /*!< Width = 1 byte */ +#define GPDMA_WIDTH_HALFWORD ((1UL)) /*!< Width = 2 bytes */ +#define GPDMA_WIDTH_WORD ((2UL)) /*!< Width = 4 bytes */ + +/** + * @brief Macro defines for DMA Configuration register + */ +#define GPDMA_DMACConfig_E ((0x01)) /*!< DMA Controller enable*/ +#define GPDMA_DMACConfig_M ((0x02)) /*!< AHB Master endianness configuration*/ +#define GPDMA_DMACConfig_BITMASK ((0x03)) + +/** + * @brief Macro defines for DMA Channel Configuration registers + */ +#define GPDMA_DMACCxConfig_E ((1UL << 0)) /*!< DMA control enable*/ +#define GPDMA_DMACCxConfig_SrcPeripheral(n) (((n & 0x1F) << 1)) /*!< Source peripheral*/ +#define GPDMA_DMACCxConfig_DestPeripheral(n) (((n & 0x1F) << 6)) /*!< Destination peripheral*/ +#define GPDMA_DMACCxConfig_TransferType(n) (((n & 0x7) << 11)) /*!< This value indicates the type of transfer*/ +#define GPDMA_DMACCxConfig_IE ((1UL << 14)) /*!< Interrupt error mask*/ +#define GPDMA_DMACCxConfig_ITC ((1UL << 15)) /*!< Terminal count interrupt mask*/ +#define GPDMA_DMACCxConfig_L ((1UL << 16)) /*!< Lock*/ +#define GPDMA_DMACCxConfig_A ((1UL << 17)) /*!< Active*/ +#define GPDMA_DMACCxConfig_H ((1UL << 18)) /*!< Halt*/ + +/** + * @brief GPDMA structure using for DMA configuration + */ +typedef struct { + uint32_t ChannelNum; /*!< DMA channel number, should be in + * range from 0 to 7. + * Note: DMA channel 0 has the highest priority + * and DMA channel 7 the lowest priority. + */ + uint32_t TransferSize; /*!< Length/Size of transfer */ + uint32_t TransferWidth; /*!< Transfer width - used for TransferType is GPDMA_TRANSFERTYPE_M2M only */ + uint32_t SrcAddr; /*!< Physical Source Address, used in case TransferType is chosen as + * GPDMA_TRANSFERTYPE_M2M or GPDMA_TRANSFERTYPE_M2P */ + uint32_t DstAddr; /*!< Physical Destination Address, used in case TransferType is chosen as + * GPDMA_TRANSFERTYPE_M2M or GPDMA_TRANSFERTYPE_P2M */ + uint32_t TransferType; /*!< Transfer Type, should be one of the following: + * - GPDMA_TRANSFERTYPE_M2M: Memory to memory - DMA control + * - GPDMA_TRANSFERTYPE_M2P: Memory to peripheral - DMA control + * - GPDMA_TRANSFERTYPE_P2M: Peripheral to memory - DMA control + * - GPDMA_TRANSFERTYPE_P2P: Source peripheral to destination peripheral - DMA control + */ +} GPDMA_Channel_CFG_T; + +/****************************************************************************** + * External global variables + *****************************************************************************/ + +/****************************************************************************** + * Local variables + *****************************************************************************/ + +static bool used_channels[NUM_GPDMA_CHANNELS]; + +/****************************************************************************** + * Local Functions + *****************************************************************************/ + +static void gpdma_transfer(GPDMA_Channel_CFG_T* cfg, + uint32_t CtrlWord, + uint32_t LinkListItem, + uint8_t SrcPeripheral, + uint8_t DstPeripheral) +{ + /* Get Channel pointer */ + LPC_GPDMACH_TypeDef* pCh = GPDMACH(cfg->ChannelNum); + + /* Reset the Interrupt status */ + LPC_GPDMA->IntTCClear = CH_MASK(cfg->ChannelNum); + LPC_GPDMA->IntErrClr = CH_MASK(cfg->ChannelNum); + + /* Assign Linker List Item value */ + pCh->CLLI = LinkListItem; + + /* Enable DMA channels, little endian */ + LPC_GPDMA->Config = GPDMA_DMACConfig_E; + while (!(LPC_GPDMA->Config & GPDMA_DMACConfig_E)) {} + + pCh->CSrcAddr = cfg->SrcAddr; + pCh->CDestAddr = cfg->DstAddr; + + /* Configure DMA Channel, enable Error Counter and Terminate counter */ + pCh->CConfig = GPDMA_DMACCxConfig_IE + | GPDMA_DMACCxConfig_ITC + | GPDMA_DMACCxConfig_TransferType((uint32_t) cfg->TransferType) + | GPDMA_DMACCxConfig_SrcPeripheral(SrcPeripheral) + | GPDMA_DMACCxConfig_DestPeripheral(DstPeripheral); + + pCh->CControl = CtrlWord; + + /* Start the Channel */ + pCh->CConfig |= GPDMA_DMACCxConfig_E; +} + +/****************************************************************************** + * Public Functions + *****************************************************************************/ + +void gpdma_init() +{ + uint8_t i; + + /* Enable GPDMA master clock */ + LPC_SC->PCONP |= (1<<29); + + /* Reset all channel configuration register */ + for (i = 0; i < NUM_GPDMA_CHANNELS; i++) { + GPDMACH(i)->CConfig = 0; + } + + /* Clear all DMA interrupt and error flag */ + LPC_GPDMA->IntTCClear = 0xFF; + LPC_GPDMA->IntErrClr = 0xFF; + + /* Reset all channels are free */ + for (int i = 0; i < NUM_GPDMA_CHANNELS; i++) + { + used_channels[i] = false; + } +} + +void gpdma_deinit() +{ + /* Disable GPDMA master clock */ + LPC_SC->PCONP &= ~(1<<29); +} + +void gpdma_stop(uint8_t ChannelNum) +{ + if (ChannelNum >= NUM_GPDMA_CHANNELS) { + return; + } + + /* Disable channel */ + GPDMACH(ChannelNum)->CConfig &= ~GPDMA_DMACCxConfig_E; + + /* check terminal count interrupt request status for DMA */ + if (LPC_GPDMA->IntTCStat & CH_MASK(ChannelNum)) { + /* Clear terminate counter Interrupt pending */ + LPC_GPDMA->IntTCClear = CH_MASK(ChannelNum); + } + + /* check status of the error interrupt for DMA channels */ + if (LPC_GPDMA->IntErrStat & CH_MASK(ChannelNum)) { + /* clear the error interrupt request */ + LPC_GPDMA->IntErrClr = CH_MASK(ChannelNum); + } + + used_channels[ChannelNum] = false; +} + +bool gpdma_interrupt(uint8_t ChannelNum) +{ + /* check status of DMA channel interrupts */ + if (LPC_GPDMA->IntStat & CH_MASK(ChannelNum)) { + /* Check counter terminal status */ + if (LPC_GPDMA->IntTCStat & CH_MASK(ChannelNum)) { + /* Clear terminate counter Interrupt pending */ + LPC_GPDMA->IntTCClear = CH_MASK(ChannelNum); + return true; + } + /* Check error terminal status */ + if (LPC_GPDMA->IntErrStat & CH_MASK(ChannelNum)) { + /* Clear error counter Interrupt pending */ + LPC_GPDMA->IntErrClr = CH_MASK(ChannelNum); + return false; + } + } + return false; +} + +bool gpdma_getFreeChannel(uint8_t* pCh) +{ + for (int i = 0; i < NUM_GPDMA_CHANNELS; i++) + { + if ((!used_channels[i]) && ((LPC_GPDMA->EnbldChns & CH_MASK(i)) == 0)) + { + used_channels[i] = true; + *pCh = i; + return true; + } + } + return false; +} + +bool gpdma_transfer_to_mci(uint8_t ChannelNum, + uint32_t src, + uint32_t Size) +{ + GPDMA_Channel_CFG_T cfg; + cfg.ChannelNum = ChannelNum; + cfg.TransferType = GPDMA_TRANSFERTYPE_M2P_CONTROLLER_PERIPHERAL; + cfg.TransferSize = Size; + cfg.TransferWidth = 0; + cfg.SrcAddr = src; + cfg.DstAddr = (uint32_t) (&LPC_MCI->FIFO); + + uint32_t ctrl_word = + GPDMA_DMACCxControl_TransferSize((uint32_t) cfg.TransferSize) + | GPDMA_DMACCxControl_SBSize(GPDMA_BSIZE_8) + | GPDMA_DMACCxControl_DBSize(GPDMA_BSIZE_8) + | GPDMA_DMACCxControl_SWidth(GPDMA_WIDTH_WORD) + | GPDMA_DMACCxControl_DWidth(GPDMA_WIDTH_WORD) + | GPDMA_DMACCxControl_DestTransUseAHBMaster1 + | GPDMA_DMACCxControl_SI + | GPDMA_DMACCxControl_I; + + if (LPC_GPDMA->EnbldChns & CH_MASK(ChannelNum)) { + /* This channel is enabled, return ERROR, need to release this channel first */ + return false; + } + + /* Select SD card interface in the DMA MUX*/ + LPC_SC->DMAREQSEL &= ~(1 << 1); + + gpdma_transfer(&cfg, ctrl_word, 0, GPDMA_CONN_MEMORY, GPDMA_CONN_SDC); + return true; +} + +bool gpdma_transfer_from_mci(uint8_t ChannelNum, + uint32_t dst, + uint32_t Size) +{ + GPDMA_Channel_CFG_T cfg; + cfg.ChannelNum = ChannelNum; + cfg.TransferType = GPDMA_TRANSFERTYPE_P2M_CONTROLLER_PERIPHERAL; + cfg.TransferSize = Size; + cfg.TransferWidth = 0; + cfg.SrcAddr = (uint32_t) (&LPC_MCI->FIFO); + cfg.DstAddr = dst; + + uint32_t ctrl_word = + GPDMA_DMACCxControl_TransferSize((uint32_t) cfg.TransferSize) + | GPDMA_DMACCxControl_SBSize(GPDMA_BSIZE_8) + | GPDMA_DMACCxControl_DBSize(GPDMA_BSIZE_8) + | GPDMA_DMACCxControl_SWidth(GPDMA_WIDTH_WORD) + | GPDMA_DMACCxControl_DWidth(GPDMA_WIDTH_WORD) + | GPDMA_DMACCxControl_SrcTransUseAHBMaster1 + | GPDMA_DMACCxControl_DI + | GPDMA_DMACCxControl_I; + + if (LPC_GPDMA->EnbldChns & CH_MASK(ChannelNum)) { + /* This channel is enabled, return ERROR, need to release this channel first */ + return false; + } + + /* Select SD card interface in the DMA MUX*/ + LPC_SC->DMAREQSEL &= ~(1 << 1); + + gpdma_transfer(&cfg, ctrl_word, 0, GPDMA_CONN_SDC, GPDMA_CONN_MEMORY); + return true; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpdma.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,91 @@ + +#ifndef GPDMA_H +#define GPDMA_H + +#include "platform.h" + +#define GPDMA_CONN_SDC ((1UL)) /*!< SD card */ + +typedef enum +{ + GPDMA_TRANSFERTYPE_M2M_CONTROLLER_DMA, /* Memory to memory - DMA control */ + GPDMA_TRANSFERTYPE_M2P_CONTROLLER_DMA, /* Memory to peripheral - DMA control */ + GPDMA_TRANSFERTYPE_P2M_CONTROLLER_DMA, /* Peripheral to memory - DMA control */ + GPDMA_TRANSFERTYPE_P2P_CONTROLLER_DMA, /* Source peripheral to destination peripheral - DMA control */ + GPDMA_TRANSFERTYPE_P2P_CONTROLLER_DestPERIPHERAL, /* Source peripheral to destination peripheral - destination peripheral control */ + GPDMA_TRANSFERTYPE_M2P_CONTROLLER_PERIPHERAL, /* Memory to peripheral - peripheral control */ + GPDMA_TRANSFERTYPE_P2M_CONTROLLER_PERIPHERAL, /* Peripheral to memory - peripheral control */ + GPDMA_TRANSFERTYPE_P2P_CONTROLLER_SrcPERIPHERAL, /* Source peripheral to destination peripheral - source peripheral control */ +} gpdma_flowControl_t; + +/** + * @brief Initialize the GPDMA + * @param pGPDMA : The base of GPDMA on the chip + * @return Nothing + */ +void gpdma_init(); + +/** + * @brief Shutdown the GPDMA + * @param pGPDMA : The base of GPDMA on the chip + * @return Nothing + */ +void gpdma_deinit(); + +/** + * @brief Stop a stream DMA transfer + * @param ChannelNum : Channel Number to be closed + * @return Nothing + */ +void gpdma_stop(uint8_t ChannelNum); + +/** + * @brief The GPDMA stream interrupt status checking + * @param ChannelNum : Channel Number to be checked on interruption + * @return Status: + * - true : DMA transfer success + * - false : DMA transfer failed + */ +bool gpdma_interrupt(uint8_t ChannelNum); + +/** + * @brief Get a free GPDMA channel for one DMA connection + * @param pCh : Assigned channel number (only valid if success) + * @return Status: + * - true : Found a free DMA channel + * - false : No free DMA channels, pCh value is undefined + */ +bool gpdma_getFreeChannel(uint8_t* pCh); + +/** + * @brief Do a DMA transfer M2M, M2P,P2M or P2P + * @param ChannelNum : Channel used for transfer + * @param src : Address of Memory or PeripheralConnection_ID which is the source + * @param dst : Address of Memory or PeripheralConnection_ID which is the destination + * @param TransferType: Select the transfer controller and the type of transfer. Should be: + * - GPDMA_TRANSFERTYPE_M2M_CONTROLLER_DMA + * - GPDMA_TRANSFERTYPE_M2P_CONTROLLER_DMA + * - GPDMA_TRANSFERTYPE_P2M_CONTROLLER_DMA + * - GPDMA_TRANSFERTYPE_P2P_CONTROLLER_DMA + * - GPDMA_TRANSFERTYPE_P2P_CONTROLLER_DestPERIPHERAL + * - GPDMA_TRANSFERTYPE_M2P_CONTROLLER_PERIPHERAL + * - GPDMA_TRANSFERTYPE_P2M_CONTROLLER_PERIPHERAL + * - GPDMA_TRANSFERTYPE_P2P_CONTROLLER_SrcPERIPHERAL + * @param Size : The number of DMA transfers + * @return False on error, true on success + */ +//bool gpdma_transfer(uint8_t ChannelNum, +// uint32_t src, +// uint32_t dst, +// gpdma_flowControl_t TransferType, +// uint32_t Size); + +bool gpdma_transfer_to_mci(uint8_t ChannelNum, + uint32_t src, + uint32_t Size); +bool gpdma_transfer_from_mci(uint8_t ChannelNum, + uint32_t dst, + uint32_t Size); +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdram.cpp Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,587 @@ +/***************************************************************************** + * + * Copyright(C) 2011, Embedded Artists AB + * All rights reserved. + * + ****************************************************************************** + * Software that is described herein is for illustrative purposes only + * which provides customers with programming information regarding the + * products. This software is supplied "AS IS" without any warranties. + * Embedded Artists AB assumes no responsibility or liability for the + * use of the software, conveys no license or title under any patent, + * copyright, or mask work right to the product. Embedded Artists AB + * reserves the right to make changes in the software without + * notification. Embedded Artists AB also make no representation or + * warranty that such application will be suitable for the specified + * use without further testing or modification. + *****************************************************************************/ + + + +/****************************************************************************** + * Includes + *****************************************************************************/ + +#include "mbed.h" +#include "sdram.h" + + +/****************************************************************************** + * Defines and typedefs + *****************************************************************************/ + + +/****************************************************************************** + * External global variables + *****************************************************************************/ + +/****************************************************************************** + * Local variables + *****************************************************************************/ + +static volatile uint32_t ringosccount[2] = {0,0}; + +static bool okToUseSdramForHeap = true; +static bool initialized = false; + +/****************************************************************************** + * Overridden Global Functions + *****************************************************************************/ + +#if defined(TOOLCHAIN_ARM) /* KEIL uVision and mbed online compiler */ + //http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0349c/Cihehbce.html + + extern "C" unsigned __rt_heap_extend(unsigned size, void **block) { + static uint32_t lastReturnedBlock = 0; + + if (okToUseSdramForHeap && !initialized) { + sdram_init(); + } + + // Make sure that SDRAM is only returned once (as all of it is returned + // the first time) and only if the user has chosen to do it (via the + // okToUseSdramForHeap variable. + if (okToUseSdramForHeap && lastReturnedBlock==0) { + *block = (void*)SDRAM_BASE; + lastReturnedBlock = SDRAM_BASE; + return SDRAM_SIZE; + } + return 0; + } +#elif defined(TOOLCHAIN_GCC_CR) /* CodeRed's RedSuite or LPCXpresso IDE */ + + // NOTE: This way of overriding the implementation of malloc in NEWLIB + // will prevent the internal RAM from being used by malloc as + // it only exposes the SDRAM. + + // Dynamic memory allocation related syscall. + extern "C" caddr_t _sbrk(int incr) { + static unsigned char* heap = (unsigned char*)SDRAM_BASE; + unsigned char* prev_heap = heap; + unsigned char* new_heap = heap + incr; + + if (okToUseSdramForHeap && !initialized) { + sdram_init(); + } + if (!okToUseSdramForHeap) { + //errno = ENOMEM; + return (caddr_t)-1; + } + if (new_heap >= (unsigned char*)(SDRAM_BASE + SDRAM_SIZE)) { + //errno = ENOMEM; + return (caddr_t)-1; + } + + heap = new_heap; + return (caddr_t) prev_heap; + } +#endif + +/****************************************************************************** + * Local Functions + *****************************************************************************/ + +static void pinConfig(void) +{ + LPC_IOCON->P3_0 |= 1; /* D0 @ P3.0 */ + LPC_IOCON->P3_1 |= 1; /* D1 @ P3.1 */ + LPC_IOCON->P3_2 |= 1; /* D2 @ P3.2 */ + LPC_IOCON->P3_3 |= 1; /* D3 @ P3.3 */ + + LPC_IOCON->P3_4 |= 1; /* D4 @ P3.4 */ + LPC_IOCON->P3_5 |= 1; /* D5 @ P3.5 */ + LPC_IOCON->P3_6 |= 1; /* D6 @ P3.6 */ + LPC_IOCON->P3_7 |= 1; /* D7 @ P3.7 */ + + LPC_IOCON->P3_8 |= 1; /* D8 @ P3.8 */ + LPC_IOCON->P3_9 |= 1; /* D9 @ P3.9 */ + LPC_IOCON->P3_10 |= 1; /* D10 @ P3.10 */ + LPC_IOCON->P3_11 |= 1; /* D11 @ P3.11 */ + + LPC_IOCON->P3_12 |= 1; /* D12 @ P3.12 */ + LPC_IOCON->P3_13 |= 1; /* D13 @ P3.13 */ + LPC_IOCON->P3_14 |= 1; /* D14 @ P3.14 */ + LPC_IOCON->P3_15 |= 1; /* D15 @ P3.15 */ + + LPC_IOCON->P3_16 |= 1; /* D16 @ P3.16 */ + LPC_IOCON->P3_17 |= 1; /* D17 @ P3.17 */ + LPC_IOCON->P3_18 |= 1; /* D18 @ P3.18 */ + LPC_IOCON->P3_19 |= 1; /* D19 @ P3.19 */ + + LPC_IOCON->P3_20 |= 1; /* D20 @ P3.20 */ + LPC_IOCON->P3_21 |= 1; /* D21 @ P3.21 */ + LPC_IOCON->P3_22 |= 1; /* D22 @ P3.22 */ + LPC_IOCON->P3_23 |= 1; /* D23 @ P3.23 */ + + LPC_IOCON->P3_24 |= 1; /* D24 @ P3.24 */ + LPC_IOCON->P3_25 |= 1; /* D25 @ P3.25 */ + LPC_IOCON->P3_26 |= 1; /* D26 @ P3.26 */ + LPC_IOCON->P3_27 |= 1; /* D27 @ P3.27 */ + + LPC_IOCON->P3_28 |= 1; /* D28 @ P3.28 */ + LPC_IOCON->P3_29 |= 1; /* D29 @ P3.29 */ + LPC_IOCON->P3_30 |= 1; /* D30 @ P3.30 */ + LPC_IOCON->P3_31 |= 1; /* D31 @ P3.31 */ + + LPC_IOCON->P4_0 |= 1; /* A0 @ P4.0 */ + LPC_IOCON->P4_1 |= 1; /* A1 @ P4.1 */ + LPC_IOCON->P4_2 |= 1; /* A2 @ P4.2 */ + LPC_IOCON->P4_3 |= 1; /* A3 @ P4.3 */ + + LPC_IOCON->P4_4 |= 1; /* A4 @ P4.4 */ + LPC_IOCON->P4_5 |= 1; /* A5 @ P4.5 */ + LPC_IOCON->P4_6 |= 1; /* A6 @ P4.6 */ + LPC_IOCON->P4_7 |= 1; /* A7 @ P4.7 */ + + LPC_IOCON->P4_8 |= 1; /* A8 @ P4.8 */ + LPC_IOCON->P4_9 |= 1; /* A9 @ P4.9 */ + LPC_IOCON->P4_10 |= 1; /* A10 @ P4.10 */ + LPC_IOCON->P4_11 |= 1; /* A11 @ P4.11 */ + + LPC_IOCON->P4_12 |= 1; /* A12 @ P4.12 */ + LPC_IOCON->P4_13 |= 1; /* A13 @ P4.13 */ + LPC_IOCON->P4_14 |= 1; /* A14 @ P4.14 */ +#if 0 // not used for SDRAM + LPC_IOCON->P4_15 |= 1; /* A15 @ P4.15 */ + + LPC_IOCON->P4_16 |= 1; /* A16 @ P4.16 */ + LPC_IOCON->P4_17 |= 1; /* A17 @ P4.17 */ + LPC_IOCON->P4_18 |= 1; /* A18 @ P4.18 */ + LPC_IOCON->P4_19 |= 1; /* A19 @ P4.19 */ + + LPC_IOCON->P4_20 |= 1; /* A20 @ P4.20 */ + LPC_IOCON->P4_21 |= 1; /* A21 @ P4.21 */ + LPC_IOCON->P4_22 |= 1; /* A22 @ P4.22 */ + LPC_IOCON->P4_23 |= 1; /* A23 @ P4.23 */ +#endif + + LPC_IOCON->P4_24 |= 1; /* OEN @ P4.24 */ + LPC_IOCON->P4_25 |= 1; /* WEN @ P4.25 */ +#if 0 // not used for SDRAM + LPC_IOCON->P4_26 |= 1; /* BLSN[0] @ P4.26 */ + LPC_IOCON->P4_27 |= 1; /* BLSN[1] @ P4.27 */ + + + LPC_IOCON->P4_28 |= 1; /* BLSN[2] @ P4.28 */ + LPC_IOCON->P4_29 |= 1; /* BLSN[3] @ P4.29 */ + LPC_IOCON->P4_30 |= 1; /* CSN[0] @ P4.30 */ + LPC_IOCON->P4_31 |= 1; /* CSN[1] @ P4.31 */ +#endif + + LPC_IOCON->P2_14 |= 1; /* CSN[2] @ P2.14 */ + LPC_IOCON->P2_15 |= 1; /* CSN[3] @ P2.15 */ + + LPC_IOCON->P2_16 |= 1; /* CASN @ P2.16 */ + LPC_IOCON->P2_17 |= 1; /* RASN @ P2.17 */ + LPC_IOCON->P2_18 |= 1; /* CLK[0] @ P2.18 */ +#if 0 // not used for SDRAM + LPC_IOCON->P2_19 |= 1; /* CLK[1] @ P2.19 */ +#endif + + LPC_IOCON->P2_20 |= 1; /* DYCSN[0] @ P2.20 */ +#if 0 // not used for SDRAM + LPC_IOCON->P2_21 |= 1; /* DYCSN[1] @ P2.21 */ + LPC_IOCON->P2_22 |= 1; /* DYCSN[2] @ P2.22 */ + LPC_IOCON->P2_23 |= 1; /* DYCSN[3] @ P2.23 */ +#endif + + LPC_IOCON->P2_24 |= 1; /* CKE[0] @ P2.24 */ +#if 0 // not used for SDRAM + LPC_IOCON->P2_25 |= 1; /* CKE[1] @ P2.25 */ + LPC_IOCON->P2_26 |= 1; /* CKE[2] @ P2.26 */ + LPC_IOCON->P2_27 |= 1; /* CKE[3] @ P2.27 */ +#endif + + LPC_IOCON->P2_28 |= 1; /* DQM[0] @ P2.28 */ + LPC_IOCON->P2_29 |= 1; /* DQM[1] @ P2.29 */ + LPC_IOCON->P2_30 |= 1; /* DQM[2] @ P2.30 */ + LPC_IOCON->P2_31 |= 1; /* DQM[3] @ P2.31 */ +} + + +static uint32_t sdram_test( void ) +{ + volatile uint32_t *wr_ptr; + volatile uint16_t *short_wr_ptr; + uint32_t data; + uint32_t i, j; + + wr_ptr = (uint32_t *)SDRAM_BASE; + short_wr_ptr = (uint16_t *)wr_ptr; + /* Clear content before 16 bit access test */ +// for (i = 0; i < SDRAM_SIZE/4; i++) +// { +// *wr_ptr++ = 0; +// } + + /* 16 bit write */ + for (i = 0; i < SDRAM_SIZE/0x40000; i++) + { + for (j = 0; j < 0x100; j++) + { + *short_wr_ptr++ = (i + j); + *short_wr_ptr++ = (i + j) + 1; + } + } + + /* Verifying */ + wr_ptr = (uint32_t *)SDRAM_BASE; + for (i = 0; i < SDRAM_SIZE/0x40000; i++) + { + for (j = 0; j < 0x100; j++) + { + data = *wr_ptr; + if (data != (((((i + j) + 1) & 0xFFFF) << 16) | ((i + j) & 0xFFFF))) + { + return 0x0; + } + wr_ptr++; + } + } + return 0x1; +} + +static uint32_t find_cmddly(void) +{ + uint32_t cmddly, cmddlystart, cmddlyend, dwtemp; + uint32_t ppass = 0x0, pass = 0x0; + + cmddly = 0x0; + cmddlystart = cmddlyend = 0xFF; + + while (cmddly < 32) + { + dwtemp = LPC_SC->EMCDLYCTL & ~0x1F; + LPC_SC->EMCDLYCTL = dwtemp | cmddly; + + if (sdram_test() == 0x1) + { + /* Test passed */ + if (cmddlystart == 0xFF) + { + cmddlystart = cmddly; + } + ppass = 0x1; + } + else + { + /* Test failed */ + if (ppass == 1) + { + cmddlyend = cmddly; + pass = 0x1; + ppass = 0x0; + } + } + + /* Try next value */ + cmddly++; + } + + /* If the test passed, the we can use the average of the min and max values to get an optimal DQSIN delay */ + if (pass == 0x1) + { + cmddly = (cmddlystart + cmddlyend) / 2; + } + else if (ppass == 0x1) + { + cmddly = (cmddlystart + 0x1F) / 2; + } + else + { + /* A working value couldn't be found, just pick something safe so the system doesn't become unstable */ + cmddly = 0x10; + } + + dwtemp = LPC_SC->EMCDLYCTL & ~0x1F; + LPC_SC->EMCDLYCTL = dwtemp | cmddly; + + return (pass | ppass); +} + +static uint32_t find_fbclkdly(void) +{ + uint32_t fbclkdly, fbclkdlystart, fbclkdlyend, dwtemp; + uint32_t ppass = 0x0, pass = 0x0; + + fbclkdly = 0x0; + fbclkdlystart = fbclkdlyend = 0xFF; + + while (fbclkdly < 32) + { + dwtemp = LPC_SC->EMCDLYCTL & ~0x1F00; + LPC_SC->EMCDLYCTL = dwtemp | (fbclkdly << 8); + + if (sdram_test() == 0x1) + { + /* Test passed */ + if (fbclkdlystart == 0xFF) + { + fbclkdlystart = fbclkdly; + } + ppass = 0x1; + } + else + { + /* Test failed */ + if (ppass == 1) + { + fbclkdlyend = fbclkdly; + pass = 0x1; + ppass = 0x0; + } + } + + /* Try next value */ + fbclkdly++; + } + + /* If the test passed, the we can use the average of the min and max values to get an optimal DQSIN delay */ + if (pass == 0x1) + { + fbclkdly = (fbclkdlystart + fbclkdlyend) / 2; + } + else if (ppass == 0x1) + { + fbclkdly = (fbclkdlystart + 0x1F) / 2; + } + else + { + /* A working value couldn't be found, just pick something safe so the system doesn't become unstable */ + fbclkdly = 0x10; + } + + dwtemp = LPC_SC->EMCDLYCTL & ~0x1F00; + LPC_SC->EMCDLYCTL = dwtemp | (fbclkdly << 8); + + return (pass | ppass); +} + +static uint32_t calibration( void ) +{ + uint32_t dwtemp, i; + uint32_t cnt = 0; + + for (i = 0; i < 10; i++) + { + dwtemp = LPC_SC->EMCCAL & ~0x4000; + LPC_SC->EMCCAL = dwtemp | 0x4000; + + dwtemp = LPC_SC->EMCCAL; + while ((dwtemp & 0x8000) == 0x0000) + { + dwtemp = LPC_SC->EMCCAL; + } + cnt += (dwtemp & 0xFF); + } + return (cnt / 10); +} + +/****************************************************************************** + * Public Functions + *****************************************************************************/ + + +void adjust_timing( void ) +{ + uint32_t dwtemp, cmddly, fbclkdly; + + /* Current value */ + ringosccount[1] = calibration(); + + dwtemp = LPC_SC->EMCDLYCTL; + cmddly = ((dwtemp & 0x1F) * ringosccount[0] / ringosccount[1]) & 0x1F; + fbclkdly = ((dwtemp & 0x1F00) * ringosccount[0] / ringosccount[1]) & 0x1F00; + LPC_SC->EMCDLYCTL = (dwtemp & ~0x1F1F) | fbclkdly | cmddly; +} + +/****************************************************************************** + * + * Description: + * Initialize the SDRAM + * + *****************************************************************************/ +uint32_t sdram_init (void) +{ + uint32_t i; + uint32_t dwtemp = 0; + //uint16_t wtemp = 0; + + if (initialized) { + return 0; + } + + LPC_SC->PCONP |= 0x00000800; + LPC_SC->EMCDLYCTL = 0x00001010; + LPC_EMC->Control = 0x00000001; + LPC_EMC->Config = 0x00000000; + + pinConfig(); //Full 32-bit Data bus, 24-bit Address + + /* Configure memory layout, but MUST DISABLE BUFFERs during configuration */ + /* 256MB, 8Mx32, 4 banks, row=12, column=9 */ + LPC_EMC->DynamicConfig0 = 0x00004480; + + /*Configure timing for ISSI IS4x32800D SDRAM*/ + +#if (SDRAM_SPEED==SDRAM_SPEED_48) +//Timing for 48MHz Bus + LPC_EMC->DynamicRasCas0 = 0x00000201; /* 1 RAS, 2 CAS latency */ + LPC_EMC->DynamicReadConfig = 0x00000001; /* Command delayed strategy, using EMCCLKDELAY */ + LPC_EMC->DynamicRP = 0x00000000; /* ( n + 1 ) -> 1 clock cycles */ + LPC_EMC->DynamicRAS = 0x00000002; /* ( n + 1 ) -> 3 clock cycles */ + LPC_EMC->DynamicSREX = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicAPR = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicDAL = 0x00000002; /* ( n ) -> 2 clock cycles */ + LPC_EMC->DynamicWR = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRC = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicRFC = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicXSR = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicRRD = 0x00000000; /* ( n + 1 ) -> 1 clock cycles */ + LPC_EMC->DynamicMRD = 0x00000000; /* ( n + 1 ) -> 1 clock cycles */ +#elif (SDRAM_SPEED==SDRAM_SPEED_50) +//Timing for 50MHz Bus (with 100MHz M3 Core) + LPC_EMC->DynamicRasCas0 = 0x00000201; /* 1 RAS, 2 CAS latency */ + LPC_EMC->DynamicReadConfig = 0x00000001; /* Command delayed strategy, using EMCCLKDELAY */ + LPC_EMC->DynamicRP = 0x00000000; /* ( n + 1 ) -> 1 clock cycles */ + LPC_EMC->DynamicRAS = 0x00000002; /* ( n + 1 ) -> 3 clock cycles */ + LPC_EMC->DynamicSREX = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicAPR = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicDAL = 0x00000002; /* ( n ) -> 2 clock cycles */ + LPC_EMC->DynamicWR = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRC = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicRFC = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicXSR = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicRRD = 0x00000000; /* ( n + 1 ) -> 1 clock cycles */ + LPC_EMC->DynamicMRD = 0x00000000; /* ( n + 1 ) -> 1 clock cycles */ +#elif (SDRAM_SPEED==SDRAM_SPEED_60) + //Timing for 60 MHz Bus (same as 72MHz) + LPC_EMC->DynamicRasCas0 = 0x00000202; /* 2 RAS, 2 CAS latency */ + LPC_EMC->DynamicReadConfig = 0x00000001; /* Command delayed strategy, using EMCCLKDELAY */ + LPC_EMC->DynamicRP = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRAS = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicSREX = 0x00000005; /* ( n + 1 ) -> 6 clock cycles */ + LPC_EMC->DynamicAPR = 0x00000002; /* ( n + 1 ) -> 3 clock cycles */ + LPC_EMC->DynamicDAL = 0x00000003; /* ( n ) -> 3 clock cycles */ + LPC_EMC->DynamicWR = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRC = 0x00000004; /* ( n + 1 ) -> 5 clock cycles */ + LPC_EMC->DynamicRFC = 0x00000004; /* ( n + 1 ) -> 5 clock cycles */ + LPC_EMC->DynamicXSR = 0x00000005; /* ( n + 1 ) -> 6 clock cycles */ + LPC_EMC->DynamicRRD = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicMRD = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ +#elif (SDRAM_SPEED==SDRAM_SPEED_72) + //Timing for 72 MHz Bus + LPC_EMC->DynamicRasCas0 = 0x00000202; /* 2 RAS, 2 CAS latency */ + LPC_EMC->DynamicReadConfig = 0x00000001; /* Command delayed strategy, using EMCCLKDELAY */ + LPC_EMC->DynamicRP = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRAS = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicSREX = 0x00000005; /* ( n + 1 ) -> 6 clock cycles */ + LPC_EMC->DynamicAPR = 0x00000002; /* ( n + 1 ) -> 3 clock cycles */ + LPC_EMC->DynamicDAL = 0x00000003; /* ( n ) -> 3 clock cycles */ + LPC_EMC->DynamicWR = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRC = 0x00000004; /* ( n + 1 ) -> 5 clock cycles */ + LPC_EMC->DynamicRFC = 0x00000004; /* ( n + 1 ) -> 5 clock cycles */ + LPC_EMC->DynamicXSR = 0x00000005; /* ( n + 1 ) -> 6 clock cycles */ + LPC_EMC->DynamicRRD = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicMRD = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ +#elif (SDRAM_SPEED==SDRAM_SPEED_80) + //Timing for 80 MHz Bus (same as 72MHz) + LPC_EMC->DynamicRasCas0 = 0x00000202; /* 2 RAS, 2 CAS latency */ + LPC_EMC->DynamicReadConfig = 0x00000001; /* Command delayed strategy, using EMCCLKDELAY */ + LPC_EMC->DynamicRP = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRAS = 0x00000003; /* ( n + 1 ) -> 4 clock cycles */ + LPC_EMC->DynamicSREX = 0x00000005; /* ( n + 1 ) -> 6 clock cycles */ + LPC_EMC->DynamicAPR = 0x00000002; /* ( n + 1 ) -> 3 clock cycles */ + LPC_EMC->DynamicDAL = 0x00000003; /* ( n ) -> 3 clock cycles */ + LPC_EMC->DynamicWR = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicRC = 0x00000004; /* ( n + 1 ) -> 5 clock cycles */ + LPC_EMC->DynamicRFC = 0x00000004; /* ( n + 1 ) -> 5 clock cycles */ + LPC_EMC->DynamicXSR = 0x00000005; /* ( n + 1 ) -> 6 clock cycles */ + LPC_EMC->DynamicRRD = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ + LPC_EMC->DynamicMRD = 0x00000001; /* ( n + 1 ) -> 2 clock cycles */ +#else + #error UNSUPPORTED SDRAM FREQ +#endif + + LPC_EMC->DynamicControl = 0x00000183; /* Issue NOP command */ + wait(0.2); /* wait 200ms */ + LPC_EMC->DynamicControl = 0x00000103; /* Issue PALL command */ + LPC_EMC->DynamicRefresh = 0x00000002; /* ( n * 16 ) -> 32 clock cycles */ + for(i = 0; i < 0x80; i++); /* wait 128 AHB clock cycles */ + + +#if (SDRAM_SPEED==SDRAM_SPEED_48) + //Timing for 48MHz Bus + LPC_EMC->DynamicRefresh = 0x0000002E; /* ( n * 16 ) -> 736 clock cycles -> 15.330uS at 48MHz <= 15.625uS ( 64ms / 4096 row ) */ +#elif (SDRAM_SPEED==SDRAM_SPEED_50) + //Timing for 50MHz Bus + LPC_EMC->DynamicRefresh = 0x0000003A; /* ( n * 16 ) -> 768 clock cycles -> 15.360uS at 50MHz <= 15.625uS ( 64ms / 4096 row ) */ +#elif (SDRAM_SPEED==SDRAM_SPEED_60) + //Timing for 60MHz Bus + LPC_EMC->DynamicRefresh = 0x0000003A; /* ( n * 16 ) -> 928 clock cycles -> 15.466uS at 60MHz <= 15.625uS ( 64ms / 4096 row ) */ +#elif (SDRAM_SPEED==SDRAM_SPEED_72) + //Timing for 72MHz Bus + LPC_EMC->DynamicRefresh = 0x00000046; /* ( n * 16 ) -> 1120 clock cycles -> 15.556uS at 72MHz <= 15.625uS ( 64ms / 4096 row ) */ +#elif (SDRAM_SPEED==SDRAM_SPEED_80) + //Timing for 80MHz Bus + LPC_EMC->DynamicRefresh = 0x0000004E; /* ( n * 16 ) -> 1248 clock cycles -> 15.600uS at 80MHz <= 15.625uS ( 64ms / 4096 row ) */ +#else + #error UNSUPPORTED SDRAM FREQ +#endif + + LPC_EMC->DynamicControl = 0x00000083; /* Issue MODE command */ + //Timing for 48/60/72MHZ Bus + dwtemp = *((volatile uint32_t *)(SDRAM_BASE | (0x22<<(2+2+9)))); /* 4 burst, 2 CAS latency */ + dwtemp = dwtemp; + LPC_EMC->DynamicControl = 0x00000000; /* Issue NORMAL command */ +//[re]enable buffers + LPC_EMC->DynamicConfig0 = 0x00084480; /* 256MB, 8Mx32, 4 banks, row=12, column=9 */ + + /* Nominal value */ + ringosccount[0] = calibration(); + + if (find_cmddly() == 0x0) + { + //while (1); /* fatal error */ + return 1;//FALSE; + } + + if (find_fbclkdly() == 0x0) + { + //while (1); /* fatal error */ + return 1;//FALSE; + } + + adjust_timing(); + + initialized = true; + + return 0;//TRUE; +} + +void sdram_disableMallocSdram() +{ + okToUseSdramForHeap = false; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdram.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,68 @@ +/***************************************************************************** + * + * Copyright(C) 2011, Embedded Artists AB + * All rights reserved. + * + ****************************************************************************** + * Software that is described herein is for illustrative purposes only + * which provides customers with programming information regarding the + * products. This software is supplied "AS IS" without any warranties. + * Embedded Artists AB assumes no responsibility or liability for the + * use of the software, conveys no license or title under any patent, + * copyright, or mask work right to the product. Embedded Artists AB + * reserves the right to make changes in the software without + * notification. Embedded Artists AB also make no representation or + * warranty that such application will be suitable for the specified + * use without further testing or modification. + *****************************************************************************/ +#ifndef __SDRAM_H +#define __SDRAM_H + +#include "stdint.h" + +/* + * These timing parameters are based on the EMC clock + * there is no way of ensuring what the EMC clock frequency is + * without severely bloating the code + * ENSURE THAT THE EMC clock is one of these values + */ +#define SDRAM_SPEED_48 0 +#define SDRAM_SPEED_50 1 +#define SDRAM_SPEED_60 2 +#define SDRAM_SPEED_72 3 +#define SDRAM_SPEED_80 4 + +#define SDRAM_SPEED SDRAM_SPEED_60 + +#define SDRAM_CONFIG_32BIT +#define SDRAM_SIZE 0x2000000 + +#define SDRAM_BASE 0xA0000000 /*CS0*/ + +/* Initializes the SDRAM. + * + * The entire SDRAM will be made available to malloc per default. + * + * Note that this functions is called internally if malloc requests + * memory from SDRAM and that hasn't been disabled with a call to + * sdram_disableMallocSdram(). + * + * @returns 0 on success, 1 on failure + */ +uint32_t sdram_init(); + +/* Prevents malloc from using SDRAM. + * + * This function must be called before the first allocation that + * would have been in SDRAM. If a big allocation has already been + * made then this call will do nothing as the SDRAM will have been + * initialized and all SDRAM given to malloc. + */ +void sdram_disableMallocSdram(); + +#endif /* end __SDRAM_H */ +/**************************************************************************** +** End Of File +*****************************************************************************/ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spifi_rom_api.h Thu Sep 26 06:37:02 2013 +0000 @@ -0,0 +1,165 @@ +/* definitions for ROM API for SPIFI in NXP MCUs + copyright (c) 2010 NXP Semiconductors + written by CAM start 4/16/10 + first testing 5/12/10 + OK with first SST & Winbond devices 6/8/10 + OK with Gigadevice, Numonyx, Atmel, + some Macronyx 7/13/10 + consensus with BK, performance optimized 8/24/10 + this file is largely platform-independent */ + +#ifndef SPIFI_ROM_API_H +#define SPIFI_ROM_API_H + + +#define SPIFI_MEM_BASE 0x28000000 +/* allocated size of the SPIFI memory area on this device */ +#define MEM_AREA_SIZE 0x00001000 +#define SPIFI_ROM_PTR 0x1FFF1FF8 + +/* define the symbol TESTING in the environment if test output desired */ + +/* maintain LONGEST_PROT >= the length (in bytes) of the largest + protection block of any serial flash that this driver handles */ +#define LONGEST_PROT 68 + +/* protection/sector descriptors */ +typedef struct { + unsigned base; + uint8_t flags; + signed char log2; + uint16_t rept; +} protEnt; + +typedef union { + uint16_t hw; + uint8_t byte[2]; +}stat_t; + +/* the object that init returns, and other routines use as an operand */ +typedef struct { + unsigned base, regbase, devSize, memSize; + uint8_t mfger, devType, devID, busy; + stat_t stat; + uint16_t reserved; + uint16_t set_prot, write_prot; + unsigned mem_cmd, prog_cmd; + uint16_t sectors, protBytes; + unsigned opts, errCheck; + uint8_t erase_shifts[4], erase_ops[4]; + protEnt *protEnts; + char prot[LONGEST_PROT]; +} SPIFIobj; + +/* operands of program and erase */ +typedef struct { + char *dest; /* starting address for programming or erasing */ + unsigned length; /* number of bytes to be programmed or erased */ + char *scratch; /* address of work area or NULL */ + int protect; /* protection to apply after programming/erasing is done */ + unsigned options; /* see the table below */ +} SPIFIopers; + + +/* bits in options operands (MODE3, RCVCLK, and FULLCLK + have the same relationship as in the Control register) */ +#define S_MODE3 1 +#define S_MODE0 0 +#define S_MINIMAL 2 +#define S_MAXIMAL 0 +#define S_FORCE_ERASE 4 +#define S_ERASE_NOT_REQD 8 +#define S_CALLER_ERASE 8 +#define S_ERASE_AS_REQD 0 +#define S_VERIFY_PROG 0x10 +#define S_VERIFY_ERASE 0x20 +#define S_NO_VERIFY 0 +#define S_RCVCLK 0x80 +#define S_INTCLK 0 +#define S_FULLCLK 0x40 +#define S_HALFCLK 0 +#define S_DUAL 0x100 +#define S_CALLER_PROT 0x200 +#define S_DRIVER_PROT 0 + +/* the length of a standard program command is 256 on all devices */ +#define PROG_SIZE 256 + +/* interface to ROM API */ +typedef struct { + int (*spifi_init) (SPIFIobj *obj, unsigned csHigh, unsigned options, + unsigned mhz); + int (*spifi_program) (SPIFIobj *obj, char *source, SPIFIopers *opers); + int (*spifi_erase) (SPIFIobj *obj, SPIFIopers *opers); + /* mode switching */ + void (*cancel_mem_mode)(SPIFIobj *obj); + void (*set_mem_mode) (SPIFIobj *obj); + + /* mid level functions */ + int (*checkAd) (SPIFIobj *obj, SPIFIopers *opers); + int (*setProt) (SPIFIobj *obj, SPIFIopers *opers, char *change, + char *saveProt); + int (*check_block) (SPIFIobj *obj, char *source, SPIFIopers *opers, + unsigned check_program); + int (*send_erase_cmd) (SPIFIobj *obj, unsigned char op, unsigned addr); + unsigned (*ck_erase) (SPIFIobj *obj, unsigned *addr, unsigned length); + int (*prog_block) (SPIFIobj *obj, char *source, SPIFIopers *opers, + unsigned *left_in_page); + unsigned (*ck_prog) (SPIFIobj *obj, char *source, char *dest, unsigned length); + + /* low level functions */ + void(*setSize) (SPIFIobj *obj, int value); + int (*setDev) (SPIFIobj *obj, unsigned opts, unsigned mem_cmd, + unsigned prog_cmd); + unsigned (*cmd) (uint8_t op, uint8_t addrLen, uint8_t intLen, unsigned short len); + unsigned (*readAd) (SPIFIobj *obj, unsigned cmd, unsigned addr); + void (*send04) (SPIFIobj *obj, uint8_t op, uint8_t len, unsigned value); + void (*wren_sendAd) (SPIFIobj *obj, unsigned cmd, unsigned addr, unsigned value); + int (*write_stat) (SPIFIobj *obj, uint8_t len, uint16_t value); + int (*wait_busy) (SPIFIobj *obj, uint8_t prog_or_erase); +} SPIFI_RTNS; + +//#define define_spifi_romPtr(name) const SPIFI_RTNS *name=*((SPIFI_RTNS **)SPIFI_ROM_PTR) + +/* example of using this interface: +#include "spifi_rom_api.h" +#define CSHIGH 4 +#define SPIFI_MHZ 80 +#define source_data_ad (char *)1234 + + int rc; + SPIFIopers opers; + + define_spifi_romPtr(spifi); + SPIFIobj *obj = malloc(sizeof(SPIFIobj)); + if (!obj) { can't allocate memory } + + rc = spifi->spifi_init (obj, CSHIGH, S_FULLCLK+S_RCVCLK, SPIFI_MHZ); + if (rc) { investigate init error rc } + printf ("the serial flash contains %d bytes\n", obj->devSize); + + opers.dest = where_to_program; + opers.length = how_many_bytes; + opers.scratch = NULL; // unprogrammed data is not saved/restored + opers.protect = -1; // save & restore protection + opers.options = S_VERIFY_PROG; + + rc = spifi->spifi_program (obj, source_data_ad, &opers); + if (rc) { investigate program error rc } +*/ + +/* these are for normal users, including boot code */ +int spifi_init (SPIFIobj *obj, unsigned csHigh, unsigned options, unsigned mhz); +int spifi_program (SPIFIobj *obj, char *source, SPIFIopers *opers); +int spifi_erase (SPIFIobj *obj, SPIFIopers *opers); + +/* these are used by the manufacturer-specific init functions */ +void setSize (SPIFIobj *obj, int value); +int setDev (SPIFIobj *obj, unsigned opts, unsigned mem_cmd, unsigned prog_cmd); +unsigned read04(SPIFIobj *obj, uint8_t op, uint8_t len); +int write_stat (SPIFIobj *obj, uint8_t len, uint16_t value); +void setProtEnts(SPIFIobj *obj, const protEnt *p, unsigned protTabLen); + +#endif + +