Wiblocks --- NB2AS microSD

NB2AS MicroSD Card Demo Program

The microSD card on the NB2AS is connected to the SPI port. Six I/O lines are required. Three SPI lines (MOSI, MISO, SCK), a chip-select line (CS) a card-detect line (CD) and a power-enable (USD_PWR).

This microSD demo sketch demonstrates the intialization of the microSD card, readback of the card identifcation (CID) register and read/write of a block. After the initialization is complete the RTC will generate an interrupt every time the seconds count is equal to twenty. Each time the interrupt occurs the time will be written to the microSD card. The time is written in a BCD format for ease of debugging.

The screenshot on the right shows the output from the intialization sequence. The CID is output after the initialization, a write command is executed followed by a read command. The data that is read is output.

The screenshot on the bottom right shows the output of a dlog command. The time is datalogged in a BCD format and then the block is readback and output. All unused bytes are filled with zeros.


































NB2AS microSD Demo Sketch

extern "C" 
{
#include <NB2AS.h>
#include <wb_uSD.h>
}

#include <TWI.h>
#include <RTC.h>

RTC rtc;

// block buffer for the uSD reads and writes

uint8_t buf[512];
uint8_t rtc_alarm_flag;

//
// predeclaration of functions used for printing
// the uSD structure
//
void print_str(char *s, uint8_t n);
void usd_print_cid();
void print_buf(void);

 
void setup() { 
  Serial.begin(19200);
  //
  // initialize the NB2AS
  //
  nb2as_init();
  //
  // setup the uSD card
  // Should be moved to nb2as_init()
  //
  if (usd_cd_p()) usd_card_inserted();
  else usd_card_reset();
  usd_cd_pcint_enable();
  usd_cd_int_clear();
  usd_cd_int_enable();

  //
  // setup the rtc
  //
  rtc_inta_init();

  // initialize the date
  //rtc.set_date(2010, 6, 2);

  // intialize the clock
  //rtc.set_time(19, 9, 0);


  // Initialize the control register for a 32768Hz square wave
  // and disable the square wave output

  // RTC_RS2 | RTC_RS1 ..... Rate Select, square wave output = 32768Hz
  // RTC_INTCN ............. Interrupt Control (=1 interrupts activated,
  //                         no square wave on INTB

  rtc.write_reg(RTC_REG_CONTROL, RTC_RS2 | RTC_RS1 | RTC_INTCN);

  // Setup ALARM1 to alarm when the seconds count equals 20
  // and then enable the ALARM1

  // RTC_ALARM1_MODE2 ..... Alarm when seconds match
  // dow .................. 0
  // hours ................ 0
  // minutes .............. 0
  // seconds .............. 20
  
  rtc.set_alarm1(RTC_ALARM1_MODE2, 0, 0, 0, 20);
  rtc.enable_alarm1();

  // Initialize the pin change interrupt mask for the
  //  pin that is connected to the RTC /INTA output. 
  
  rtc_inta_mask_enable();
  
  // Enable the pin change interrupts
  
  rtc_int_enable();

  sei();
}


uint8_t * copy_to_buf(uint16_t v, uint8_t num_digits, uint8_t *buf) {
  uint16_t d = 1;
  for (uint8_t i = 0; i < num_digits - 1; i++) d *= 10;
  uint8_t *p;
  p = buf;
  uint8_t bcd_byte  = 0;
  uint8_t bcd_shift = 4;
  while(1) {
    uint8_t bcd_nibble = (uint8_t)(v / d);
    bcd_byte |= (bcd_nibble << bcd_shift);
    if (bcd_shift) {
      bcd_shift = 0;
      v -= bcd_nibble * d;
    } else {
      v -= bcd_nibble * d;
      *p++ = bcd_byte;
      bcd_byte = 0;
      bcd_shift = 4;
    }
    if (d == 1) break; 
    d /= 10;
  }
  return p;
}    

void fill_buf(void) {
  uint16_t i;
  for (i = 0; i < 512; i++) {
    buf[i] = (uint8_t)(i & 0xFF);
  }
  buf[508] = 0xBA;
  buf[509] = 0xBE;
  buf[510] = 0xFA;
  buf[511] = 0xCE;
}

void clear_buf(void) {
  uint16_t i;
  for (i = 0; i < 512; i++) buf[i] = 0;
  buf[508] = 0xBA;
  buf[509] = 0xBE;
  buf[510] = 0xFA;
  buf[511] = 0xCE;
}

void print_buf(void) {
  uint16_t i;
  for (i = 0; i < 512; i++) {
    if (buf[i] < 0x10) Serial.print("0");
    Serial.print(buf[i], HEX);
    if ((i + 1) % 16 == 0) Serial.println("");
    else Serial.print(" ");
  }
}

uint8_t write_and_read_buf(uint32_t addr) {
  uint8_t err;
  err = usd_write_block(0x00000000, buf);
  Serial.print("(write_block) err = "); Serial.println(err,DEC); 
  if (err) debug_led_blink(0);
  clear_buf();
  err = usd_read_block(0x00000000, buf);
  Serial.print("(read_block)  err = "); Serial.println(err,DEC); 
  if (err) debug_led_blink(0);
  return 0;
}

void loop() {
  char timestr[22];

  delay_ms(1000);

  while(1) {
    if (!usd_card_uinit_p()) { 
      debug_led_off();
    } else {
      uint8_t err;
      uint16_t i;
      debug_led_on();
      //
      // initialize and test the uSD card
      //
      { 
	i = 0;
	while(1) {
	  err = usd_init();
	  if (err == 0) break;
	  if (++i > 100) break;
	  delay(10);
	}
	if (err) { 
	  Serial.print("(usd_init) err = ");
	  Serial.println(err, DEC);
	  debug_led_blink(0); // blink forever
	} else {
	  Serial.println("(usd_init) success!");
	}
      }
      err = usd_get_cid();
      if (err) { 
	Serial.print("(usd_get_cid) err = ");
	Serial.println(err, DEC);
	debug_led_blink(0); // blink forever
      }
      usd_print_cid();
      fill_buf();
      write_and_read_buf(0x00000000);
      print_buf();
      break;
    }
  }

  while(1) {

    // If there was an alarm then print the alarm
    // message, clear the alarms and re-enable the
    // interrupts.

    if (rtc_alarm_flag) {
      rtc_int_disable();
      rtc_alarm_flag = 0;
      rtc.clear_alarm1();
      Serial.print("(dlog time)\n");
      rtc.read_regs();
      uint8_t *p;
      clear_buf();
      p = buf;
      p = copy_to_buf(rtc.get_year(),  4, p);
      p = copy_to_buf(rtc.get_month(), 2, p);
      p = copy_to_buf(rtc.get_day(),   2, p);
      p = copy_to_buf(rtc.get_hours(), 2, p);
      p = copy_to_buf(rtc.get_mins(),  2, p);
      p = copy_to_buf(rtc.get_secs(),  2, p);
      write_and_read_buf(0x00000000);
      print_buf();
      rtc_inta_mask_enable();
      rtc_int_enable();
    }
    rtc.read_regs();
    rtc.localtime(timestr);
    Serial.print(timestr);
    Serial.print("\n");
    debug_led_on(); 
    delay(5000);
  }

}

//
// When the state of the uSD CD bit changes this interrupt
// routine is called and the state of the card is changed
// by calling usd_card_inserted() or usd_card_removed().
// The main application needs to do the "appropriate"
// thing. 
//
// Be careful if the "appropriate" thing is shutting off 
// the USD power. The MOSI, SCLK and CS should be high impedance
// when the power is off.
//
ISR( PCINT1_vect ) {
  if (usd_cd_p()) usd_card_inserted();
  else usd_card_removed();
}


// Both RTC alarms call this routine.  When this 
// routine is called the alarm flag is set and 
// the pin change mask is cleared. 

// The interrupt flags are read and reset in the main loop.

ISR( PCINT3_vect )
{
  if (rtc_inta_p()) {
    rtc_alarm_flag |= _BV(RTC_INTA_BIT);
    rtc_inta_clear();
  }
}



//
// functions for displaying
// the usd_card struct
//
void print_str(char *s, uint8_t n) {
  char *p;
  p = s;
  while(1) {
    Serial.print(*p++);
    if ((p-s) == n) break;
  }
  Serial.print('\n');
}

void usd_print_cid() {
  Serial.print("card type....."); Serial.println(usd_card.card_type, DEC);
  Serial.print("mfg_id........");  Serial.println(usd_card.mfg_id, HEX);
  Serial.print("oem_id........");  print_str(usd_card.oem_id,2);
  Serial.print("prod_name.....");  print_str(usd_card.prod_name,5);
  Serial.print("prod_rev......");  Serial.println(usd_card.prod_rev, DEC);
  Serial.print("prod_sn.......");  Serial.println(usd_card.prod_sn,  HEX);
  Serial.print("mfg_year......");  Serial.println(usd_card.mfg_year);
  Serial.print("mfg_month.....");  Serial.println(usd_card.mfg_month, DEC);
  Serial.print("blocklen......");  Serial.println(usd_card.block_length, DEC);  
  Serial.println("OCR");
  Serial.print("  hc..........");  Serial.println(usd_card.hc_p, DEC);
  Serial.print("  vdd range...0x");  Serial.println(usd_card.vdd_range, HEX); 
}