Add sd card support + savegames

This commit is contained in:
Vincent Mistler
2023-02-13 17:38:14 +01:00
parent 447ba93878
commit c8ee224092
42 changed files with 28650 additions and 4 deletions
+116
View File
@@ -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 */
+42
View 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 */
+96
View 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);
}
/*-----------------------------------------------------------*/
+33
View File
@@ -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
+93
View File
@@ -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 */
+121
View 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 */
+42
View 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 1s. 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 */
+221
View 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 */
+93
View 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 */