{"id":160,"date":"2017-03-19T22:54:13","date_gmt":"2017-03-19T20:54:13","guid":{"rendered":"http:\/\/blog.ilialex.gr\/?p=160"},"modified":"2017-04-04T10:41:00","modified_gmt":"2017-04-04T08:41:00","slug":"cofilos-sd-card-driver","status":"publish","type":"post","link":"http:\/\/blog.ilialex.gr\/?p=160","title":{"rendered":"COFILOS SD Card Driver"},"content":{"rendered":"<p><strong>SD Card Driver Development on COFILOS<\/strong><\/p>\n<p>Last year I completed a major milestone for COFILOS and the Perseus board. I completed a fully TDD written SD Card Driver.<\/p>\n<p>You may check the following video: <a href=\"https:\/\/www.youtube.com\/watch?v=vihvE6rhUSo\"><u><span style=\"color: #0066cc;\">COFILOS SD Card<\/span><\/u><\/a><\/p>\n<p>I was a big pain to write it, even though I had guide code from the internet and enough relevant information. The outcome is a very readable and organized code.<\/p>\n<p>The initialization phase is very complex (and took the lion&#8217;s share of time to implement) is shown here:<\/p>\n<pre class=\"prettyprint\"><code class=\"language-c\">\r\n<span style=\"font-size: x-small;\">\r\n\/*!\r\n* \\brief Device Start (Stub)\r\n* @param pstDriver_ : Pointer to Device structure\r\n* @return 0 (SUCCESS), 1: Fail (Could not open SPI driver)\r\n*\/\r\nBOOL f_DriverSD_Start(void *pstDriver_ )\r\n{\r\ntype_stDRIVERSD *pst_SD;\r\nINT8U v_error;\r\nINT8U v_PhysDevID;\r\nBOOL v_retval;\r\n\r\npst_SD = (type_stDRIVERSD *) pstDriver_;\r\n\r\nif (pst_SD-&gt;stDriver.eState != DRIVER_CLOSE) return 1;\r\nif (pst_SD-&gt;f_HAL_HookCardPresent == NULL) return 1;\r\nif (pst_SD-&gt;f_HAL_HookCardPresent() == 0) return 255;\r\n\r\nv_retval = 1; \/* default is failure *\/\r\n\r\nf_DriverSDHelper_InitSPI(pst_SD);\r\n\r\nf_DriverCoFILOS_Open((PINT8) \"SPI.Driver\", 0xFF, (DRIVER_STRUCT *) &amp;pst_SD-&gt;st_SPI);\r\n\r\nDriver_Start((DRIVER_STRUCT *) &amp;pst_SD-&gt;st_SPI);\r\n\r\n\/* Acquire Mutex, we need uninterrupted SPI access *\/\r\nDriver_Control((DRIVER_STRUCT *) &amp;pst_SD-&gt;st_SPI, CMD_SET_SPIMUTEX_ON, NULL);\r\n\r\nf_DriverSDHelper_ResetSD(pst_SD);\r\n\r\n\/* set default value for Unknown Card Type *\/\r\n\/* Assuming initially that all is good *\/\r\nv_PhysDevID = pst_SD-&gt;v_PhyVolumeID;\r\nst_SDPhysical[v_PhysDevID].v_UnknownCard = eSDUn_None;\r\npst_SD-&gt;v_CardType = eSDCardUnknown;\r\n\r\n\/* set CS, clocked *\/\r\nf_DriverSDHelper_SelectDevice(pst_SD);\r\n\r\nv_error = f_DriverSDHelper_SendCMD0(pst_SD);\r\nif (v_error == 0xFE)\r\n{\r\n  \/* Unknown Card *\/\r\n  \/* TODO: Unknown Card *\/\r\n  f_DriverSDHelper_UnknownDevice(pst_SD-&gt;v_PhyVolumeID, eSDUn_CMD0);\r\n}\r\nelse\r\n{\r\n  v_error = f_DriverSDHelper_SendCMD8(pst_SD);\r\n\r\n  if (v_error == 0) \/* SDV2 Byte or Block *\/\r\n  {\r\n    v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x40000000);\r\n    v_error = f_DriverSDHelper_SendCMD58(pst_SD);\r\n\r\n    if (pst_SD-&gt;v_CardType == eSDCardSDv2Byte)\r\n    {\r\n      v_error = f_DriverSDHelper_SendCMD16(pst_SD);\r\n      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Byte;\r\n    }\r\n    else\r\n    {\r\n      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Block;\r\n    }\r\n  }\r\n  else if (v_error == 0xFE) \/* SDv1 or MMCv3 or Unknown ? *\/\r\n {\r\n   st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv1;\r\n   v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x00000000);\r\n   if (v_error != 0)\r\n   {\r\n     \/* MMCv3 ? *\/\r\n     v_error = f_DriverSDHelper_SendCMD1(pst_SD);\r\n     if (v_error == 0)\r\n    {\r\n      st_SDPhysical[v_PhysDevID].v_CardType = eSDCardMMCv3;\r\n    }\r\n    else\r\n    {\r\n      f_DriverSDHelper_UnknownDevice(pst_SD-&gt;v_PhyVolumeID, eSDUn_CMD1);\r\n    }\r\n   }\r\n\r\n   if (v_error == 0)\r\n   {\r\n     v_error = f_DriverSDHelper_SendCMD16(pst_SD);\r\n   }\r\n  }\r\n  else\r\n  {\r\n    \/* Unknown card *\/\r\n    f_DriverSDHelper_UnknownDevice(pst_SD-&gt;v_PhyVolumeID, eSDUn_CMD8);\r\n  }\r\n}\r\n\r\n\/* if Card is SDv1 or better then acquire CSD\/CID *\/\r\nswitch(st_SDPhysical[v_PhysDevID].v_CardType)\r\n{\r\n   case eSDCardSDv1:\r\n   case eSDCardSDv2Block:\r\n   case eSDCardSDv2Byte:\r\n     f_DriverSDHelper_SendCMD9(pst_SD);\r\n     f_DriverSDHelper_SendCMD10(pst_SD);\r\n     break;\r\n  default:;\r\n}\r\n\r\n\/* Deassert CS, clocked *\/\r\nf_DriverSDHelper_DeSelectDevice(pst_SD);\r\n\r\n\/* Release Mutex, finished exclusive SPI access *\/\r\nDriver_Control((DRIVER_STRUCT *) &amp;pst_SD-&gt;st_SPI, CMD_SET_SPIMUTEX_OFF, NULL);\r\n\r\nif (st_SDPhysical[v_PhysDevID].v_CardType == eSDCardUnknown)\r\n{\r\n  v_retval = 1;\r\n}\r\nelse\r\n{\r\n  st_SDPhysical[v_PhysDevID].v_DriverState = eSD_Started;\r\n  v_retval = 0;\r\n  pst_SD-&gt;st_SPI.v_ConfigReg = 0xA002; \/* increase clock to 24MHz *\/\r\n}\r\nst_SDPhysical[v_PhysDevID].v_OpenCnt = 0;\r\n\r\nreturn v_retval;\r\n\r\n}\r\n<\/span><\/code><\/pre>\n<p>After completing the TDD phase on my host (PC) I needed to run my tests on the actual target (ColdFire) microcontroller.<\/p>\n<p>In the next picture you may see the hardware setup along with my programmer. I used a micro-SD of 8GB from Kingston for test.<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_Board.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-124\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_Board-300x169.jpg\" alt=\"sd_board\" width=\"300\" height=\"169\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_Board-300x169.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_Board-768x432.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_Board-1024x576.jpg 1024w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_Board.jpg 1920w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>I had already captured the sectors (and image) of the SD card and saved them on my hard-drive. I wanted to be able to compare what the microcontroller would read with the actual data stored.<\/p>\n<p>After having my target run the code, I opened a PuTTY terminal to connect with my virtual COM on my target. Then through the CLI (Command Line Interpreter) I mounted the SD card. I issued the <strong>SDInfo<\/strong> command and the data provided the SD card &#8220;geometry&#8221;.<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_Init.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-125\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_Init-300x169.jpg\" alt=\"sd_cli_init\" width=\"300\" height=\"169\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_Init-300x169.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_Init-768x432.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_Init-1024x576.jpg 1024w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_Init.jpg 1920w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>I then read sector zero (or MBR for FAT file system). I compared the data with the image capture to confirm reading of the same piece of information.<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec0000.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-126\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec0000-300x169.jpg\" alt=\"sd_cli_rdsec0000\" width=\"300\" height=\"169\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec0000-300x169.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec0000-768x432.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec0000-1024x576.jpg 1024w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec0000.jpg 1920w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>The first FAT sector (0x2000) was also read and verified. I had to check it as I needed to run a FAT File System Handler to read the SD Card at a later stage (not very later, though).<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec2000.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-127\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec2000-300x169.jpg\" alt=\"sd_cli_rdsec2000\" width=\"300\" height=\"169\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec2000-300x169.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec2000-768x432.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec2000-1024x576.jpg 1024w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_RdSec2000.jpg 1920w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Then the write tests started. My write command in CLI supports a single 32-bit value write currently. This is sufficient at the moment to verify a sector write operation. So I used sector 1 which is unused to write the value 0x1234567<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-128\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001-300x169.jpg\" alt=\"sd_cli_wrsec0001\" width=\"300\" height=\"169\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001-300x169.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001-768x432.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001-1024x576.jpg 1024w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001.jpg 1920w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Next a read on sector 1 is performed to check that the sector was written. And voila! Notice the Big-Endian write (0x78 at byte 0, instead of 0x12).<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001b.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-129\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001b-300x169.jpg\" alt=\"sd_cli_wrsec0001b\" width=\"300\" height=\"169\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001b-300x169.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001b-768x432.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001b-1024x576.jpg 1024w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SD_CLI_WrSec0001b.jpg 1920w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>At the same time during debugging I used my Tektronix TDS3012B Digital Oscilloscope and a Python script to capture and decode the SD SPI data.<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_Capture.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-130\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_Capture-300x155.jpg\" alt=\"spi_capture\" width=\"300\" height=\"155\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_Capture-300x155.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_Capture-768x396.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_Capture.jpg 987w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>The script outputs also a VCD format file suitable for use with GTKWave. Thus I could see and analyse the data, both analog and digital.<\/p>\n<p><a href=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_SD.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-131\" src=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_SD-300x143.jpg\" alt=\"spi_sd\" width=\"300\" height=\"143\" srcset=\"http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_SD-300x143.jpg 300w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_SD-768x367.jpg 768w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_SD-1024x489.jpg 1024w, http:\/\/blog.ilialex.gr\/wp-content\/uploads\/2017\/01\/SPI_SD.jpg 1315w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Due to the TDD methods used the debugging phase on the target was very fast. With a few iterations and the help of the scope and the Python scripting tool the SD card driver worked well.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<div id=\"CodeProject\" style=\"display: none;\"><a href=\"https:\/\/www.codeproject.com\" rel=\"tag\">CodeProject<\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>SD Card Driver Development on COFILOS Last year I completed a major milestone for COFILOS and the Perseus board. I completed a fully TDD written SD Card Driver. You may check the following video: COFILOS SD Card I was a big pain to write it, even though I had guide code from the internet and &hellip; <a href=\"http:\/\/blog.ilialex.gr\/?p=160\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">COFILOS SD Card Driver<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"video","meta":{"footnotes":""},"categories":[7],"tags":[9,11,10],"class_list":["post-160","post","type-post","status-publish","format-video","hentry","category-firmware","tag-cofilos","tag-coldfire","tag-sd","post_format-post-format-video"],"_links":{"self":[{"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=\/wp\/v2\/posts\/160","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=160"}],"version-history":[{"count":14,"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=\/wp\/v2\/posts\/160\/revisions"}],"predecessor-version":[{"id":188,"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=\/wp\/v2\/posts\/160\/revisions\/188"}],"wp:attachment":[{"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=160"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=160"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.ilialex.gr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=160"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}