Add sd card support + savegames
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
/* crc.c
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
/* Derived from:
|
||||
* SD/MMC File System Library
|
||||
* Copyright (c) 2016 Neil Thiessen
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "crc.h"
|
||||
|
||||
static const char m_Crc7Table[] = {0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36,
|
||||
0x3F, 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77, 0x19, 0x10, 0x0B,
|
||||
0x02, 0x3D, 0x34, 0x2F, 0x26, 0x51, 0x58, 0x43, 0x4A, 0x75, 0x7C, 0x67,
|
||||
0x6E, 0x32, 0x3B, 0x20, 0x29, 0x16, 0x1F, 0x04, 0x0D, 0x7A, 0x73, 0x68,
|
||||
0x61, 0x5E, 0x57, 0x4C, 0x45, 0x2B, 0x22, 0x39, 0x30, 0x0F, 0x06, 0x1D,
|
||||
0x14, 0x63, 0x6A, 0x71, 0x78, 0x47, 0x4E, 0x55, 0x5C, 0x64, 0x6D, 0x76,
|
||||
0x7F, 0x40, 0x49, 0x52, 0x5B, 0x2C, 0x25, 0x3E, 0x37, 0x08, 0x01, 0x1A,
|
||||
0x13, 0x7D, 0x74, 0x6F, 0x66, 0x59, 0x50, 0x4B, 0x42, 0x35, 0x3C, 0x27,
|
||||
0x2E, 0x11, 0x18, 0x03, 0x0A, 0x56, 0x5F, 0x44, 0x4D, 0x72, 0x7B, 0x60,
|
||||
0x69, 0x1E, 0x17, 0x0C, 0x05, 0x3A, 0x33, 0x28, 0x21, 0x4F, 0x46, 0x5D,
|
||||
0x54, 0x6B, 0x62, 0x79, 0x70, 0x07, 0x0E, 0x15, 0x1C, 0x23, 0x2A, 0x31,
|
||||
0x38, 0x41, 0x48, 0x53, 0x5A, 0x65, 0x6C, 0x77, 0x7E, 0x09, 0x00, 0x1B,
|
||||
0x12, 0x2D, 0x24, 0x3F, 0x36, 0x58, 0x51, 0x4A, 0x43, 0x7C, 0x75, 0x6E,
|
||||
0x67, 0x10, 0x19, 0x02, 0x0B, 0x34, 0x3D, 0x26, 0x2F, 0x73, 0x7A, 0x61,
|
||||
0x68, 0x57, 0x5E, 0x45, 0x4C, 0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D,
|
||||
0x04, 0x6A, 0x63, 0x78, 0x71, 0x4E, 0x47, 0x5C, 0x55, 0x22, 0x2B, 0x30,
|
||||
0x39, 0x06, 0x0F, 0x14, 0x1D, 0x25, 0x2C, 0x37, 0x3E, 0x01, 0x08, 0x13,
|
||||
0x1A, 0x6D, 0x64, 0x7F, 0x76, 0x49, 0x40, 0x5B, 0x52, 0x3C, 0x35, 0x2E,
|
||||
0x27, 0x18, 0x11, 0x0A, 0x03, 0x74, 0x7D, 0x66, 0x6F, 0x50, 0x59, 0x42,
|
||||
0x4B, 0x17, 0x1E, 0x05, 0x0C, 0x33, 0x3A, 0x21, 0x28, 0x5F, 0x56, 0x4D,
|
||||
0x44, 0x7B, 0x72, 0x69, 0x60, 0x0E, 0x07, 0x1C, 0x15, 0x2A, 0x23, 0x38,
|
||||
0x31, 0x46, 0x4F, 0x54, 0x5D, 0x62, 0x6B, 0x70, 0x79};
|
||||
|
||||
static const unsigned short m_Crc16Table[256] = {0x0000, 0x1021, 0x2042,
|
||||
0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B,
|
||||
0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5,
|
||||
0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C,
|
||||
0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4,
|
||||
0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B,
|
||||
0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5,
|
||||
0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E,
|
||||
0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96,
|
||||
0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79,
|
||||
0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03,
|
||||
0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68,
|
||||
0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
|
||||
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188,
|
||||
0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1,
|
||||
0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB,
|
||||
0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2,
|
||||
0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E,
|
||||
0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447,
|
||||
0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D,
|
||||
0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844,
|
||||
0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C,
|
||||
0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37,
|
||||
0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D,
|
||||
0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2,
|
||||
0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA,
|
||||
0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1,
|
||||
0x1EF0};
|
||||
|
||||
char crc7(const char* data, int length)
|
||||
{
|
||||
//Calculate the CRC7 checksum for the specified data block
|
||||
char crc = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
crc = m_Crc7Table[(crc << 1) ^ data[i]];
|
||||
}
|
||||
|
||||
//Return the calculated checksum
|
||||
return crc;
|
||||
}
|
||||
|
||||
unsigned short crc16(const char* data, int length)
|
||||
{
|
||||
//Calculate the CRC16 checksum for the specified data block
|
||||
unsigned short crc = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
crc = (crc << 8) ^ m_Crc16Table[((crc >> 8) ^ data[i]) & 0x00FF];
|
||||
}
|
||||
|
||||
//Return the calculated checksum
|
||||
return crc;
|
||||
}
|
||||
|
||||
void update_crc16(unsigned short *pCrc16, const char data[], size_t length) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
*pCrc16 = (*pCrc16 << 8) ^ m_Crc16Table[((*pCrc16 >> 8) ^ data[i]) & 0x00FF];
|
||||
}
|
||||
}
|
||||
/* [] END OF FILE */
|
||||
@@ -0,0 +1,42 @@
|
||||
/* crc.h
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
/* Derived from:
|
||||
* SD/MMC File System Library
|
||||
* Copyright (c) 2016 Neil Thiessen
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SD_CRC_H
|
||||
#define SD_CRC_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
char crc7(const char* data, int length);
|
||||
unsigned short crc16(const char* data, int length);
|
||||
void update_crc16(unsigned short *pCrc16, const char data[], size_t length);
|
||||
|
||||
#endif
|
||||
|
||||
/* [] END OF FILE */
|
||||
@@ -0,0 +1,96 @@
|
||||
/* demo_logging.c
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
/*
|
||||
FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
|
||||
All rights reserved
|
||||
|
||||
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
|
||||
|
||||
This file is part of the FreeRTOS distribution.
|
||||
|
||||
FreeRTOS is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License (version 2) as published by the
|
||||
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
|
||||
|
||||
***************************************************************************
|
||||
>>! NOTE: The modification to the GPL is included to allow you to !<<
|
||||
>>! distribute a combined work that includes FreeRTOS without being !<<
|
||||
>>! obliged to provide the source code for proprietary components !<<
|
||||
>>! outside of the FreeRTOS kernel. !<<
|
||||
***************************************************************************
|
||||
|
||||
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. Full license text is available on the following
|
||||
link: http://www.freertos.org/a00114.html
|
||||
|
||||
***************************************************************************
|
||||
* *
|
||||
* FreeRTOS provides completely free yet professionally developed, *
|
||||
* robust, strictly quality controlled, supported, and cross *
|
||||
* platform software that is more than just the market leader, it *
|
||||
* is the industry's de facto standard. *
|
||||
* *
|
||||
* Help yourself get started quickly while simultaneously helping *
|
||||
* to support the FreeRTOS project by purchasing a FreeRTOS *
|
||||
* tutorial book, reference manual, or both: *
|
||||
* http://www.FreeRTOS.org/Documentation *
|
||||
* *
|
||||
***************************************************************************
|
||||
|
||||
http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
|
||||
the FAQ page "My application does not run, what could be wrong?". Have you
|
||||
defined configASSERT()?
|
||||
|
||||
http://www.FreeRTOS.org/support - In return for receiving this top quality
|
||||
embedded software for free we request you assist our global community by
|
||||
participating in the support forum.
|
||||
|
||||
http://www.FreeRTOS.org/training - Investing in training allows your team to
|
||||
be as productive as possible as early as possible. Now you can receive
|
||||
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
|
||||
Ltd, and the world's leading authority on the world's leading RTOS.
|
||||
|
||||
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
|
||||
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
|
||||
compatible FAT file system, and our tiny thread aware UDP/IP stack.
|
||||
|
||||
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
|
||||
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
|
||||
|
||||
http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
|
||||
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
|
||||
licenses offer ticketed support, indemnification and commercial middleware.
|
||||
|
||||
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
|
||||
engineered and independently SIL3 certified version for use in safety and
|
||||
mission critical applications that require provable dependability.
|
||||
|
||||
1 tab == 4 spaces!
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void vLoggingPrintf( const char *pcFormat, ... )
|
||||
{
|
||||
char pcBuffer[256] = {0};
|
||||
va_list xArgs;
|
||||
va_start( xArgs, pcFormat );
|
||||
vsnprintf( pcBuffer, sizeof(pcBuffer), pcFormat, xArgs );
|
||||
va_end( xArgs );
|
||||
printf("%s", pcBuffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
@@ -0,0 +1,33 @@
|
||||
/* hw_config.h
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ff.h"
|
||||
#include "sd_card.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
size_t sd_get_num();
|
||||
sd_card_t *sd_get_by_num(size_t num);
|
||||
|
||||
size_t spi_get_num();
|
||||
spi_t *spi_get_by_num(size_t num);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* [] END OF FILE */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
/* sd_card.h
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
// Note: The model used here is one FatFS per SD card.
|
||||
// Multiple partitions on a card are not supported.
|
||||
|
||||
#ifndef _SD_CARD_H_
|
||||
#define _SD_CARD_H_
|
||||
|
||||
#include <stdint.h>
|
||||
//
|
||||
#include "hardware/gpio.h"
|
||||
#include "pico/mutex.h"
|
||||
//
|
||||
#include "ff.h"
|
||||
//
|
||||
#include "spi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// "Class" representing SD Cards
|
||||
typedef struct {
|
||||
const char *pcName;
|
||||
spi_t *spi;
|
||||
// Slave select is here in sd_card_t because multiple SDs can share an SPI
|
||||
uint ss_gpio; // Slave select for this SD card
|
||||
bool use_card_detect;
|
||||
uint card_detect_gpio; // Card detect; ignored if !use_card_detect
|
||||
uint card_detected_true; // Varies with card socket; ignored if !use_card_detect
|
||||
// Drive strength levels for GPIO outputs.
|
||||
// enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2,
|
||||
// GPIO_DRIVE_STRENGTH_12MA = 3 }
|
||||
bool set_drive_strength;
|
||||
enum gpio_drive_strength ss_gpio_drive_strength;
|
||||
|
||||
// Following fields are used to keep track of the state of the card:
|
||||
int m_Status; // Card status
|
||||
uint64_t sectors; // Assigned dynamically
|
||||
int card_type; // Assigned dynamically
|
||||
mutex_t mutex;
|
||||
FATFS fatfs;
|
||||
bool mounted;
|
||||
} sd_card_t;
|
||||
|
||||
#define SD_BLOCK_DEVICE_ERROR_NONE 0
|
||||
#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */
|
||||
#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */
|
||||
#define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */
|
||||
#define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */
|
||||
#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */
|
||||
#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */
|
||||
#define SD_BLOCK_DEVICE_ERROR_UNUSABLE -5007 /*!< unusable card */
|
||||
#define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE -5008 /*!< No response from device */
|
||||
#define SD_BLOCK_DEVICE_ERROR_CRC -5009 /*!< CRC error */
|
||||
#define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */
|
||||
#define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */
|
||||
|
||||
///* Disk Status Bits (DSTATUS) */
|
||||
// See diskio.h.
|
||||
//enum {
|
||||
// STA_NOINIT = 0x01, /* Drive not initialized */
|
||||
// STA_NODISK = 0x02, /* No medium in the drive */
|
||||
// STA_PROTECT = 0x04 /* Write protected */
|
||||
//};
|
||||
|
||||
bool sd_init_driver();
|
||||
int sd_init(sd_card_t *pSD);
|
||||
int sd_write_blocks(sd_card_t *pSD, const uint8_t *buffer,
|
||||
uint64_t ulSectorNumber, uint32_t blockCnt);
|
||||
int sd_read_blocks(sd_card_t *pSD, uint8_t *buffer, uint64_t ulSectorNumber,
|
||||
uint32_t ulSectorCount);
|
||||
bool sd_card_detect(sd_card_t *pSD);
|
||||
uint64_t sd_sectors(sd_card_t *pSD);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/* [] END OF FILE */
|
||||
@@ -0,0 +1,121 @@
|
||||
/* sd_spi.c
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/* Standard includes. */
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
//
|
||||
#include "hardware/gpio.h"
|
||||
//
|
||||
#include "my_debug.h"
|
||||
#include "sd_card.h"
|
||||
#include "sd_spi.h"
|
||||
#include "spi.h"
|
||||
|
||||
//#define TRACE_PRINTF(fmt, args...)
|
||||
#define TRACE_PRINTF printf // task_printf
|
||||
|
||||
void sd_spi_go_high_frequency(sd_card_t *pSD) {
|
||||
uint actual = spi_set_baudrate(pSD->spi->hw_inst, pSD->spi->baud_rate);
|
||||
TRACE_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual);
|
||||
}
|
||||
void sd_spi_go_low_frequency(sd_card_t *pSD) {
|
||||
uint actual = spi_set_baudrate(pSD->spi->hw_inst, 400 * 1000); // Actual frequency: 398089
|
||||
TRACE_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual);
|
||||
}
|
||||
|
||||
static void sd_spi_lock(sd_card_t *pSD) {
|
||||
spi_lock(pSD->spi);
|
||||
}
|
||||
static void sd_spi_unlock(sd_card_t *pSD) {
|
||||
spi_unlock(pSD->spi);
|
||||
}
|
||||
|
||||
// Would do nothing if pSD->ss_gpio were set to GPIO_FUNC_SPI.
|
||||
static void sd_spi_select(sd_card_t *pSD) {
|
||||
gpio_put(pSD->ss_gpio, 0);
|
||||
// A fill byte seems to be necessary, sometimes:
|
||||
uint8_t fill = SPI_FILL_CHAR;
|
||||
spi_write_blocking(pSD->spi->hw_inst, &fill, 1);
|
||||
LED_ON();
|
||||
}
|
||||
|
||||
static void sd_spi_deselect(sd_card_t *pSD) {
|
||||
gpio_put(pSD->ss_gpio, 1);
|
||||
LED_OFF();
|
||||
/*
|
||||
MMC/SDC enables/disables the DO output in synchronising to the SCLK. This
|
||||
means there is a posibility of bus conflict with MMC/SDC and another SPI
|
||||
slave that shares an SPI bus. Therefore to make MMC/SDC release the MISO
|
||||
line, the master device needs to send a byte after the CS signal is
|
||||
deasserted.
|
||||
*/
|
||||
uint8_t fill = SPI_FILL_CHAR;
|
||||
spi_write_blocking(pSD->spi->hw_inst, &fill, 1);
|
||||
}
|
||||
/* Some SD cards want to be deselected between every bus transaction */
|
||||
void sd_spi_deselect_pulse(sd_card_t *pSD) {
|
||||
sd_spi_deselect(pSD);
|
||||
// tCSH Pulse duration, CS high 200 ns
|
||||
sd_spi_select(pSD);
|
||||
}
|
||||
void sd_spi_acquire(sd_card_t *pSD) {
|
||||
sd_spi_lock(pSD);
|
||||
sd_spi_select(pSD);
|
||||
}
|
||||
|
||||
void sd_spi_release(sd_card_t *pSD) {
|
||||
sd_spi_deselect(pSD);
|
||||
sd_spi_unlock(pSD);
|
||||
}
|
||||
|
||||
bool sd_spi_transfer(sd_card_t *pSD, const uint8_t *tx, uint8_t *rx,
|
||||
size_t length) {
|
||||
return spi_transfer(pSD->spi, tx, rx, length);
|
||||
}
|
||||
|
||||
uint8_t sd_spi_write(sd_card_t *pSD, const uint8_t value) {
|
||||
// TRACE_PRINTF("%s\n", __FUNCTION__);
|
||||
uint8_t received = SPI_FILL_CHAR;
|
||||
#if 0
|
||||
int num = spi_write_read_blocking(pSD->spi->hw_inst, &value, &received, 1);
|
||||
myASSERT(1 == num);
|
||||
#else
|
||||
bool success = spi_transfer(pSD->spi, &value, &received, 1);
|
||||
myASSERT(success);
|
||||
#endif
|
||||
return received;
|
||||
}
|
||||
|
||||
void sd_spi_send_initializing_sequence(sd_card_t * pSD) {
|
||||
bool old_ss = gpio_get(pSD->ss_gpio);
|
||||
// Set DI and CS high and apply 74 or more clock pulses to SCLK:
|
||||
gpio_put(pSD->ss_gpio, 1);
|
||||
uint8_t ones[10];
|
||||
memset(ones, 0xFF, sizeof ones);
|
||||
absolute_time_t timeout_time = make_timeout_time_ms(1);
|
||||
do {
|
||||
sd_spi_transfer(pSD, ones, NULL, sizeof ones);
|
||||
} while (0 < absolute_time_diff_us(get_absolute_time(), timeout_time));
|
||||
gpio_put(pSD->ss_gpio, old_ss);
|
||||
}
|
||||
|
||||
void sd_spi_init_pl022(sd_card_t *pSD) {
|
||||
// Let the PL022 SPI handle it.
|
||||
// the CS line is brought high between each byte during transmission.
|
||||
gpio_set_function(pSD->ss_gpio, GPIO_FUNC_SPI);
|
||||
}
|
||||
|
||||
/* [] END OF FILE */
|
||||
@@ -0,0 +1,42 @@
|
||||
/* sd_spi.h
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _SD_SPI_H_
|
||||
#define _SD_SPI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sd_card.h"
|
||||
|
||||
/* Transfer tx to SPI while receiving SPI to rx.
|
||||
tx or rx can be NULL if not important. */
|
||||
bool sd_spi_transfer(sd_card_t *pSD, const uint8_t *tx, uint8_t *rx, size_t length);
|
||||
uint8_t sd_spi_write(sd_card_t *pSD, const uint8_t value);
|
||||
void sd_spi_deselect_pulse(sd_card_t *pSD);
|
||||
void sd_spi_acquire(sd_card_t *pSD);
|
||||
void sd_spi_release(sd_card_t *pSD);
|
||||
void sd_spi_go_low_frequency(sd_card_t *this);
|
||||
void sd_spi_go_high_frequency(sd_card_t *this);
|
||||
|
||||
/*
|
||||
After power up, the host starts the clock and sends the initializing sequence on the CMD line.
|
||||
This sequence is a contiguous stream of logical ‘1’s. The sequence length is the maximum of 1msec,
|
||||
74 clocks or the supply-ramp-uptime; the additional 10 clocks
|
||||
(over the 64 clocks after what the card should be ready for communication) is
|
||||
provided to eliminate power-up synchronization problems.
|
||||
*/
|
||||
void sd_spi_send_initializing_sequence(sd_card_t * pSD);
|
||||
|
||||
#endif
|
||||
|
||||
/* [] END OF FILE */
|
||||
@@ -0,0 +1,221 @@
|
||||
/* spi.c
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
//
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/sem.h"
|
||||
//
|
||||
#include "my_debug.h"
|
||||
//
|
||||
#include "spi.h"
|
||||
|
||||
static bool irqChannel1 = false;
|
||||
static bool irqShared = true;
|
||||
|
||||
void spi_irq_handler(spi_t *pSPI) {
|
||||
if (irqChannel1) {
|
||||
if (dma_hw->ints1 & 1u << pSPI->rx_dma) { // Ours?
|
||||
dma_hw->ints1 = 1u << pSPI->rx_dma; // clear it
|
||||
myASSERT(!dma_channel_is_busy(pSPI->rx_dma));
|
||||
sem_release(&pSPI->sem);
|
||||
}
|
||||
} else {
|
||||
if (dma_hw->ints0 & 1u << pSPI->rx_dma) { // Ours?
|
||||
dma_hw->ints0 = 1u << pSPI->rx_dma; // clear it
|
||||
myASSERT(!dma_channel_is_busy(pSPI->rx_dma));
|
||||
sem_release(&pSPI->sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_spi_dma_irq_channel(bool useChannel1, bool shared) {
|
||||
irqChannel1 = useChannel1;
|
||||
irqShared = shared;
|
||||
}
|
||||
|
||||
// SPI Transfer: Read & Write (simultaneously) on SPI bus
|
||||
// If the data that will be received is not important, pass NULL as rx.
|
||||
// If the data that will be transmitted is not important,
|
||||
// pass NULL as tx and then the SPI_FILL_CHAR is sent out as each data
|
||||
// element.
|
||||
bool spi_transfer(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length) {
|
||||
// myASSERT(512 == length || 1 == length);
|
||||
myASSERT(tx || rx);
|
||||
// myASSERT(!(tx && rx));
|
||||
|
||||
// tx write increment is already false
|
||||
if (tx) {
|
||||
channel_config_set_read_increment(&pSPI->tx_dma_cfg, true);
|
||||
} else {
|
||||
static const uint8_t dummy = SPI_FILL_CHAR;
|
||||
tx = &dummy;
|
||||
channel_config_set_read_increment(&pSPI->tx_dma_cfg, false);
|
||||
}
|
||||
|
||||
// rx read increment is already false
|
||||
if (rx) {
|
||||
channel_config_set_write_increment(&pSPI->rx_dma_cfg, true);
|
||||
} else {
|
||||
static uint8_t dummy = 0xA5;
|
||||
rx = &dummy;
|
||||
channel_config_set_write_increment(&pSPI->rx_dma_cfg, false);
|
||||
}
|
||||
// Clear the interrupt request.
|
||||
dma_hw->ints0 = 1u << pSPI->rx_dma;
|
||||
|
||||
dma_channel_configure(pSPI->tx_dma, &pSPI->tx_dma_cfg,
|
||||
&spi_get_hw(pSPI->hw_inst)->dr, // write address
|
||||
tx, // read address
|
||||
length, // element count (each element is of
|
||||
// size transfer_data_size)
|
||||
false); // start
|
||||
dma_channel_configure(pSPI->rx_dma, &pSPI->rx_dma_cfg,
|
||||
rx, // write address
|
||||
&spi_get_hw(pSPI->hw_inst)->dr, // read address
|
||||
length, // element count (each element is of
|
||||
// size transfer_data_size)
|
||||
false); // start
|
||||
|
||||
// start them exactly simultaneously to avoid races (in extreme cases
|
||||
// the FIFO could overflow)
|
||||
dma_start_channel_mask((1u << pSPI->tx_dma) | (1u << pSPI->rx_dma));
|
||||
|
||||
/* Timeout 1 sec */
|
||||
uint32_t timeOut = 1000;
|
||||
/* Wait until master completes transfer or time out has occured. */
|
||||
bool rc = sem_acquire_timeout_ms(
|
||||
&pSPI->sem, timeOut); // Wait for notification from ISR
|
||||
if (!rc) {
|
||||
// If the timeout is reached the function will return false
|
||||
DBG_PRINTF("Notification wait timed out in %s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
// Shouldn't be necessary:
|
||||
dma_channel_wait_for_finish_blocking(pSPI->tx_dma);
|
||||
dma_channel_wait_for_finish_blocking(pSPI->rx_dma);
|
||||
|
||||
myASSERT(!dma_channel_is_busy(pSPI->tx_dma));
|
||||
myASSERT(!dma_channel_is_busy(pSPI->rx_dma));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void spi_lock(spi_t *pSPI) {
|
||||
myASSERT(mutex_is_initialized(&pSPI->mutex));
|
||||
mutex_enter_blocking(&pSPI->mutex);
|
||||
}
|
||||
void spi_unlock(spi_t *pSPI) {
|
||||
myASSERT(mutex_is_initialized(&pSPI->mutex));
|
||||
mutex_exit(&pSPI->mutex);
|
||||
}
|
||||
|
||||
bool my_spi_init(spi_t *pSPI) {
|
||||
auto_init_mutex(my_spi_init_mutex);
|
||||
mutex_enter_blocking(&my_spi_init_mutex);
|
||||
if (!pSPI->initialized) {
|
||||
//// The SPI may be shared (using multiple SSs); protect it
|
||||
//pSPI->mutex = xSemaphoreCreateRecursiveMutex();
|
||||
//xSemaphoreTakeRecursive(pSPI->mutex, portMAX_DELAY);
|
||||
if (!mutex_is_initialized(&pSPI->mutex)) mutex_init(&pSPI->mutex);
|
||||
spi_lock(pSPI);
|
||||
|
||||
// For the IRQ notification:
|
||||
sem_init(&pSPI->sem, 0, 1);
|
||||
|
||||
/* Configure component */
|
||||
// Enable SPI at 100 kHz and connect to GPIOs
|
||||
spi_init(pSPI->hw_inst, 100 * 1000);
|
||||
spi_set_format(pSPI->hw_inst, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
||||
|
||||
gpio_set_function(pSPI->miso_gpio, GPIO_FUNC_SPI);
|
||||
gpio_set_function(pSPI->mosi_gpio, GPIO_FUNC_SPI);
|
||||
gpio_set_function(pSPI->sck_gpio, GPIO_FUNC_SPI);
|
||||
// ss_gpio is initialized in sd_init_driver()
|
||||
|
||||
// Slew rate limiting levels for GPIO outputs.
|
||||
// enum gpio_slew_rate { GPIO_SLEW_RATE_SLOW = 0, GPIO_SLEW_RATE_FAST = 1 }
|
||||
// void gpio_set_slew_rate (uint gpio,enum gpio_slew_rate slew)
|
||||
// Default appears to be GPIO_SLEW_RATE_SLOW.
|
||||
|
||||
// Drive strength levels for GPIO outputs.
|
||||
// enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2,
|
||||
// GPIO_DRIVE_STRENGTH_12MA = 3 }
|
||||
// enum gpio_drive_strength gpio_get_drive_strength (uint gpio)
|
||||
if (pSPI->set_drive_strength) {
|
||||
gpio_set_drive_strength(pSPI->mosi_gpio, pSPI->mosi_gpio_drive_strength);
|
||||
gpio_set_drive_strength(pSPI->sck_gpio, pSPI->sck_gpio_drive_strength);
|
||||
}
|
||||
|
||||
// SD cards' DO MUST be pulled up.
|
||||
gpio_pull_up(pSPI->miso_gpio);
|
||||
|
||||
// Grab some unused dma channels
|
||||
pSPI->tx_dma = dma_claim_unused_channel(true);
|
||||
pSPI->rx_dma = dma_claim_unused_channel(true);
|
||||
|
||||
pSPI->tx_dma_cfg = dma_channel_get_default_config(pSPI->tx_dma);
|
||||
pSPI->rx_dma_cfg = dma_channel_get_default_config(pSPI->rx_dma);
|
||||
channel_config_set_transfer_data_size(&pSPI->tx_dma_cfg, DMA_SIZE_8);
|
||||
channel_config_set_transfer_data_size(&pSPI->rx_dma_cfg, DMA_SIZE_8);
|
||||
|
||||
// We set the outbound DMA to transfer from a memory buffer to the SPI
|
||||
// transmit FIFO paced by the SPI TX FIFO DREQ The default is for the
|
||||
// read address to increment every element (in this case 1 byte -
|
||||
// DMA_SIZE_8) and for the write address to remain unchanged.
|
||||
channel_config_set_dreq(&pSPI->tx_dma_cfg, spi_get_index(pSPI->hw_inst)
|
||||
? DREQ_SPI1_TX
|
||||
: DREQ_SPI0_TX);
|
||||
channel_config_set_write_increment(&pSPI->tx_dma_cfg, false);
|
||||
|
||||
// We set the inbound DMA to transfer from the SPI receive FIFO to a
|
||||
// memory buffer paced by the SPI RX FIFO DREQ We coinfigure the read
|
||||
// address to remain unchanged for each element, but the write address
|
||||
// to increment (so data is written throughout the buffer)
|
||||
channel_config_set_dreq(&pSPI->rx_dma_cfg, spi_get_index(pSPI->hw_inst)
|
||||
? DREQ_SPI1_RX
|
||||
: DREQ_SPI0_RX);
|
||||
channel_config_set_read_increment(&pSPI->rx_dma_cfg, false);
|
||||
|
||||
/* Theory: we only need an interrupt on rx complete,
|
||||
since if rx is complete, tx must also be complete. */
|
||||
|
||||
// Configure the processor to run dma_handler() when DMA IRQ 0/1 is
|
||||
// asserted:
|
||||
int irq = irqChannel1 ? DMA_IRQ_1 : DMA_IRQ_0;
|
||||
if (irqShared) {
|
||||
irq_add_shared_handler(
|
||||
irq, pSPI->dma_isr,
|
||||
PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
} else {
|
||||
irq_set_exclusive_handler(irq, pSPI->dma_isr);
|
||||
}
|
||||
|
||||
// Tell the DMA to raise IRQ line 0/1 when the channel finishes a block
|
||||
if (irqChannel1) {
|
||||
dma_channel_set_irq1_enabled(pSPI->rx_dma, true);
|
||||
} else {
|
||||
dma_channel_set_irq0_enabled(pSPI->rx_dma, true);
|
||||
}
|
||||
irq_set_enabled(irq, true);
|
||||
LED_INIT();
|
||||
pSPI->initialized = true;
|
||||
spi_unlock(pSPI);
|
||||
}
|
||||
mutex_exit(&my_spi_init_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* [] END OF FILE */
|
||||
@@ -0,0 +1,93 @@
|
||||
/* spi.h
|
||||
Copyright 2021 Carl John Kugler III
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the License); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
//
|
||||
// Pico includes
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/spi.h"
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/sem.h"
|
||||
#include "pico/types.h"
|
||||
|
||||
#define SPI_FILL_CHAR (0xFF)
|
||||
|
||||
// "Class" representing SPIs
|
||||
typedef struct {
|
||||
// SPI HW
|
||||
spi_inst_t *hw_inst;
|
||||
uint miso_gpio; // SPI MISO GPIO number (not pin number)
|
||||
uint mosi_gpio;
|
||||
uint sck_gpio;
|
||||
uint baud_rate;
|
||||
|
||||
// Drive strength levels for GPIO outputs.
|
||||
// enum gpio_drive_strength { GPIO_DRIVE_STRENGTH_2MA = 0, GPIO_DRIVE_STRENGTH_4MA = 1, GPIO_DRIVE_STRENGTH_8MA = 2,
|
||||
// GPIO_DRIVE_STRENGTH_12MA = 3 }
|
||||
bool set_drive_strength;
|
||||
enum gpio_drive_strength mosi_gpio_drive_strength;
|
||||
enum gpio_drive_strength sck_gpio_drive_strength;
|
||||
|
||||
// State variables:
|
||||
uint tx_dma;
|
||||
uint rx_dma;
|
||||
dma_channel_config tx_dma_cfg;
|
||||
dma_channel_config rx_dma_cfg;
|
||||
irq_handler_t dma_isr;
|
||||
bool initialized;
|
||||
semaphore_t sem;
|
||||
mutex_t mutex;
|
||||
} spi_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// SPI DMA interrupts
|
||||
void __not_in_flash_func(spi_irq_handler)(spi_t *pSPI);
|
||||
|
||||
bool __not_in_flash_func(spi_transfer)(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length);
|
||||
void spi_lock(spi_t *pSPI);
|
||||
void spi_unlock(spi_t *pSPI);
|
||||
bool my_spi_init(spi_t *pSPI);
|
||||
void set_spi_dma_irq_channel(bool useChannel1, bool shared);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_PICO_LED
|
||||
# define USE_LED 1
|
||||
#endif
|
||||
|
||||
#if USE_LED
|
||||
# define LED_PIN 25
|
||||
# define LED_INIT() \
|
||||
{ \
|
||||
gpio_init(LED_PIN); \
|
||||
gpio_set_dir(LED_PIN, GPIO_OUT); \
|
||||
}
|
||||
# define LED_ON() gpio_put(LED_PIN, 1)
|
||||
# define LED_OFF() gpio_put(LED_PIN, 0)
|
||||
#else
|
||||
# define LED_ON()
|
||||
# define LED_OFF()
|
||||
# define LED_INIT()
|
||||
#endif
|
||||
|
||||
/* [] END OF FILE */
|
||||
Reference in New Issue
Block a user