Hi. I need help with my script.
Whole point is to lock/unlock SD card with specific HEX password via CMD42 command, and my script working ok but when I need to change password i always need to convert HEX to DEC and re-program my arduino every time.
Serial commands are:
l - lock
u - unlock
Can someone write script for me to just send command over serial with HEX password directly, problem is that it need to be converted from hex to dec using explode or etc... It is too complicated for me.
For example
HEX 11111111111111111111111111111111 = DEC 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
HEX 22222222222222222222222222222222 = DEC 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34
...
And i just want to be able to send command over serial for example:
l 12312312312312312312312312312312
and lock my sd card with HEX 12312312312312312312312312312312 (DEC 18, 49, 35, 18, 49, 35, 18, 49, 35, 18, 49, 35, 18, 49, 35, 18)
Here is code, I would appreciate it if someone could do it, thanks in advance.
#define SD_GO_IDLE (0x40 + 0) /* CMD0 - go to idle state */
#define SD_INIT (0x40 + 1) /* CMD1 - start initialization */
#define SD_SEND_IF_COND (0x40 + 8) /* CMD8 - send interface (conditional), works for SDHC only */
#define SD_SEND_CSD (0x40 + 9) /* CMD9 - send CSD block (16 bytes) */
#define SD_SEND_CID (0x40 + 10) /* CMD10 - send CID block (16 bytes) */
#define SD_SEND_STATUS (0x40 + 13) /* CMD13 - send card status */
#define SD_SET_BLK_LEN (0x40 + 16) /* CMD16 - set length of block in bytes */
#define SD_READ_BLK (0x40 + 17) /* read single block */
#define SD_LOCK_UNLOCK (0x40 + 42) /* CMD42 - lock/unlock card */
#define CMD55 (0x40 + 55) /* multi-byte preface command */
#define SD_READ_OCR (0x40 + 58) /* read OCR */
#define SD_ADV_INIT (0xc0 + 41) /* ACMD41, for SDHC cards - advanced start initialization */
#define SD_PROGRAM_CSD (0x40 + 27) /* CMD27 - get CSD block (15 bytes data + CRC) */
/*
Define error tokens that can be returned following a data read/write
request.
*/
#define ERRTKN_CARD_LOCKED (1 << 4)
#define ERRTKN_OUT_OF_RANGE (1 << 3)
#define ERRTKN_CARD_ECC (1 << 2)
#define ERRTKN_CARD_CC (1 << 1)
#define SDCARD_OK 0 /* success */
#define SDCARD_NO_DETECT 1 /* unable to detect SD card */
#define SDCARD_TIMEOUT 2 /* last operation timed out */
#define SDCARD_RWFAIL -1 /* read/write command failed */
/*
Define options for accessing the SD card's PWD (CMD42)
*/
#define MASK_ERASE 0x08 /* erase the entire card */
#define MASK_LOCK_UNLOCK 0x04 /* lock or unlock the card with password */
#define MASK_CLR_PWD 0x02 /* clear password */
#define MASK_SET_PWD 0x01 /* set password */
#define SDTYPE_UNKNOWN 0 /* card type not determined */
#define SDTYPE_SD 1 /* SD v1 (1 MB to 2 GB) */
#define SDTYPE_SDHC 2 /* SDHC (4 GB to 32 GB) */
/*
Define the port and DDR used by the SPI.
*/
#define SPI_PORT PORTB
#define SPI_DDR DDRB
/*
Define bits used by the SPI port.
*/
#define MOSI_BIT 3
#define MISO_BIT 4
#define SCK_BIT 5
/*
Define the port, DDR, and bit used as chip-select for the
SD card.
*/
#define SD_CS_PORT PORTB
#define SD_CS_DDR DDRB
#define SD_CS_BIT 2
#define SD_CS_MASK (1 << SD_CS_BIT)
/*
Define LED patterns.
*/
#define PATTERN_NO_DETECT 0xc800c800
#define PATTERN_CANNOT_CHG 0xa5000000
/*
Define the CRC7 polynomial
*/
#define CRC7_POLY 0x89 /* polynomial used for CSD CRCs */
/*
Define bit masks for fields in the lock/unlock command (CMD42) data structure
*/
#define SET_PWD_MASK (1 << 0)
#define CLR_PWD_MASK (1 << 1)
#define LOCK_UNLOCK_MASK (1 << 2)
#define ERASE_MASK (1 << 3)
/*
Local variables
*/
uint8_t sdtype; // flag for SD card type
uint8_t csd[16];
uint8_t cid[16];
uint8_t ocr[4];
uint8_t crctable[256];
uint8_t block[512];
uint8_t cardstatus[2]; // updated by ReadLockStatus
uint8_t pwd[16];
uint8_t pwd_len;
// **************** password here **************************
//const char GlobalPWDStr[4] PROGMEM =
const char PWD_TTLive1105[16] = {
100, 136, 176, 76, 15, 35, 112, 70, 220, 30, 13, 191, 114, 139, 131, 91
};
//#define GLOBAL_PWD_LEN (sizeof(GlobalPWDStr))
#define GLOBAL_PWD_LEN 16
/*
Local functions
*/
static void select(void);
static void deselect(void);
static uint8_t xchg(uint8_t c);
static int8_t SDInit(void);
static int8_t ExamineSD(void);
static int8_t ReadOCR(void);
static int8_t ReadCID(void);
static int8_t ReadCSD(void);
static int8_t WriteCSD(void);
static int8_t ReadBlock(uint32_t blocknum, uint8_t *buffer);
static void ShowBlock(void);
static void ShowErrorCode(int8_t status);
static int8_t ReadCardStatus(void);
static int8_t ShowPassword(void);
static void ShowCardStatus(void);
static void Load_Password(void);
static int8_t ModifyPWD(uint8_t mask);
static int8_t ForceErase(void);
static int8_t sd_send_command(uint8_t command, uint32_t arg);
static int8_t sd_wait_for_data(void);
static void GenerateCRCTable(void);
static uint8_t AddByteToCRC(uint8_t crc, uint8_t b);
static void GenerateCRCTable() {
int i, j;
// generate a table value for all 256 possible byte values
for (i = 0; i < 256; i++) {
crctable[i] = (i & 0x80) ? i ^ CRC7_POLY : i;
for (j = 1; j < 8; j++) {
crctable[i] <<= 1;
if (crctable[i] & 0x80)
crctable[i] ^= CRC7_POLY;
}
}
}
static uint8_t AddByteToCRC(uint8_t crc, uint8_t b) {
return crctable[(crc << 1) ^ b];
}
/*
select select (enable) the SD card
*/
static void select(void) {
SD_CS_PORT = SD_CS_PORT & ~SD_CS_MASK;
}
/*
deselect deselect (disable) the SD card.
*/
static void deselect(void) {
SD_CS_PORT = SD_CS_PORT | SD_CS_MASK;
}
/*
xchg exchange a byte of data with the SD card via host's SPI bus
*/
static unsigned char xchg(unsigned char c) {
SPDR = c;
while ((SPSR & (1 << SPIF)) == 0)
;
return SPDR;
}
static int8_t SDInit(void) {
int i;
int8_t response;
sdtype = SDTYPE_UNKNOWN; // assume this fails
/*
Begin initialization by sending CMD0 and waiting until SD card
responds with In Idle Mode (0x01). If the response is not 0x01
within a reasonable amount of time, there is no SD card on the bus.
*/
deselect(); // always make sure
for (i = 0; i < 10; i++) // send several clocks while card power stabilizes
xchg(0xff);
for (i = 0; i < 0x10; i++) {
response = sd_send_command(SD_GO_IDLE, 0); // send CMD0 - go to idle state
if (response == 1) break;
}
if (response != 1) {
return SDCARD_NO_DETECT;
}
sd_send_command(SD_SET_BLK_LEN, 512); // always set block length (CMD6) to 512 bytes
response = sd_send_command(SD_SEND_IF_COND, 0x1aa); // probe to see if card is SDv2 (SDHC)
if (response == 0x01) // if card is SDHC...
{
for (i = 0; i < 4; i++) // burn the 4-byte response (OCR)
{
xchg(0xff);
}
for (i = 20000; i > 0; i--) {
response = sd_send_command(SD_ADV_INIT, 1UL << 30);
if (response == 0) break;
}
sdtype = SDTYPE_SDHC;
} else {
response = sd_send_command(SD_READ_OCR, 0);
if (response == 0x01) {
for (i = 0; i < 4; i++) // OCR is 4 bytes
{
xchg(0xff); // burn the 4-byte response (OCR)
}
for (i = 20000; i > 0; i--) {
response = sd_send_command(SD_INIT, 0);
if (response == 0) break;
}
sd_send_command(SD_SET_BLK_LEN, 512);
sdtype = SDTYPE_SD;
}
}
xchg(0xff); // send 8 final clocks
/*
At this point, the SD card has completed initialization. The calling routine
can now increase the SPI clock rate for the SD card to the maximum allowed by
the SD card (typically, 20 MHz).
*/
return SDCARD_OK; // if no power routine or turning off the card, call it good
}
static void ShowBlock(void) {
uint32_t i;
uint8_t str[17];
str[16] = 0;
str[0] = 0; // only need for first newline, overwritten as chars are processed
Serial.print("\n\rContents of block buffer:");
for (i = 0; i < 512; i++) {
if ((i % 16) == 0) {
printf_P(PSTR(" %s\n\r%04X: "), str, i);
}
printf_P(PSTR("%02X "), (uint8_t)block[i]);
if (isalpha(block[i]) || isdigit(block[i])) str[i % 16] = block[i];
else str[i % 16] = '.';
}
printf_P(PSTR(" %s\n\r"), str);
}
static int8_t ExamineSD(void) {
int8_t response;
response = ReadOCR(); // this fails with Samsung; don't test response until know why
response = ReadCSD();
if (response == SDCARD_OK) {
// printf_P(PSTR(" ReadCSD is OK "));
response = ReadCID();
}
if (response == SDCARD_OK) {
// printf_P(PSTR(" ReadCID is OK "));
response = ReadCardStatus();
}
return response;
}
static int8_t ReadOCR(void) {
uint8_t i;
int8_t response;
for (i = 0; i < 4; i++) ocr[i] = 0;
if (sdtype == SDTYPE_SDHC) {
response = sd_send_command(SD_SEND_IF_COND, 0x1aa);
if (response != 0) {
return SDCARD_RWFAIL;
}
for (i = 0; i < 4; i++) {
ocr[i] = xchg(0xff);
}
xchg(0xff); // burn the CRC
} else {
response = sd_send_command(SD_READ_OCR, 0);
if (response != 0x00) {
return SDCARD_RWFAIL;
}
for (i = 0; i < 4; i++) // OCR is 4 bytes
{
ocr[i] = xchg(0xff);
}
xchg(0xff);
}
return SDCARD_OK;
}
static int8_t ReadCSD(void) {
uint8_t i;
int8_t response;
for (i = 0; i < 16; i++) csd[i] = 0;
response = sd_send_command(SD_SEND_CSD, 0);
response = sd_wait_for_data();
if (response != (int8_t)0xfe) {
//printf_P(PSTR("\n\rReadCSD(), sd_wait_for_data returns %02x."), response);
Serial.print("\n\rReadCSD(), sd_wait_for_data returns ");
Serial.print(response);
return SDCARD_RWFAIL;
}
for (i = 0; i < 16; i++) {
csd[i] = xchg(0xff);
}
xchg(0xff); // burn the CRC
return SDCARD_OK;
}
static int8_t ReadCID(void) {
uint8_t i;
int8_t response;
for (i = 0; i < 16; i++) cid[i] = 0;
response = sd_send_command(SD_SEND_CID, 0);
response = sd_wait_for_data();
if (response != (int8_t)0xfe) {
return SDCARD_RWFAIL;
}
for (i = 0; i < 16; i++) {
cid[i] = xchg(0xff);
}
xchg(0xff); // burn the CRC
return SDCARD_OK;
}
static int8_t WriteCSD(void) {
int8_t response;
uint8_t tcrc;
uint16_t i;
response = sd_send_command(SD_PROGRAM_CSD, 0);
if (response != 0) {
return SDCARD_RWFAIL;
}
xchg(0xfe); // send data token marking start of data block
tcrc = 0;
for (i = 0; i < 15; i++) // for all 15 data bytes in CSD...
{
xchg(csd[i]); // send each byte via SPI
tcrc = AddByteToCRC(tcrc, csd[i]); // add byte to CRC
}
xchg((tcrc << 1) + 1); // format the CRC7 value and send it
xchg(0xff); // ignore dummy checksum
xchg(0xff); // ignore dummy checksum
i = 0xffff; // max timeout
while (!xchg(0xFF) && (--i))
; // wait until we are not busy
if (i) return SDCARD_OK; // return success
else return SDCARD_RWFAIL; // nope, didn't work
}
static int8_t ReadCardStatus(void) {
cardstatus[0] = sd_send_command(SD_SEND_STATUS, 0);
cardstatus[1] = xchg(0xff);
// printf_P(PSTR("\r\nReadCardStatus = %02x %02x"), cardstatus[0], cardstatus[1]);
xchg(0xff);
return SDCARD_OK;
}
static int8_t ShowPassword(void) {
Serial.print("DEC: ");
for (int i = 0; i < GLOBAL_PWD_LEN; i++) {
Serial.print(pwd[i]);
Serial.print(" ");
}
Serial.print("\r\n");
Serial.print("HEX: ");
for (int i = 0; i < GLOBAL_PWD_LEN; i++) {
if (pwd[i] <= 0x0F) Serial.print("0");
Serial.print(pwd[i], HEX);
}
}
static int8_t ReadBlock(uint32_t blocknum, uint8_t *buffer) {
uint16_t i;
uint8_t status;
uint32_t addr;
/*
Compute byte address of start of desired sector.
For SD cards, the argument to CMD17 must be a byte address.
For SDHC cards, the argument to CMD17 must be a block (512 bytes) number.
*/
if (sdtype == SDTYPE_SD) addr = blocknum << 9; // SD card; convert block number to byte addr
status = sd_send_command(SD_READ_BLK, addr); // send read command and logical sector address
if (status != SDCARD_OK) {
return SDCARD_RWFAIL;
}
status = sd_wait_for_data(); // wait for valid data token from card
if (status != 0xfe) // card must return 0xfe for CMD17
{
ShowErrorCode(status); // tell the user
return SDCARD_RWFAIL; // return error code
}
for (i = 0; i < 512; i++) // read sector data
block[i] = xchg(0xff);
xchg(0xff); // ignore CRC
xchg(0xff); // ignore CRC
return SDCARD_OK; // return success
}
static int8_t ModifyPWD(uint8_t mask) {
int8_t r;
uint16_t i;
mask = mask & 0x07; // top five bits MUST be 0, do not allow forced-erase!
r = sd_send_command(SD_LOCK_UNLOCK, 0);
if (r != 0) {
return SDCARD_RWFAIL;
}
xchg(0xfe); // send data token marking start of data block
xchg(mask); // always start with required command
xchg(pwd_len); // then send the password length
for (i = 0; i < 512; i++) // need to send one full block for CMD42
{
if (i < pwd_len) {
xchg(pwd[i]); // send each byte via SPI
} else {
xchg(0xff);
}
}
xchg(0xff); // ignore dummy checksum
xchg(0xff); // ignore dummy checksum
i = 0xffff; // max timeout
while (!xchg(0xFF) && (--i))
; // wait until we are not busy
if (i) return SDCARD_OK; // return success
else return SDCARD_RWFAIL; // nope, didn't work
}
static int8_t ForceErase(void) {
int8_t r;
sd_send_command(SD_SET_BLK_LEN, 1); // always set block length (CMD6) to 512 bytes
r = sd_send_command(SD_LOCK_UNLOCK, 0);
if (r != 0) {
return SDCARD_RWFAIL;
}
xchg(0xfe); // send data token marking start of data block
xchg(MASK_ERASE); // always start with required command
return SDCARD_OK; // return success
}
static void ShowErrorCode(int8_t status) {
if ((status & 0xe0) == 0) // if status byte has an error value...
{
Serial.print("\n\rDate error:");
if (status & ERRTKN_CARD_LOCKED) {
Serial.print(" Card is locked!");
}
if (status & ERRTKN_OUT_OF_RANGE) {
Serial.print(" Address is out of range!");
}
if (status & ERRTKN_CARD_ECC) {
Serial.print(" Card ECC failed!");
}
if (status & ERRTKN_CARD_CC) {
Serial.print("Card CC failed!");
}
}
}
static void ShowCardStatus(void) {
ReadCardStatus();
Serial.print("Password status: ");
if ((cardstatus[1] & 0x01) == 0) Serial.print("un");
Serial.print("locked");
}
static void Load_Password(void) {
uint8_t i;
for (i = 0; i < GLOBAL_PWD_LEN; i++) {
//pwd[i] = pgm_read_byte(&(GlobalPWDStr[i]));
pwd[i] = PWD_TTLive1105[i];
}
pwd_len = GLOBAL_PWD_LEN;
}
/*
==========================================================================
sd_send_command send raw command to SD card, return response
This routine accepts a single SD command and a 4-byte argument. It sends
the command plus argument, adding the appropriate CRC. It then returns
the one-byte response from the SD card.
For advanced commands (those with a command byte having bit 7 set), this
routine automatically sends the required preface command (CMD55) before
sending the requested command.
Upon exit, this routine returns the response byte from the SD card.
Possible responses are:
0xff No response from card; card might actually be missing
0x01 SD card returned 0x01, which is OK for most commands
0x?? other responses are command-specific
*/
static int8_t sd_send_command(uint8_t command, uint32_t arg) {
uint8_t response;
uint8_t i;
uint8_t crc;
if (command & 0x80) // special case, ACMD(n) is sent as CMD55 and CMDn
{
command = command & 0x7f; // strip high bit for later
response = sd_send_command(CMD55, 0); // send first part (recursion)
if (response > 1) return response;
}
deselect();
xchg(0xff);
select(); // enable CS
xchg(0xff);
xchg(command | 0x40); // command always has bit 6 set!
xchg((unsigned char)(arg >> 24)); // send data, starting with top byte
xchg((unsigned char)(arg >> 16));
xchg((unsigned char)(arg >> 8));
xchg((unsigned char)(arg & 0xff));
crc = 0x01; // good for most cases
if (command == SD_GO_IDLE) crc = 0x95; // this will be good enough for most commands
if (command == SD_SEND_IF_COND) crc = 0x87; // special case, have to use different CRC
xchg(crc); // send final byte
for (i = 0; i < 10; i++) // loop until timeout or response
{
response = xchg(0xff);
if ((response & 0x80) == 0) break; // high bit cleared means we got a response
}
/*
We have issued the command but the SD card is still selected. We
only deselect the card if the command we just sent is NOT a command
that requires additional data exchange, such as reading or writing
a block.
*/
if ((command != SD_READ_BLK) && (command != SD_READ_OCR) && (command != SD_SEND_CSD) && (command != SD_SEND_STATUS) && (command != SD_SEND_CID) && (command != SD_SEND_IF_COND) && (command != SD_LOCK_UNLOCK) && (command != SD_PROGRAM_CSD)) {
deselect(); // all done
xchg(0xff); // close with eight more clocks
}
return response; // let the caller sort it out
}
static int8_t sd_wait_for_data(void) {
int16_t i;
uint8_t r;
for (i = 0; i < 100; i++) {
r = xchg(0xff);
if (r != 0xff) break;
}
return (int8_t)r;
}
// ========================================================================================== //
String serialdata = ""; // Initialised to nothing.
void setup() {
Serial.begin(9600);
Serial.println("\r\nSD CARD LOCK/UNLOCK");
SD_CS_DDR = SD_CS_DDR | SD_CS_MASK; // make CS line an output
deselect(); // always start with SD card deselected
SPI_PORT = SPI_PORT | ((1 << MOSI_BIT) | (1 << SCK_BIT)); // drive outputs to the SPI port
SPI_DDR = SPI_DDR | ((1 << MOSI_BIT) | (1 << SCK_BIT)); // make the proper lines outputs
SPI_PORT = SPI_PORT | (1 << MISO_BIT); // turn on pull-up for DI
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
GenerateCRCTable();
}
void loop() {
byte ch;
String valStr;
int val;
if (Serial.available()) {
ch = Serial.read();
serialdata += (char)ch;
if (ch == '\r') { // Command received and ready.
serialdata.trim();
switch (serialdata.charAt(0)) {
//======================================================//
case 'i':
Serial.println("\r\n__________________________________________________\r\n\r\ni (info)");
uint8_t sw;
static uint8_t prev_sw = 0;
uint8_t r;
r = SDInit();
if (r != SDCARD_OK) {
Serial.println("\n\r\n\rCannot initialize card. Make sure the card is plugged in properly.");
}
Serial.print("\r\nCard type ");
Serial.println(sdtype);
r = ExamineSD();
if (r == SDCARD_OK) {
Serial.print("\r\nOCR = ");
for (r = 0; r < 4; r++) {
if (ocr[r] <= 0x0F) Serial.print("0");
Serial.print(ocr[r], HEX);
}
Serial.print("\r\nCSD = ");
for (r = 0; r < 16; r++) {
if (csd[r] <= 0x0F) Serial.print("0");
Serial.print(csd[r], HEX);
}
Serial.print("\r\nCID = ");
for (r = 0; r < 16; r++) {
if (cid[r] <= 0x0F) Serial.print("0");
Serial.print(cid[r], HEX);
}
Serial.println("\r\n");
ShowCardStatus();
Serial.println("\r\n");
} else {
Serial.println("\r\nUnable to read CSD.");
}
break;
//======================================================//
case 'l':
Serial.println("\r\n__________________________________________________\r\n\r\nl (locking)");
r = SDInit();
if (r != SDCARD_OK) {
Serial.println("\n\r\n\r No SD Card?");
}
ReadCardStatus();
if ((cardstatus[1] & 0x01) == 0) // if card is still unlocked...
{
Serial.println("\r\nLocking...");
Load_Password();
r = ModifyPWD(MASK_SET_PWD);
ReadCardStatus();
r = ModifyPWD(MASK_LOCK_UNLOCK);
ReadCardStatus();
if ((cardstatus[1] & 0x01) == 0) // if card is still unlocked...
{
Serial.println("Error! SD Card is still unlocked.");
} else {
Serial.println("\r\nLocked with password: ");
ShowPassword();
Serial.println("\r\n");
}
} else {
Serial.println("SD Card is already locked!");
}
break;
//======================================================//
case 'u':
Serial.println("\r\n__________________________________________________\r\n\r\nu (unlocking)");
r = SDInit();
if (r != SDCARD_OK) {
Serial.println("\n\r\n\r No SD Card?.");
}
ReadCardStatus();
if (cardstatus[1] & 0x01) {
Serial.println("\r\nUnlocking...");
Load_Password();
r = ModifyPWD(MASK_CLR_PWD);
ReadCardStatus();
if (cardstatus[1] & 0x01) // if card is still locked
{
r = ModifyPWD(MASK_CLR_PWD); // the unlock failed, try one more time
ReadCardStatus();
}
if (cardstatus[1] & 0x01) // if card is still locked...
{
Serial.println("Error, SD Card is still locked.");
} else {
Serial.println("\r\nUnlocked with password: ");
ShowPassword();
Serial.println("\r\n");
}
} else {
Serial.println("SD Card is already unlocked!");
}
break;
//======================================================//
case 'f':
Serial.println("\r\n__________________________________________________\r\n\r\nf (force erase)");
Serial.print("\r\nTrying to force-erase SD CARD...\r\n");
ReadCardStatus();
if (cardstatus[1] & 0x01) // if card is locked...
{
r = ForceErase();
Serial.print("please wait...\r\n");
_delay_ms(1000);
ReadCardStatus();
if (cardstatus[1] & 0x01) // if card is still locked...
{
r = ForceErase(); // erasing failed, try one more time
Serial.print("please wait...\r\n");
_delay_ms(1000);
ReadCardStatus();
}
if (cardstatus[1] & 0x01) // if card is still locked...
{
Serial.print("Error. SD Card is still locked.");
} else {
Serial.print("done.\r\n");
}
} else {
("card is not locked...\r\n");
}
break;
//======================================================//
default: Serial.println(serialdata);
} // switch
serialdata = ""; // Clear the string ready for the next command.
} // if \r
}
}