Commit 8fc87403 by tatsukiishikawa

adding sd-card, lcd example

parent 3adfe119
Showing with 4901 additions and 0 deletions
# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
set(USERHOME $ENV{USERPROFILE})
else()
set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.2.0)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.2.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
include(${picoVscode})
endif()
# ====================================================================================
cmake_minimum_required(VERSION 3.13)
set(PICO_BOARD pico_w CACHE STRING "Board type")
set(PROGRAM_NAME main)
include(pico_sdk_import.cmake)
project(${PROGRAM_NAME} C CXX ASM)
# Initialise the Pico SDK
pico_sdk_init()
add_subdirectory(lib/config)
add_subdirectory(lib/lcd)
add_subdirectory(lib/font)
add_subdirectory(lib/sd_card_driver build)
include_directories(./lib/config)
include_directories(./lib/lcd)
include_directories(./lib/font)
# Add executable. Default name is the project name, version 0.1
add_executable(${PROGRAM_NAME}
config/hw_config.c
main.cpp
)
# https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf
target_compile_definitions(${PROGRAM_NAME} PRIVATE
PICO_STACK_SIZE=0x1000
PICO_CORE1_STACK_SIZE=0x800
)
target_compile_options(${PROGRAM_NAME} PUBLIC
-Wall
-Wextra
-Wshadow
-Wstack-usage=2048
-fanalyzer
)
add_compile_definitions(
PARAM_ASSERTIONS_ENABLE_ALL=1
PICO_MALLOC_PANIC=1
PICO_USE_STACK_GUARDS=1
)
set_property(TARGET ${PROGRAM_NAME} APPEND_STRING PROPERTY LINK_FLAGS
"-Wl,--print-memory-usage"
)
pico_set_program_name(${PROGRAM_NAME} "${PROGRAM_NAME}")
pico_set_program_version(${PROGRAM_NAME} "0.1")
pico_enable_stdio_uart(${PROGRAM_NAME} 0)
pico_enable_stdio_usb(${PROGRAM_NAME} 1)
target_include_directories(${PROGRAM_NAME} PUBLIC
include/
)
target_link_libraries(${PROGRAM_NAME}
lcd
font
config
pico_stdlib
hardware_i2c
pico_multicore
pico_util
sd_custom_driver
hardware_clocks
hardware_adc
hardware_pio
)
# Add the standard include files to the build
target_include_directories(${PROGRAM_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
pico_add_extra_outputs(${PROGRAM_NAME})
/* hw_config.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.
*/
/*
This file should be tailored to match the hardware design.
There should be one element of the spi[] array for each RP2040 hardware SPI used.
There should be one element of the spi_ifs[] array for each SPI interface object.
* Each element of spi_ifs[] must point to an spi_t instance with member "spi".
There should be one element of the sdio_ifs[] array for each SDIO interface object.
There should be one element of the sd_cards[] array for each SD card slot.
* Each element of sd_cards[] must point to its interface with spi_if_p or sdio_if_p.
*/
/* Hardware configuration for Pico SD Card Development Board
See https://oshwlab.com/carlk3/rp2040-sd-card-dev
See https://docs.google.com/spreadsheets/d/1BrzLWTyifongf_VQCc2IpJqXWtsrjmG7KnIbSBy-CPU/edit?usp=sharing,
tab "Dev Brd", for pin assignments assumed in this configuration file.
*/
#include <assert.h>
//
#include "hw_config.h"
// Hardware Configuration of SPI "objects"
// Note: multiple SD cards can be driven by one SPI if they use different slave
// selects (or "chip selects").
// Using SPI1 to avoid conflicts with CYW43439 WiFi chip (which uses SPI0 internally on Pico W)
static spi_t spis[] = {
{ // SPI1 for SD card (avoiding CYW43439 conflict)
.hw_inst = spi1, // Use SPI1 instead of SPI0
.sck_gpio = 10, // SPI1 SCK (GP10)
.mosi_gpio = 11, // SPI1 MOSI (GP11) - TX
.miso_gpio = 12, // SPI1 MISO (GP12) - RX
.set_drive_strength = true,
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
.no_miso_gpio_pull_up = true,
.baud_rate = 125 * 1000 * 1000 / 64 // ~2 MHz startup
}
};
/* SPI Interfaces */
static sd_spi_if_t spi_ifs[] = {
{
.spi = &spis[0],
.ss_gpio = 13 // CS (Chip Select) pin for SD card
}
};
/* SDIO Interfaces */
/*
Pins CLK_gpio, D1_gpio, D2_gpio, and D3_gpio are at offsets from pin D0_gpio.
The offsets are determined by sd_driver\SDIO\rp2040_sdio.pio.
CLK_gpio = (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32;
As of this writing, SDIO_CLK_PIN_D0_OFFSET is 30,
which is -2 in mod32 arithmetic, so:
CLK_gpio = D0_gpio -2.
D1_gpio = D0_gpio + 1;
D2_gpio = D0_gpio + 2;
D3_gpio = D0_gpio + 3;
*/
static sd_sdio_if_t sdio_ifs[] = {
{
.CMD_gpio = 18,
.D0_gpio = 19,
.CLK_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
.CMD_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D0_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D1_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D2_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.D3_gpio_drive_strength = GPIO_DRIVE_STRENGTH_4MA,
.SDIO_PIO = pio0, // Use PIO0 (CYW43 uses PIO internally, but PIO0 is safer for SDIO)
.DMA_IRQ_num = DMA_IRQ_1, // Use DMA_IRQ_1 to avoid conflict with WiFi (which may use DMA_IRQ_0)
.baud_rate = 125 * 1000 * 1000 / 7
}
};
/* Hardware Configuration of the SD Card "objects"
These correspond to SD card sockets
*/
static sd_card_t sd_cards[] = {
{ // sd_cards[0]: Socket sd0 using SDIO interface
.type = SD_IF_SDIO,
.spi_if_p = &sdio_ifs[0],
.use_card_detect = false,
.card_detect_gpio = 9,
.card_detected_true = 0, // What the GPIO read returns when a card is
// present.
.card_detect_use_pull = true,
.card_detect_pull_hi = true
}
};
/* ********************************************************************** */
size_t sd_get_num() { return count_of(sd_cards); }
sd_card_t *sd_get_by_num(size_t num) {
assert(num < sd_get_num());
if (num < sd_get_num()) {
return &sd_cards[num];
} else {
return NULL;
}
}
/* [] END OF FILE */
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_Config_SRCS 变量
aux_source_directory(. DIR_CONFIG_SRCS)
# 生成链接库
add_library(config ${DIR_CONFIG_SRCS})
target_link_libraries(config PUBLIC pico_stdlib hardware_spi hardware_pwm)
/*****************************************************************************
* | File : DEV_Config.c
* | Author : Waveshare team
* | Function : Show SDcard BMP picto LCD
* | Info :
* Provide the hardware underlying interface
*----------------
* | This version: V1.0
* | Date : 2018-01-11
* | Info : Basic version
*
******************************************************************************/
#include "DEV_Config.h"
#include "pico/stdlib.h"
void DEV_Digital_Write(UWORD Pin, UBYTE Value)
{
gpio_put(Pin, Value);
}
UBYTE DEV_Digital_Read(UWORD Pin)
{
return gpio_get(Pin);
}
/**
* GPIO Mode
**/
void DEV_GPIO_Mode(UWORD Pin, UWORD Mode)
{
gpio_init(Pin);
if(Mode == 0 || Mode == GPIO_IN) {
gpio_set_dir(Pin, GPIO_IN);
} else {
gpio_set_dir(Pin, GPIO_OUT);
}
}
void DEV_GPIO_Init(void)
{
DEV_GPIO_Mode(LCD_RST_PIN,GPIO_OUT);
DEV_GPIO_Mode(LCD_DC_PIN, GPIO_OUT);
//DEV_GPIO_Mode(LCD_BKL_PIN, GPIO_OUT);
DEV_GPIO_Mode(LCD_CS_PIN, GPIO_OUT);
DEV_GPIO_Mode(TP_CS_PIN,GPIO_OUT);
DEV_GPIO_Mode(TP_IRQ_PIN,GPIO_IN);
DEV_GPIO_Mode(SD_CS_PIN,GPIO_OUT);
//gpio_set_pulls(TP_IRQ_PIN,true,false);
DEV_Digital_Write(TP_CS_PIN, 1);
DEV_Digital_Write(LCD_CS_PIN, 1);
//DEV_Digital_Write(LCD_BKL_PIN, 0);
DEV_Digital_Write(SD_CS_PIN, 1);
gpio_set_function(LCD_BKL_PIN, GPIO_FUNC_PWM);
}
/********************************************************************************
function: System Init
note:
Initialize the communication method
********************************************************************************/
uint8_t System_Init(void)
{
stdio_init_all();
DEV_GPIO_Init();
spi_init(SPI_PORT,5000000);
gpio_set_function(LCD_CLK_PIN,GPIO_FUNC_SPI);
gpio_set_function(LCD_MOSI_PIN,GPIO_FUNC_SPI);
gpio_set_function(LCD_MISO_PIN,GPIO_FUNC_SPI);
return 0;
}
void System_Exit(void)
{
}
/*********************************************
function: Hardware interface
note:
SPI4W_Write_Byte(value) :
Register hardware SPI
*********************************************/
uint8_t SPI4W_Write_Byte(uint8_t value)
{
uint8_t rxDat;
spi_write_read_blocking(spi1,&value,&rxDat,1);
return rxDat;
}
uint8_t SPI4W_Read_Byte(uint8_t value)
{
return SPI4W_Write_Byte(value);
}
/********************************************************************************
function: Delay function
note:
Driver_Delay_ms(xms) : Delay x ms
Driver_Delay_us(xus) : Delay x us
********************************************************************************/
void Driver_Delay_ms(uint32_t xms)
{
sleep_ms(xms);
}
void Driver_Delay_us(uint32_t xus)
{
int j;
for(j=xus; j > 0; j--);
}
/*****************************************************************************
* | File : DEV_Config.c
* | Author : Waveshare team
* | Function : GPIO Function
* | Info :
* Provide the hardware underlying interface
*----------------
* | This version: V1.0
* | Date : 2018-01-11
* | Info : Basic version
*
******************************************************************************/
#ifndef _DEV_CONFIG_H_
#define _DEV_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/pwm.h"
#include "stdio.h"
#define UBYTE uint8_t
#define UWORD uint16_t
#define UDOUBLE uint32_t
#define LCD_RST_PIN 15
#define LCD_DC_PIN 8
#define LCD_CS_PIN 9
#define LCD_CLK_PIN 10
#define LCD_BKL_PIN 13
#define LCD_MOSI_PIN 11
#define LCD_MISO_PIN 12
#define TP_CS_PIN 16
#define TP_IRQ_PIN 17
#define SD_CS_PIN 22
#define SPI_PORT spi1
#define MAX_BMP_FILES 25
/*------------------------------------------------------------------------------------------------------*/
void DEV_Digital_Write(UWORD Pin, UBYTE Value);
UBYTE DEV_Digital_Read(UWORD Pin);
void DEV_GPIO_Mode(UWORD Pin, UWORD Mode);
void DEV_GPIO_Init(void);
uint8_t System_Init(void);
void System_Exit(void);
uint8_t SPI4W_Write_Byte(uint8_t value);
uint8_t SPI4W_Read_Byte(uint8_t value);
void Driver_Delay_ms(uint32_t xms);
void Driver_Delay_us(uint32_t xus);
#ifdef __cplusplus
}
#endif
#endif
aux_source_directory(. DIR_font_SRCS)
add_library(font ${DIR_font_SRCS})
target_link_libraries(font PUBLIC)
/**
******************************************************************************
* @file fonts.h
* @author MCD Application Team
* @version V1.0.0
* @date 18-February-2014
* @brief Header for fonts.c file
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __FONTS_H
#define __FONTS_H
/* Max size of bitmap will based on a font24 (17x24) */
#define MAX_HEIGHT_FONT 24
#define MAX_WIDTH_FONT 17
#define OFFSET_BITMAP 54
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
typedef struct _tFont
{
const uint8_t *table;
uint16_t Width;
uint16_t Height;
} sFONT;
extern sFONT Font24;
extern sFONT Font20;
extern sFONT Font16;
extern sFONT Font12;
extern sFONT Font8;
#ifdef __cplusplus
}
#endif
#endif /* __FONTS_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
aux_source_directory(. DIR_LCD_SRCS)
include_directories(../config)
include_directories(../font)
add_library(lcd ${DIR_LCD_SRCS})
target_link_libraries(lcd PUBLIC config font pico_stdlib)
/*****************************************************************************
* | File : LCD_Driver.h
* | Author : Waveshare team
* | Function : ILI9486 Drive function
* | Info :
* Image scanning:
* Please use progressive scanning to generate images or fonts
*----------------
* | This version: V1.0
* | Date : 2018-01-11
* | Info : Basic version
*
******************************************************************************/
/**************************Intermediate driver layer**************************/
#ifndef __LCD_DRIVER_H
#define __LCD_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "DEV_Config.h"
#define LCD_2_8 0x52
#define LCD_3_5 0x00
#define COLOR uint16_t //The variable type of the color (unsigned short)
#define POINT uint16_t //The type of coordinate (unsigned short)
#define LENGTH uint16_t //The type of coordinate (unsigned short)
/********************************************************************************
function:
Define the full screen height length of the display
********************************************************************************/
#define LCD_X_MAXPIXEL 480 //LCD width maximum memory
#define LCD_Y_MAXPIXEL 320 //LCD height maximum memory
#define LCD_X 0
#define LCD_Y 0
#define LCD_3_5_WIDTH (LCD_X_MAXPIXEL - 2 * LCD_X) //LCD width
#define LCD_3_5_HEIGHT LCD_Y_MAXPIXEL //LCD height
#define LCD_2_8_WIDTH 240 //LCD width
#define LCD_2_8_HEIGHT 320
/********************************************************************************
function:
scanning method
********************************************************************************/
typedef enum {
L2R_U2D = 0, //The display interface is displayed , left to right, up to down
L2R_D2U ,
R2L_U2D ,
R2L_D2U ,
U2D_L2R ,
U2D_R2L ,
D2U_L2R ,
D2U_R2L ,
} LCD_SCAN_DIR;
#define SCAN_DIR_DFT D2U_L2R //Default scan direction = L2R_U2D
/********************************************************************************
function:
Defines the total number of rows in the display area
********************************************************************************/
typedef struct {
LENGTH LCD_Dis_Column; //COLUMN
LENGTH LCD_Dis_Page; //PAGE
LCD_SCAN_DIR LCD_Scan_Dir;
POINT LCD_X_Adjust; //LCD x actual display position calibration
POINT LCD_Y_Adjust; //LCD y actual display position calibration
} LCD_DIS;
/********************************************************************************
function:
Macro definition variable name
********************************************************************************/
void LCD_Init(LCD_SCAN_DIR LCD_ScanDir, uint16_t LCD_BLval);
void LCD_SetGramScanWay(LCD_SCAN_DIR Scan_dir);
void LCD_WriteReg(uint8_t Reg);
void LCD_WriteData(uint16_t Data);
void LCD_SetWindow(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend);
void LCD_SetCursor(POINT Xpoint, POINT Ypoint);
void LCD_SetColor(COLOR Color ,POINT Xpoint, POINT Ypoint);
void LCD_SetPointlColor(POINT Xpoint, POINT Ypoint, COLOR Color);
void LCD_SetArealColor(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend,COLOR Color);
void LCD_Clear(COLOR Color);
uint8_t LCD_Read_Id(void);
void LCD_SetBackLight(uint16_t value);
#ifdef __cplusplus
}
#endif
#endif
/*****************************************************************************
* | File : LCD_GUI.h
* | Author : Waveshare team
* | Function : Achieve drawing: draw points, lines, boxes, circles and
* their size, solid dotted line, solid rectangle hollow
* rectangle, solid circle hollow circle.
* | Info :
* Achieve display characters: Display a single character, string, number
* Achieve time display: adaptive size display time minutes and seconds
*----------------
* | This version: V1.0
* | Date : 2017-08-16
* | Info : Basic version
*
******************************************************************************/
/****************************Upper application layer**************************/
#ifndef __LCD_GUI_H
#define __LCD_GUI_H
#ifdef __cplusplus
extern "C" {
#endif
#include "LCD_Driver.h"
#include "fonts.h"
#define LOW_Speed_Show 0
#define HIGH_Speed_Show 1
/********************************************************************************
function:
dot pixel
********************************************************************************/
typedef enum {
DOT_PIXEL_1X1 = 1, // dot pixel 1 x 1
DOT_PIXEL_2X2 , // dot pixel 2 X 2
DOT_PIXEL_3X3 , // dot pixel 3 X 3
DOT_PIXEL_4X4 , // dot pixel 4 X 4
DOT_PIXEL_5X5 , // dot pixel 5 X 5
DOT_PIXEL_6X6 , // dot pixel 6 X 6
DOT_PIXEL_7X7 , // dot pixel 7 X 7
DOT_PIXEL_8X8 , // dot pixel 8 X 8
} DOT_PIXEL;
#define DOT_PIXEL_DFT DOT_PIXEL_1X1 //Default dot pilex
/********************************************************************************
function:
dot Fill style
********************************************************************************/
typedef enum {
DOT_FILL_AROUND = 1, // dot pixel 1 x 1
DOT_FILL_RIGHTUP , // dot pixel 2 X 2
} DOT_STYLE;
#define DOT_STYLE_DFT DOT_FILL_AROUND //Default dot pilex
/********************************************************************************
function:
solid line and dotted line
********************************************************************************/
typedef enum {
LINE_SOLID = 0,
LINE_DOTTED,
} LINE_STYLE;
/********************************************************************************
function:
DRAW Internal fill
********************************************************************************/
typedef enum {
DRAW_EMPTY = 0,
DRAW_FULL,
} DRAW_FILL;
/********************************************************************************
function:
time
********************************************************************************/
typedef struct {
uint16_t Year; //0000
uint8_t Month; //1 - 12
uint8_t Day; //1 - 30
uint8_t Hour; //0 - 23
uint8_t Min; //0 - 59
uint8_t Sec; //0 - 59
} DEV_TIME;
extern DEV_TIME sDev_time;
/********************************************************************************
function:
Defines commonly used colors for the display
********************************************************************************/
#define LCD_BACKGROUND WHITE //Default background color
#define FONT_BACKGROUND WHITE //Default font background color
#define FONT_FOREGROUND GRED //Default font foreground color
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40
#define BRRED 0XFC07
#define GRAY 0X8430
/********************************************************************************
function:
Macro definition variable name
********************************************************************************/
//Clear
void GUI_Clear(COLOR Color);
//Drawing
void GUI_DrawPoint(POINT Xpoint, POINT Ypoint, COLOR Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_FillWay);
void GUI_DrawLine(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend, COLOR Color, LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel);
void GUI_DrawRectangle(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend, COLOR Color, DRAW_FILL Filled , DOT_PIXEL Dot_Pixel );
void GUI_DrawCircle(POINT X_Center, POINT Y_Center, LENGTH Radius, COLOR Color, DRAW_FILL Draw_Fill , DOT_PIXEL Dot_Pixel );
//pic
void GUI_Disbitmap(POINT Xpoint, POINT Ypoint, const unsigned char *pMap, POINT Width, POINT Height);
void GUI_DisGrayMap(POINT Xpoint, POINT Ypoint, const unsigned char *pBmp);
//Display string
void GUI_DisChar(POINT Xstart, POINT Ystart, const char Acsii_Char, sFONT* Font, COLOR Color_Background, COLOR Color_Foreground);
void GUI_DisString_EN(POINT Xstart, POINT Ystart, const char * pString, sFONT* Font, COLOR Color_Background, COLOR Color_Foreground );
void GUI_DisNum(POINT Xpoint, POINT Ypoint, int32_t Nummber, sFONT* Font, COLOR Color_Background, COLOR Color_Foreground );
void GUI_Showtime(POINT Xstart, POINT Ystart, POINT Xend, POINT Yend, DEV_TIME *pTime, COLOR Color);
//show
void GUI_Show(void);
#ifdef __cplusplus
}
#endif
#endif
#ifndef __LCD_TOUCH_H_
#define __LCD_TOUCH_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "DEV_Config.h"
#include "LCD_Driver.h"
#include "LCD_GUI.h"
#include <math.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/float.h"
#include "pico/multicore.h"
#define TP_PRESS_DOWN 0x80
#define TP_PRESSED 0x40
#define DATA_READY_FLAG 0xABCDEF01
#define WRITE_FAILED_FLAG 0x12345678
#define TASK_COMPLETE_FLAG 0xDEADBEAF
// ---- Capture area (the black box) ------------------------------------------
#define BOX_X0 100
#define BOX_Y0 50
#define BOX_X1 380 // right edge (exclusive in our math)
#define BOX_Y1 290 // bottom edge (exclusive in our math)
#define BOX_W (BOX_X1 - BOX_X0) // 280
#define BOX_H (BOX_Y1 - BOX_Y0) // 240
//Touch screen structure
typedef struct {
POINT Xpoint0;
POINT Ypoint0;
POINT Xpoint;
POINT Ypoint;
uint8_t chStatus;
uint8_t chType;
int16_t iXoff;
int16_t iYoff;
float fXfac;
float fYfac;
//Select the coordinates of the XPT2046 touch \
screen relative to what scan direction
LCD_SCAN_DIR TP_Scan_Dir;
}TP_DEV;
//Brush structure
typedef struct{
POINT Xpoint;
POINT Ypoint;
COLOR Color;
DOT_PIXEL DotPixel;
}TP_DRAW;
typedef struct {
size_t data_len;
uint8_t *data;
}TP_DATA;
void TP_GetAdFac(void);
void TP_Adjust(void);
void TP_Dialog(void);
void TP_Save(void);
void TP_DrawBoard(void);
void TP_Init(LCD_SCAN_DIR Lcd_ScanDir, TP_DATA *tp_data_ptr, mutex_t *mutex);
#ifdef __cplusplus
}
#endif
#endif
add_library(sd_custom_driver INTERFACE)
pico_generate_pio_header(sd_custom_driver ${CMAKE_CURRENT_LIST_DIR}/sd_driver/SDIO/rp2040_sdio.pio)
target_compile_definitions(sd_custom_driver INTERFACE
PICO_MAX_SHARED_IRQ_HANDLERS=8u
)
# target_compile_options(sd_custom_driver INTERFACE -ffile-prefix-map=${CMAKE_CURRENT_LIST_DIR}=)
target_sources(sd_custom_driver INTERFACE
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ff.c
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ffsystem.c
${CMAKE_CURRENT_LIST_DIR}/ff15/source/ffunicode.c
${CMAKE_CURRENT_LIST_DIR}/sd_driver/dma_interrupts.c
${CMAKE_CURRENT_LIST_DIR}/sd_driver/sd_card.c
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SDIO/rp2040_sdio.c
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SDIO/sd_card_sdio.c
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SPI/my_spi.c
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SPI/sd_card_spi.c
${CMAKE_CURRENT_LIST_DIR}/sd_driver/SPI/sd_spi.c
${CMAKE_CURRENT_LIST_DIR}/src/crash.c
${CMAKE_CURRENT_LIST_DIR}/src/crc.c
${CMAKE_CURRENT_LIST_DIR}/src/f_util.c
${CMAKE_CURRENT_LIST_DIR}/src/ff_stdio.c
${CMAKE_CURRENT_LIST_DIR}/src/file_stream.c
${CMAKE_CURRENT_LIST_DIR}/src/glue.c
${CMAKE_CURRENT_LIST_DIR}/src/my_debug.c
${CMAKE_CURRENT_LIST_DIR}/src/my_rtc.c
${CMAKE_CURRENT_LIST_DIR}/src/sd_timeouts.c
${CMAKE_CURRENT_LIST_DIR}/src/util.c
)
target_include_directories(sd_custom_driver INTERFACE
ff15/source
sd_driver
include
)
target_link_libraries(sd_custom_driver INTERFACE
hardware_dma
hardware_pio
hardware_spi
pico_aon_timer
pico_stdlib
cmsis_core
)
FatFs Module Source Files R0.15
FILES
00readme.txt This file.
00history.txt Revision history.
ff.c FatFs module.
ffconf.h Configuration file of FatFs module.
ff.h Common include file for FatFs and application module.
diskio.h Common include file for FatFs and disk I/O module.
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
ffunicode.c Optional Unicode utility functions.
ffsystem.c An example of optional O/S related functions.
Low level disk I/O module is not included in this archive because the FatFs
module is only a generic file system layer and it does not depend on any specific
storage device. You need to provide a low level disk I/O module written to
control the storage device that attached to the target system.
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
/* Definitions of physical drive number for each drive */
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
int result;
switch (pdrv) {
case DEV_RAM :
result = RAM_disk_status();
// translate the reslut code here
return stat;
case DEV_MMC :
result = MMC_disk_status();
// translate the reslut code here
return stat;
case DEV_USB :
result = USB_disk_status();
// translate the reslut code here
return stat;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
int result;
switch (pdrv) {
case DEV_RAM :
result = RAM_disk_initialize();
// translate the reslut code here
return stat;
case DEV_MMC :
result = MMC_disk_initialize();
// translate the reslut code here
return stat;
case DEV_USB :
result = USB_disk_initialize();
// translate the reslut code here
return stat;
}
return STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
DRESULT res;
int result;
switch (pdrv) {
case DEV_RAM :
// translate the arguments here
result = RAM_disk_read(buff, sector, count);
// translate the reslut code here
return res;
case DEV_MMC :
// translate the arguments here
result = MMC_disk_read(buff, sector, count);
// translate the reslut code here
return res;
case DEV_USB :
// translate the arguments here
result = USB_disk_read(buff, sector, count);
// translate the reslut code here
return res;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
DRESULT res;
int result;
switch (pdrv) {
case DEV_RAM :
// translate the arguments here
result = RAM_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case DEV_MMC :
// translate the arguments here
result = MMC_disk_write(buff, sector, count);
// translate the reslut code here
return res;
case DEV_USB :
// translate the arguments here
result = USB_disk_write(buff, sector, count);
// translate the reslut code here
return res;
}
return RES_PARERR;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
int result;
switch (pdrv) {
case DEV_RAM :
// Process of the command for the RAM drive
return res;
case DEV_MMC :
// Process of the command for the MMC/SD card
return res;
case DEV_USB :
// Process of the command the USB drive
return res;
}
return RES_PARERR;
}
/*-----------------------------------------------------------------------/
/ Low level disk interface modlue include file (C)ChaN, 2019 /
/-----------------------------------------------------------------------*/
#ifndef _DISKIO_DEFINED
#define _DISKIO_DEFINED
#include "ff.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Status of Disk Functions */
typedef BYTE DSTATUS;
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
/*---------------------------------------*/
/* Prototypes for disk control functions */
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
/* Disk Status Bits (DSTATUS) */
#define STA_NOINIT 0x01 /* Drive not initialized */
#define STA_NODISK 0x02 /* No medium in the drive */
#define STA_PROTECT 0x04 /* Write protected */
/* Command code for disk_ioctrl fucntion */
/* Generic command (Used by FatFs) */
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
/* Generic command (Not used by FatFs) */
#define CTRL_POWER 5 /* Get/Set power status */
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
#define CTRL_EJECT 7 /* Eject media */
#define CTRL_FORMAT 8 /* Create physical format on the media */
/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE 10 /* Get card type */
#define MMC_GET_CSD 11 /* Get CSD */
#define MMC_GET_CID 12 /* Get CID */
#define MMC_GET_OCR 13 /* Get OCR */
#define MMC_GET_SDSTAT 14 /* Get SD status */
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
/* ATA/CF specific ioctl command */
#define ATA_GET_REV 20 /* Get F/W revision */
#define ATA_GET_MODEL 21 /* Get model name */
#define ATA_GET_SN 22 /* Get serial number */
#ifdef __cplusplus
}
#endif
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*------------------------------------------------------------------------*/
/* A Sample Code of User Provided OS Dependent Functions for FatFs */
/*------------------------------------------------------------------------*/
#include "ff.h"
#if FF_USE_LFN == 3 /* Use dynamic memory allocation */
/*------------------------------------------------------------------------*/
/* Allocate/Free a Memory Block */
/*------------------------------------------------------------------------*/
#include <stdlib.h> /* with POSIX API */
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
UINT msize /* Number of bytes to allocate */
)
{
return malloc((size_t)msize); /* Allocate a new memory block */
}
void ff_memfree (
void* mblock /* Pointer to the memory block to free (no effect if null) */
)
{
free(mblock); /* Free the memory block */
}
#endif
#if FF_FS_REENTRANT /* Mutal exclusion */
/*------------------------------------------------------------------------*/
/* Definitions of Mutex */
/*------------------------------------------------------------------------*/
#define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */
#if OS_TYPE == 0 /* Win32 */
#include <windows.h>
static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
#elif OS_TYPE == 1 /* uITRON */
#include "itron.h"
#include "kernel.h"
static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
#elif OS_TYPE == 2 /* uc/OS-II */
#include "includes.h"
static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */
#elif OS_TYPE == 3 /* FreeRTOS */
#include "FreeRTOS.h"
#include "semphr.h"
static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
#elif OS_TYPE == 4 /* CMSIS-RTOS */
#include "cmsis_os.h"
static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
#endif
/*------------------------------------------------------------------------*/
/* Create a Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount function to create a new mutex
/ or semaphore for the volume. When a 0 is returned, the f_mount function
/ fails with FR_INT_ERR.
*/
int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
Mutex[vol] = CreateMutex(NULL, FALSE, NULL);
return (int)(Mutex[vol] != INVALID_HANDLE_VALUE);
#elif OS_TYPE == 1 /* uITRON */
T_CMTX cmtx = {TA_TPRI,1};
Mutex[vol] = acre_mtx(&cmtx);
return (int)(Mutex[vol] > 0);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
Mutex[vol] = OSMutexCreate(0, &err);
return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 3 /* FreeRTOS */
Mutex[vol] = xSemaphoreCreateMutex();
return (int)(Mutex[vol] != NULL);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexDef(cmsis_os_mutex);
Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex));
return (int)(Mutex[vol] != NULL);
#endif
}
/*------------------------------------------------------------------------*/
/* Delete a Mutex */
/*------------------------------------------------------------------------*/
/* This function is called in f_mount function to delete a mutex or
/ semaphore of the volume created with ff_mutex_create function.
*/
void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
CloseHandle(Mutex[vol]);
#elif OS_TYPE == 1 /* uITRON */
del_mtx(Mutex[vol]);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err);
#elif OS_TYPE == 3 /* FreeRTOS */
vSemaphoreDelete(Mutex[vol]);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexDelete(Mutex[vol]);
#endif
}
/*------------------------------------------------------------------------*/
/* Request a Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on enter file functions to lock the volume.
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
*/
int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0);
#elif OS_TYPE == 1 /* uITRON */
return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK);
#elif OS_TYPE == 2 /* uC/OS-II */
OS_ERR err;
OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err));
return (int)(err == OS_NO_ERR);
#elif OS_TYPE == 3 /* FreeRTOS */
return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK);
#endif
}
/*------------------------------------------------------------------------*/
/* Release a Grant to Access the Volume */
/*------------------------------------------------------------------------*/
/* This function is called on leave file functions to unlock the volume.
*/
void ff_mutex_give (
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
)
{
#if OS_TYPE == 0 /* Win32 */
ReleaseMutex(Mutex[vol]);
#elif OS_TYPE == 1 /* uITRON */
unl_mtx(Mutex[vol]);
#elif OS_TYPE == 2 /* uC/OS-II */
OSMutexPost(Mutex[vol]);
#elif OS_TYPE == 3 /* FreeRTOS */
xSemaphoreGive(Mutex[vol]);
#elif OS_TYPE == 4 /* CMSIS-RTOS */
osMutexRelease(Mutex[vol]);
#endif
}
#endif /* FF_FS_REENTRANT */
This source diff could not be displayed because it is too large. You can view the blob instead.
/* crash.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.
*/
// Original from M0AGX (blog@m0agx.eu), "Preserving debugging breadcrumbs across
// reboots in Cortex-M,"
// https://m0agx.eu/2018/08/18/preserving-debugging-breadcrumbs-across-reboots-in-cortex-m/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
//
#include "pico/stdlib.h"
#ifdef __cplusplus
extern "C" {
#endif
/* The crash info section is at the beginning of the RAM,
* that is not initialized by the linker to preserve
* information across reboots.
*/
/**
* These are the positions of the fault frame elements in the
* fault frame structure.
*/
enum {
R0_Pos = 0, /**< The position of the R0 content in a fault structure */
R1_Pos, /**< The position of the R1 content in a fault structure */
R2_Pos, /**< The position of the R2 content in a fault structure */
R3_Pos, /**< The position of the R3 content in a fault structure */
R12_Pos, /**< The position of the R12 content in a fault structure */
LR_Pos, /**< The position of the LR content in a fault structure */
PC_Pos, /**< The position of the PC content in a fault structure */
PSR_Pos, /**< The position of the PSR content in a fault structure */
NUM_REGS, /**< The number of registers in the fault frame */
};
typedef struct {
uint32_t r0; /**< R0 register content */
uint32_t r1; /**< R1 register content */
uint32_t r2; /**< R2 register content */
uint32_t r3; /**< R3 register content */
uint32_t r12; /**< R12 register content */
uint32_t lr; /**< LR register content */
uint32_t pc; /**< PC register content */
uint32_t psr; /**< PSR register content */
} __attribute__((packed)) cy_stc_fault_frame_t;
typedef enum {
crash_magic_none = 0,
crash_magic_bootloader_entry = 0xB000B000,
crash_magic_hard_fault = 0xCAFEBABE,
crash_magic_debug_mon = 0x01020304,
crash_magic_reboot_requested = 0x00ABCDEF,
crash_magic_stack_overflow = 0x0BADBEEF,
crash_magic_assert = 0xDEBDEBDE
} crash_magic_t;
typedef struct {
char file[32];
int line;
char func[32];
char pred[32];
} crash_assert_t;
typedef struct {
uint32_t magic;
time_t timestamp;
union {
cy_stc_fault_frame_t cy_faultFrame;
crash_assert_t assert;
char calling_func[64];
};
uint8_t xor_checksum; // last to avoid including in calculation
} crash_info_t;
// Trick to find struct size at compile time:
// char (*__kaboom)[sizeof(crash_info_flash_t)] = 1;
// warning: initialization of 'char (*)[132]' from 'int' makes ...
void crash_handler_init();
const crash_info_t *crash_handler_get_info();
volatile const crash_info_t *crash_handler_get_info_flash();
#define SYSTEM_RESET() system_reset_func(__FUNCTION__)
void system_reset_func(char const *const func) __attribute__((noreturn));
void capture_assert(const char *file, int line, const char *func, const char *pred)
__attribute__((noreturn));
void capture_assert_case_not(const char *file, int line, const char *func, int v)
__attribute__((noreturn));
int dump_crash_info(crash_info_t const *const pCrashInfo, int next, char *const buf,
size_t const buf_sz);
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/* 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>
#include <stdint.h>
/**
* @brief Calculate the CRC7 checksum for the specified data block.
*
* This function calculates the CRC7 checksum for the specified data block
* using the lookup table defined in the m_Crc7Table array.
*
* @param data The data block to be checked.
* @param length The length of the data block in bytes.
* @return The calculated checksum.
*/
__attribute__((optimize("Ofast")))
static inline char crc7(uint8_t const *data, int const length) {
extern const char m_Crc7Table[];
char crc = 0;
for (int i = 0; i < length; i++) {
crc = m_Crc7Table[(crc << 1) ^ data[i]];
}
//Return the calculated checksum
return crc;
}
/**
* @brief Calculate the CRC16 checksum for the specified data block.
*
* This function calculates the CRC16 checksum for the specified data block
* using the lookup table defined in the m_Crc7Table array.
*
* @param data The data block to be checked.
* @param length The length of the data block in bytes.
* @return The calculated checksum.
*/
uint16_t crc16(uint8_t const *data, int const length);
#endif
/* [] END OF FILE */
/*
* delays.h
*
* Created on: Apr 25, 2022
* Author: carlk
*/
/* Using millis() or micros() for timeouts
For example,
uint32_t start = millis();
do {
// ...
}
} while (millis() - start < TIMEOUT);
There is no problem if the millis() counter wraps,
due to the properties of unsigned integer modulo arithmetic.
"A computation involving unsigned operands can never overflow,
because a result that cannot be represented by the resulting
unsigned integer type is reduced modulo the number that is
one greater than the largest value that can be represented
by the resulting type."
-- ISO/IEC 9899:1999 (E) §6.2.5/9
In other words, a uint32_t will wrap at 0 and UINT_MAX.
So, for example,
0x00000000 - 0xFFFFFFFF = 0x00000001
Remember that an unsigned integer will never be negative!
Be careful with comparisons. In the example above,
if 0x00000000 is the result of the counter wrapping,
and 0xFFFFFFFF is the start timestamp, a comparison like
millis() - start < TIMEOUT
is OK, but the following code is problematic if, say, the first call
to millis() returns 0xFFFFFFF0 and the second
call to millis() returns 0xFFFFFFFF:
uint32_t end = millis() + 100; // end = 0x00000054
while (millis() < end) // while (0xFFFFFFFF < 0x00000054)
*/
#pragma once
#include <stdint.h>
//
#include "pico/stdlib.h"
#if PICO_RP2040
#include "RP2040.h"
#else
#include "RP2350.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
static inline uint32_t millis() {
__COMPILER_BARRIER();
return time_us_64() / 1000;
__COMPILER_BARRIER();
}
static inline void delay_ms(uint32_t ulTime_ms) {
sleep_ms(ulTime_ms);
}
static inline uint64_t micros() {
__COMPILER_BARRIER();
return to_us_since_boot(get_absolute_time());
__COMPILER_BARRIER();
}
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/* f_util.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"
#ifdef __cplusplus
extern "C" {
#endif
const char *FRESULT_str(FRESULT i);
FRESULT delete_node (
TCHAR* path, /* Path name buffer with the sub-directory to delete */
UINT sz_buff, /* Size of path name buffer (items) */
FILINFO* fno /* Name read buffer */
);
void ls(const char *dir);
#ifdef __cplusplus
}
#endif
/* ff_stdio.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.
*/
// For compatibility with FreeRTOS+FAT API
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
//
#include "ff.h"
//
#include "my_debug.h"
#define BaseType_t int
#define FF_FILE FIL
#define ff_rewind f_rewind
#define pvPortMalloc malloc
#define vPortFree free
#define ffconfigMAX_FILENAME 250
#define configASSERT myASSERT
#define FF_PRINTF printf
#define pdFREERTOS_ERRNO_NONE 0
#define FF_EOF (-1)
#define FF_SEEK_SET 0
#define FF_SEEK_CUR 1
#define FF_SEEK_END 2
#define pdFALSE 0
#define pdTRUE 1
#define ff_filelength f_size
#define ff_feof f_eof
typedef struct FF_STAT {
uint32_t st_size; /* Size of the object in number of bytes. */
// uint16_t st_mode; /* The mode (attribute bits) of this
// file or directory. */
} FF_Stat_t;
typedef struct {
DIR dir;
FILINFO fileinfo;
const char *pcFileName;
uint32_t ulFileSize;
//uint8_t ucAttributes;
} FF_FindData_t;
FF_FILE *ff_fopen(const char *pcFile, const char *pcMode);
int ff_fclose(FF_FILE *pxStream);
int ff_stat(const char *pcFileName, FF_Stat_t *pxStatBuffer);
size_t ff_fwrite(const void *pvBuffer, size_t xSize, size_t xItems,
FF_FILE *pxStream);
size_t ff_fread(void *pvBuffer, size_t xSize, size_t xItems, FF_FILE *pxStream);
int ff_chdir(const char *pcDirectoryName);
char *ff_getcwd(char *pcBuffer, size_t xBufferLength);
int ff_mkdir(const char *pcPath);
int ff_fputc(int iChar, FF_FILE *pxStream);
int ff_fgetc(FF_FILE *pxStream);
int ff_rmdir(const char *pcDirectory);
int ff_remove(const char *pcPath);
long ff_ftell(FF_FILE *pxStream);
int ff_fseek(FF_FILE *pxStream, int iOffset, int iWhence);
int ff_findfirst(const char *pcDirectory, FF_FindData_t *pxFindData);
int ff_findnext( FF_FindData_t *pxFindData );
FF_FILE *ff_truncate( const char * pcFileName, long lTruncateSize );
int ff_seteof( FF_FILE *pxStream );
int ff_rename( const char *pcOldName, const char *pcNewName, int bDeleteIfExists );
char *ff_fgets(char *pcBuffer, size_t xCount, FF_FILE *pxStream);
/*
* file_stream.h
*
* Wraps a FreeRTOS+FAT FF_FILE in a standard I/O stream FILE
* to take advantage of the buffering provided by the standard I/O library
* for a tremendous speedup of random-sized writes (e.g., fprintf output).
*
* Created on: Jun 20, 2024
* Author: carlk
*/
#pragma once
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
FILE *open_file_stream( const char *pcFile, const char *pcMode );
#ifdef __cplusplus
}
#endif
/* 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 <stddef.h>
#include "sd_card.h"
#ifdef __cplusplus
extern "C" {
#endif
/* FatFS supports up to 10 logical drives. By default, each logical
drive is associated with the physical drive in same drive number. */
/* Return the number of physical drives (SD card sockets) in the configuration */
size_t sd_get_num();
/* Return a pointer to the SD card "object" at the given physical drive number.
(See http://elm-chan.org/fsw/ff/doc/filename.html#vol.)
Parameter `num` must be less than sd_get_num(). */
sd_card_t* sd_get_by_num(size_t num);
/* See http://elm-chan.org/fsw/ff/doc/config.html#str_volume_id */
#if FF_STR_VOLUME_ID
extern const char* VolumeStr[FF_VOLUMES]; /* User defined volume ID */
#endif
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/* my_debug.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>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/* USE_PRINTF
If this is defined and not zero,
these message output functions will use the Pico SDK's stdout.
*/
/* USE_DBG_PRINTF
If this is not defined or is zero or NDEBUG is defined,
DBG_PRINTF statements will be effectively stripped from the code.
*/
/* Single string output callbacks: send message output somewhere.
To use these, do not define the USE_PRINTF compile definition,
and override these "weak" functions by strongly implementing them in user code.
The weak implementations do nothing.
*/
void put_out_error_message(const char *s);
void put_out_info_message(const char *s);
void put_out_debug_message(const char *s);
// https://gcc.gnu.org/onlinedocs/gcc-3.2.3/cpp/Variadic-Macros.html
int error_message_printf(const char *func, int line, const char *fmt, ...)
__attribute__((format(__printf__, 3, 4)));
#ifndef EMSG_PRINTF
#define EMSG_PRINTF(fmt, ...) error_message_printf(__func__, __LINE__, fmt, ##__VA_ARGS__)
#endif
int error_message_printf_plain(const char *fmt, ...) __attribute__((format(__printf__, 1, 2)));
int debug_message_printf(const char *func, int line, const char *fmt, ...)
__attribute__((format(__printf__, 3, 4)));
#ifndef DBG_PRINTF
# if defined(USE_DBG_PRINTF) && USE_DBG_PRINTF // && !defined(NDEBUG)
# define DBG_PRINTF(fmt, ...) debug_message_printf(__func__, __LINE__, fmt, ##__VA_ARGS__)
# else
# define DBG_PRINTF(fmt, ...) (void)0
# endif
#endif
int info_message_printf(const char *fmt, ...) __attribute__((format(__printf__, 1, 2)));
#ifndef IMSG_PRINTF
#define IMSG_PRINTF(fmt, ...) info_message_printf(fmt, ##__VA_ARGS__)
#endif
void lock_printf();
void unlock_printf();
void my_assert_func(const char *file, int line, const char *func, const char *pred)
__attribute__((noreturn));
#ifdef NDEBUG /* required by ANSI standard */
# define myASSERT(__e) ((void)0)
#else
# define myASSERT(__e) \
{ ((__e) ? (void)0 : my_assert_func(__func__, __LINE__, __func__, #__e)); }
#endif
void assert_always_func(const char *file, int line, const char *func, const char *pred)
__attribute__((noreturn));
#define ASSERT_ALWAYS(__e) \
((__e) ? (void)0 : my_assert_func(__FILE__, __LINE__, __func__, #__e))
void assert_case_is(const char *file, int line, const char *func, int v, int expected)
__attribute__((noreturn));
#define ASSERT_CASE_IS(__v, __e) \
((__v == __e) ? (void)0 : assert_case_is(__FILE__, __LINE__, __func__, __v, __e))
void assert_case_not_func(const char *file, int line, const char *func, int v)
__attribute__((noreturn));
#define ASSERT_CASE_NOT(__v) (assert_case_not_func(__FILE__, __LINE__, __func__, __v))
#ifdef NDEBUG /* required by ANSI standard */
#define DBG_ASSERT_CASE_NOT(__e) ((void)0)
#else
#define DBG_ASSERT_CASE_NOT(__v) (assert_case_not_func(__FILE__, __LINE__, __func__, __v))
#endif
static inline void dump_bytes(size_t num, uint8_t bytes[]) {
(void)num;
(void)bytes;
DBG_PRINTF(" ");
for (size_t j = 0; j < 16; ++j) {
DBG_PRINTF("%02hhx", j);
if (j < 15)
DBG_PRINTF(" ");
else {
DBG_PRINTF("\n");
}
}
for (size_t i = 0; i < num; i += 16) {
DBG_PRINTF("%04x ", i);
for (size_t j = 0; j < 16 && i + j < num; ++j) {
DBG_PRINTF("%02hhx", bytes[i + j]);
if (j < 15)
DBG_PRINTF(" ");
else {
DBG_PRINTF("\n");
}
}
}
DBG_PRINTF("\n");
}
void dump8buf(char *buf, size_t buf_sz, uint8_t *pbytes, size_t nbytes);
void hexdump_8(const char *s, const uint8_t *pbytes, size_t nbytes);
bool compare_buffers_8(const char *s0, const uint8_t *pbytes0, const char *s1,
const uint8_t *pbytes1, const size_t nbytes);
// sz is size in BYTES!
void hexdump_32(const char *s, const uint32_t *pwords, size_t nwords);
// sz is size in BYTES!
bool compare_buffers_32(const char *s0, const uint32_t *pwords0, const char *s1,
const uint32_t *pwords1, const size_t nwords);
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/* my_rtc.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
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
extern time_t epochtime;
void time_init();
#ifdef __cplusplus
}
#endif
/* rtc.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
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
extern time_t epochtime;
void time_init();
#ifdef __cplusplus
}
#endif
#pragma once
#include <stdint.h>
typedef struct {
uint32_t sd_command;
unsigned sd_command_retries;
unsigned sd_lock;
unsigned sd_spi_read;
unsigned sd_spi_write;
unsigned sd_spi_write_read;
unsigned spi_lock;
unsigned rp2040_sdio_command_R1;
unsigned rp2040_sdio_command_R2;
unsigned rp2040_sdio_command_R3;
unsigned rp2040_sdio_rx_poll;
unsigned rp2040_sdio_tx_poll;
unsigned sd_sdio_begin;
unsigned sd_sdio_stopTransmission;
} sd_timeouts_t;
extern sd_timeouts_t sd_timeouts;
/* util.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 <stddef.h>
#include <stdint.h>
#include <string.h>
//
#if PICO_RP2040
#include "RP2040.h"
#else
#include "RP2350.h"
#endif
//
#include "my_debug.h"
#ifdef __cplusplus
extern "C" {
#endif
// Greatest Common Divisor: Euclidian Algorithm
// https://www.freecodecamp.org/news/euclidian-gcd-algorithm-greatest-common-divisor/
int gcd(int a,int b);
typedef int (*printer_t)(const char* format, ...);
// works with negative index
static inline int wrap_ix(int index, int n)
{
return ((index % n) + n) % n;
}
// Calculate arr indices with wrap around (+ and -)
static inline int mod_floor(int a, int n) {
return ((a % n) + n) % n;
}
__attribute__((always_inline)) static inline uint32_t calculate_checksum(uint32_t const *p, size_t const size){
uint32_t checksum = 0;
for (uint32_t i = 0; i < (size/sizeof(uint32_t))-1; i++){
checksum ^= *p;
p++;
}
return checksum;
}
static inline void ext_str(size_t const data_sz,
uint8_t const data[],
size_t const msb,
size_t const lsb,
size_t const buf_sz,
char buf[]) {
memset(buf, 0, buf_sz);
size_t size = (1 + msb - lsb) / 8; // bytes
size_t byte = (data_sz - 1) - (msb / 8);
for (uint32_t i = 0; i < size; i++) {
myASSERT(i < buf_sz);
myASSERT(byte < data_sz);
buf[i] = data[byte++];
}
}
static inline uint32_t ext_bits(size_t n_src_bytes, unsigned char const *data, int msb, int lsb) {
uint32_t bits = 0;
uint32_t size = 1 + msb - lsb;
for (uint32_t i = 0; i < size; i++) {
uint32_t position = lsb + i;
uint32_t byte = (n_src_bytes - 1) - (position >> 3);
uint32_t bit = position & 0x7;
uint32_t value = (data[byte] >> bit) & 1;
bits |= value << i;
}
return bits;
}
static inline uint32_t ext_bits16(unsigned char const *data, int msb, int lsb) {
return ext_bits(16, data, msb, lsb);
}
char const* uint8_binary_str(uint8_t number);
char const* uint_binary_str(unsigned int number);
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/**
* Copyright (c) 2011-2022 Bill Greiman
* This file is part of the SdFat library for SD memory cards.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef sd_sdio_h
#define sd_sdio_h
#include "sd_card.h"
/** Initialize the SD card.
* \return true for success or false for failure.
*/
bool sd_sdio_begin(sd_card_t *sd_card_p);
/** CMD6 Switch mode: Check Function Set Function.
* \param[in] arg CMD6 argument.
* \param[out] status return status data.
*
* \return true for success or false for failure.
*/
bool sd_sdio_cardCMD6(sd_card_t *sd_card_p, uint32_t arg, uint8_t *status);
/** Disable an SDIO card.
* not implemented.
*/
// void sd_sdio_end() {}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
uint32_t __attribute__((error("use sectorCount()"))) cardSize();
#endif // DOXYGEN_SHOULD_SKIP_THIS
/** Erase a range of sectors.
*
* \param[in] firstSector The address of the first sector in the range.
* \param[in] lastSector The address of the last sector in the range.
*
* \note This function requests the SD card to do a flash erase for a
* range of sectors. The data on the card after an erase operation is
* either 0 or 1, depends on the card vendor. The card must support
* single sector erase.
*
* \return true for success or false for failure.
*/
bool sd_sdio_erase(sd_card_t *sd_card_p, uint32_t firstSector, uint32_t lastSector);
/**
* \return code for the last error. See SdCardInfo.h for a list of error codes.
*/
uint8_t sd_sdio_errorCode() /* const */;
/** \return error data for last error. */
uint32_t sd_sdio_errorData() /* const */;
/** \return error line for last error. Tmp function for debug. */
uint32_t sd_sdio_errorLine() /* const */;
/**
* Check for busy with CMD13.
*
* \return true if busy else false.
*/
bool sd_sdio_isBusy();
/** \return the SD clock frequency in kHz. */
//uint32_t sd_sdio_kHzSdClk();
/**
* Read a 512 byte sector from an SD card.
*
* \param[in] sector Logical sector to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return true for success or false for failure.
*/
bool sd_sdio_readSector(sd_card_t *sd_card_p, uint32_t sector, uint8_t *dst);
/**
* Read multiple 512 byte sectors from an SD card.
*
* \param[in] sector Logical sector to be read.
* \param[in] ns Number of sectors to be read.
* \param[out] dst Pointer to the location that will receive the data.
* \return true for success or false for failure.
*/
bool sd_sdio_readSectors(sd_card_t *sd_card_p, uint32_t sector, uint8_t *dst, size_t ns);
// Read 512-bit SD status
bool rp2040_sdio_get_sd_status(sd_card_t *sd_card_p, uint8_t response[64]);
/** Read one data sector in a multiple sector read sequence
*
* \param[out] dst Pointer to the location for the data to be read.
*
* \return true for success or false for failure.
*/
bool sd_sdio_readData(sd_card_t *sd_card_p, uint8_t *dst);
/** Read OCR register.
*
* \param[out] ocr Value of OCR register.
* \return true for success or false for failure.
*/
bool sd_sdio_readOCR(sd_card_t *sd_card_p, uint32_t *ocr);
/** Read SCR register.
*
* \param[out] scr Value of SCR register.
* \return true for success or false for failure.
*/
// bool sd_sdio_readSCR(sd_card_t *sd_card_p, scr_t *scr);
/** Start a read multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
*
* \note This function is used with readData() and readStop() for optimized
* multiple sector reads. SPI chipSelect must be low for the entire sequence.
*
* \return true for success or false for failure.
*/
// bool sd_sdio_readStart(uint32_t sector);
/** Start a read multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
* \param[in] count Maximum sector count.
* \note This function is used with readData() and readStop() for optimized
* multiple sector reads. SPI chipSelect must be low for the entire sequence.
*
* \return true for success or false for failure.
*/
bool sd_sdio_readStart(sd_card_t *sd_card_p, uint32_t sector, uint32_t count);
/** End a read multiple sectors sequence.
*
* \return true for success or false for failure.
*/
bool sd_sdio_readStop(sd_card_t *sd_card_p);
/** \return SDIO card status. */
uint32_t sd_sdio_status(sd_card_t *sd_card_p);
/**
* Determine the size of an SD flash memory card.
*
* \return The number of 512 byte data sectors in the card
* or zero if an error occurs.
*/
uint32_t sd_sdio_sectorCount(sd_card_t *sd_card_p);
/**
* Send CMD12 to stop read or write.
*
* \param[in] blocking If true, wait for command complete.
*
* \return true for success or false for failure.
*/
bool sd_sdio_stopTransmission(sd_card_t *sd_card_p, bool blocking);
/** \return success if sync successful. Not for user apps. */
//bool sd_sdio_syncDevice(sd_card_t *sd_card_p);
/** Return the card type: SD V1, SD V2 or SDHC
* \return 0 - SD V1, 1 - SD V2, or 3 - SDHC.
*/
uint8_t sd_sdio_type(sd_card_t *sd_card_p) /* const */;
/**
* Writes a 512 byte sector to an SD card.
*
* \param[in] sector Logical sector to be written.
* \param[in] src Pointer to the location of the data to be written.
* \return true for success or false for failure.
*/
bool sd_sdio_writeSector(sd_card_t *sd_card_p, uint32_t sector, const uint8_t *src);
/**
* Write multiple 512 byte sectors to an SD card.
*
* \param[in] sector Logical sector to be written.
* \param[in] ns Number of sectors to be written.
* \param[in] src Pointer to the location of the data to be written.
* \return true for success or false for failure.
*/
bool sd_sdio_writeSectors(sd_card_t *sd_card_p, uint32_t sector, const uint8_t *src, size_t ns);
/** Write one data sector in a multiple sector write sequence.
* \param[in] src Pointer to the location of the data to be written.
* \return true for success or false for failure.
*/
bool sd_sdio_writeData(sd_card_t *sd_card_p, const uint8_t *src);
/** Start a write multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
*
* \note This function is used with writeData() and writeStop()
* for optimized multiple sector writes.
*
* \return true for success or false for failure.
*/
// bool sd_sdio_writeStart(sd_card_t *sd_card_p, uint32_t sector);
/** Start a write multiple sectors sequence.
*
* \param[in] sector Address of first sector in sequence.
* \param[in] count Maximum sector count.
* \note This function is used with writeData() and writeStop()
* for optimized multiple sector writes.
*
* \return true for success or false for failure.
*/
bool sd_sdio_writeStart(sd_card_t *sd_card_p, uint32_t sector, uint32_t count);
/** End a write multiple sectors sequence.
*
* \return true for success or false for failure.
*/
bool sd_sdio_writeStop(sd_card_t *sd_card_p);
void sd_sdio_ctor(sd_card_t *sd_card_p);
#endif // sd_sdio_h
// Platform-specific definitions for ZuluSCSI RP2040 hardware.
#pragma once
#include <stdint.h>
// #include <Arduino.h>
// #include "ZuluSCSI_platform_gpio.h"
// #include "scsiHostPhy.h"
#include "SdioCard.h"
#include "pico/stdlib.h"
#ifdef __cplusplus
extern "C" {
#endif
#define delayMicroseconds sleep_us
/* These are used in debug output and default SCSI strings */
extern const char *g_azplatform_name;
#define PLATFORM_NAME "ZuluSCSI RP2040"
#define PLATFORM_REVISION "2.0"
#define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_SYNC_10
#define PLATFORM_OPTIMAL_MIN_SD_WRITE_SIZE 32768
#define PLATFORM_OPTIMAL_MAX_SD_WRITE_SIZE 65536
#define PLATFORM_OPTIMAL_LAST_SD_WRITE_SIZE 8192
#define SD_USE_SDIO 1
#define PLATFORM_HAS_INITIATOR_MODE 1
// NOTE: The driver supports synchronous speeds higher than 10MB/s, but this
// has not been tested due to lack of fast enough SCSI adapter.
// #define PLATFORM_MAX_SCSI_SPEED S2S_CFG_SPEED_TURBO
// Debug logging function, can be used to print to e.g. serial port.
// May get called from interrupt handlers.
void azplatform_log(const char *s);
void azplatform_emergency_log_save();
#if 0
// Timing and delay functions.
// Arduino platform already provides these
// unsigned long millis(void);
void delay(unsigned long ms);
// Short delays, can be called from interrupt mode
static inline void delay_ns(unsigned long ns)
{
delayMicroseconds((ns + 999) / 1000);
}
#endif
// Approximate fast delay
static inline void delay_100ns()
{
asm volatile ("nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop");
}
// Initialize SD card and GPIO configuration
void azplatform_init();
// Initialization for main application, not used for bootloader
void azplatform_late_init();
// Disable the status LED
void azplatform_disable_led(void);
// Query whether initiator mode is enabled on targets with PLATFORM_HAS_INITIATOR_MODE
bool azplatform_is_initiator_mode_enabled();
// Setup soft watchdog if supported
void azplatform_reset_watchdog();
// Set callback that will be called during data transfer to/from SD card.
// This can be used to implement simultaneous transfer to SCSI bus.
typedef void (*sd_callback_t)(uint32_t bytes_complete);
void azplatform_set_sd_callback(sd_callback_t func, const uint8_t *buffer);
// Reprogram firmware in main program area.
#ifndef RP2040_DISABLE_BOOTLOADER
#define AZPLATFORM_BOOTLOADER_SIZE (128 * 1024)
#define AZPLATFORM_FLASH_TOTAL_SIZE (1024 * 1024)
#define AZPLATFORM_FLASH_PAGE_SIZE 4096
bool azplatform_rewrite_flash_page(uint32_t offset, uint8_t buffer[AZPLATFORM_FLASH_PAGE_SIZE]);
void azplatform_boot_to_main_firmware();
#endif
// ROM drive in the unused external flash area
#ifndef RP2040_DISABLE_ROMDRIVE
#define PLATFORM_HAS_ROM_DRIVE 1
// Check maximum available space for ROM drive in bytes
uint32_t azplatform_get_romdrive_maxsize();
// Read ROM drive area
bool azplatform_read_romdrive(uint8_t *dest, uint32_t start, uint32_t count);
// Reprogram ROM drive area
#define AZPLATFORM_ROMDRIVE_PAGE_SIZE 4096
bool azplatform_write_romdrive(const uint8_t *data, uint32_t start, uint32_t count);
#endif
// Parity lookup tables for write and read from SCSI bus.
// These are used by macros below and the code in scsi_accel_rp2040.cpp
extern const uint16_t g_scsi_parity_lookup[256];
extern const uint16_t g_scsi_parity_check_lookup[512];
// Below are GPIO access definitions that are used from scsiPhy.cpp.
// Write a single SCSI pin.
// Example use: SCSI_OUT(ATN, 1) sets SCSI_ATN to low (active) state.
#define SCSI_OUT(pin, state) \
*(state ? &sio_hw->gpio_clr : &sio_hw->gpio_set) = 1 << (SCSI_OUT_ ## pin)
// Read a single SCSI pin.
// Example use: SCSI_IN(ATN), returns 1 for active low state.
#define SCSI_IN(pin) \
((sio_hw->gpio_in & (1 << (SCSI_IN_ ## pin))) ? 0 : 1)
// Set pin directions for initiator vs. target mode
#define SCSI_ENABLE_INITIATOR() \
(sio_hw->gpio_oe_set = (1 << SCSI_OUT_ACK) | \
(1 << SCSI_OUT_ATN)), \
(sio_hw->gpio_oe_clr = (1 << SCSI_IN_IO) | \
(1 << SCSI_IN_CD) | \
(1 << SCSI_IN_MSG) | \
(1 << SCSI_IN_REQ))
// Enable driving of shared control pins
#define SCSI_ENABLE_CONTROL_OUT() \
(sio_hw->gpio_oe_set = (1 << SCSI_OUT_CD) | \
(1 << SCSI_OUT_MSG))
// Set SCSI data bus to output
#define SCSI_ENABLE_DATA_OUT() \
(sio_hw->gpio_clr = (1 << SCSI_DATA_DIR), \
sio_hw->gpio_oe_set = SCSI_IO_DATA_MASK)
// Write SCSI data bus, also sets REQ to inactive.
#define SCSI_OUT_DATA(data) \
gpio_put_masked(SCSI_IO_DATA_MASK | (1 << SCSI_OUT_REQ), \
g_scsi_parity_lookup[(uint8_t)(data)] | (1 << SCSI_OUT_REQ)), \
SCSI_ENABLE_DATA_OUT()
// Release SCSI data bus and REQ signal
#define SCSI_RELEASE_DATA_REQ() \
(sio_hw->gpio_oe_clr = SCSI_IO_DATA_MASK, \
sio_hw->gpio_set = (1 << SCSI_DATA_DIR) | (1 << SCSI_OUT_REQ))
// Release all SCSI outputs
#define SCSI_RELEASE_OUTPUTS() \
SCSI_RELEASE_DATA_REQ(), \
sio_hw->gpio_oe_clr = (1 << SCSI_OUT_CD) | \
(1 << SCSI_OUT_MSG), \
sio_hw->gpio_set = (1 << SCSI_OUT_IO) | \
(1 << SCSI_OUT_CD) | \
(1 << SCSI_OUT_MSG) | \
(1 << SCSI_OUT_RST) | \
(1 << SCSI_OUT_BSY) | \
(1 << SCSI_OUT_REQ) | \
(1 << SCSI_OUT_SEL)
// Read SCSI data bus
#define SCSI_IN_DATA() \
(~sio_hw->gpio_in & SCSI_IO_DATA_MASK) >> SCSI_IO_SHIFT
// SD card driver for SdFat
#ifdef SD_USE_SDIO
struct SdioConfig;
// extern SdioConfig g_sd_sdio_config;
// #define SD_CONFIG g_sd_sdio_config
#define SD_CONFIG_CRASH g_sd_sdio_config
#else
struct SdSpiConfig;
extern SdSpiConfig g_sd_spi_config;
#define SD_CONFIG g_sd_spi_config
#define SD_CONFIG_CRASH g_sd_spi_config
#endif
#ifdef __cplusplus
}
#endif
// SD card access using SDIO for RP2040 platform.
// This module contains the low-level SDIO bus implementation using
// the PIO peripheral. The high-level commands are in sd_card_sdio.cpp.
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "sd_card.h"
//FIXME: why?
typedef struct sd_card_t sd_card_t;
typedef
enum sdio_status_t {
SDIO_OK = 0,
SDIO_BUSY = 1,
SDIO_ERR_RESPONSE_TIMEOUT = 2, // Timed out waiting for response from card
SDIO_ERR_RESPONSE_CRC = 3, // Response CRC is wrong
SDIO_ERR_RESPONSE_CODE = 4, // Response command code does not match what was sent
SDIO_ERR_DATA_TIMEOUT = 5, // Timed out waiting for data block
SDIO_ERR_DATA_CRC = 6, // CRC for data packet is wrong
SDIO_ERR_WRITE_CRC = 7, // Card reports bad CRC for write
SDIO_ERR_WRITE_FAIL = 8, // Card reports write failure
} sdio_status_t;
#define SDIO_BLOCK_SIZE 512
#define SDIO_WORDS_PER_BLOCK (SDIO_BLOCK_SIZE / 4) // 128
// Maximum number of 512 byte blocks to transfer in one request
#define SDIO_MAX_BLOCKS 256
typedef enum sdio_transfer_state_t { SDIO_IDLE, SDIO_RX, SDIO_TX, SDIO_TX_WAIT_IDLE} sdio_transfer_state_t;
typedef struct sd_sdio_if_state_t {
bool resources_claimed;
uint32_t ocr; // Operating condition register from card
uint32_t rca; // Relative card address
int error_line;
sdio_status_t error;
uint32_t dma_buf[128];
int SDIO_DMA_CH;
int SDIO_DMA_CHB;
int SDIO_CMD_SM;
int SDIO_DATA_SM;
uint32_t pio_cmd_clk_offset;
uint32_t pio_data_rx_offset;
pio_sm_config pio_cfg_data_rx;
uint32_t pio_data_tx_offset;
pio_sm_config pio_cfg_data_tx;
sdio_transfer_state_t transfer_state;
uint32_t transfer_start_time;
uint32_t *data_buf;
uint32_t blocks_done; // Number of blocks transferred so far
uint32_t total_blocks; // Total number of blocks to transfer
uint32_t blocks_checksumed; // Number of blocks that have had CRC calculated
uint32_t checksum_errors; // Number of checksum errors detected
// Variables for block writes
uint64_t next_wr_block_checksum;
uint32_t end_token_buf[3]; // CRC and end token for write block
sdio_status_t wr_status;
uint32_t card_response;
// Variables for extended block writes
bool ongoing_wr_mlt_blk;
uint32_t wr_mlt_blk_cnt_sector;
// Variables for block reads
// This is used to perform DMA into data buffers and checksum buffers separately.
struct {
void * write_addr;
uint32_t transfer_count;
} dma_blocks[SDIO_MAX_BLOCKS * 2];
struct {
uint32_t top;
uint32_t bottom;
} received_checksums[SDIO_MAX_BLOCKS];
} sd_sdio_if_state_t;
// Execute a command that has 48-bit reply (response types R1, R6, R7)
// If response is NULL, does not wait for reply.
sdio_status_t rp2040_sdio_command_R1(sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint32_t *response);
// Execute a command that has 136-bit reply (response type R2)
// Response buffer should have space for 16 bytes (the 128 bit payload)
sdio_status_t rp2040_sdio_command_R2(const sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint8_t *response);
// Execute a command that has 48-bit reply but without CRC (response R3)
sdio_status_t rp2040_sdio_command_R3(sd_card_t *sd_card_p, uint8_t command, uint32_t arg, uint32_t *response);
// Start transferring data from SD card to memory buffer
sdio_status_t rp2040_sdio_rx_start(sd_card_t *sd_card_p, uint8_t *buffer, uint32_t num_blocks, size_t block_size);
// Check if reception is complete
// Returns SDIO_BUSY while transferring, SDIO_OK when done and error on failure.
sdio_status_t rp2040_sdio_rx_poll(sd_card_t *sd_card_p, size_t block_size_words);
// Start transferring data from memory to SD card
sdio_status_t rp2040_sdio_tx_start(sd_card_t *sd_card_p, const uint8_t *buffer, uint32_t num_blocks);
// Check if transmission is complete
sdio_status_t rp2040_sdio_tx_poll(sd_card_t *sd_card_p, uint32_t *bytes_complete /* = nullptr */);
// (Re)initialize the SDIO interface
bool rp2040_sdio_init(sd_card_t *sd_card_p, float clk_div);
void __not_in_flash_func(sdio_irq_handler)(sd_card_t *sd_card_p);
#ifdef __cplusplus
}
#endif
; RP2040 PIO program for implementing SD card access in SDIO mode
; Run "pioasm rp2040_sdio.pio rp2040_sdio.pio.h" to regenerate the C header from this.
; The RP2040 official work-in-progress code at
; https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
; may be useful reference, but this is independent implementation.
;
; For official SDIO specifications, refer to:
; https://www.sdcard.org/downloads/pls/
; "SDIO Physical Layer Simplified Specification Version 8.00"
; Clock settings
; For 3.3V communication the available speeds are:
; - Default speed: max. 25 MHz clock
; - High speed: max. 50 MHz clock
;
; From the default RP2040 clock speed of 125 MHz, the closest dividers
; are 3 for 41.7 MHz and 5 for 25 MHz. The CPU can apply further divider
; through state machine registers for the initial handshake.
;
; Because data is written on the falling edge and read on the rising
; edge, it is preferrable to have a long 0 state and short 1 state.
;.define CLKDIV 3
;.define CLKDIV 5
;.define D0 ((CLKDIV + 1) / 2 - 1)
;.define D1 (CLKDIV/2 - 1)
.define D0 1
.define D1 1
.define PUBLIC CLKDIV D0 + 1 + D1 + 1
; .define PUBLIC SDIO_CLK_GPIO 17
; This is relative to D0 GPIO number.
; The pin is selected by adding Index to the
; PINCTRL_IN_BASE configuration, modulo 32.
; This is used as a WAIT index, and must be between 4 and 31.
; (Offsets 0-3 are D0, D1, D2, and D3.)
.define PUBLIC SDIO_CLK_PIN_D0_OFFSET 18 ; (-2 in mod32 arithmetic)
; State machine 0 is used to:
; - generate continuous clock on SDIO_CLK
; - send CMD packets
; - receive response packets
;
; Pin mapping for this state machine:
; - Sideset : CLK
; - IN/OUT/SET : CMD
; - JMP_PIN : CMD
;
; The commands to send are put on TX fifo and must have two words:
; Word 0 bits 31-24: Number of bits in command minus one (usually 47)
; Word 0 bits 23-00: First 24 bits of the command packet, shifted out MSB first
; Word 1 bits 31-08: Last 24 bits of the command packet, shifted out MSB first
; Word 1 bits 07-00: Number of bits in response minus one (usually 47), or 0 if no response
;
; The response is put on RX fifo, starting with the MSB.
; Partial last word will be padded with zero bits at the top.
;
; The state machine EXECCTRL should be set so that STATUS indicates TX FIFO < 2
; and that AUTOPULL and AUTOPUSH are enabled.
.program sdio_cmd_clk
.side_set 1
mov OSR, NULL side 1 [D1] ; Make sure OSR is full of zeros to prevent autopull
wait_cmd:
mov Y, !STATUS side 0 [D0] ; Check if TX FIFO has data
jmp !Y wait_cmd side 1 [D1]
load_cmd:
out NULL, 32 side 0 [D0] ; Load first word (trigger autopull)
out X, 8 side 1 [D1] ; Number of bits to send
set pins, 1 side 0 [D0] ; Initial state of CMD is high
set pindirs, 1 side 1 [D1] ; Set SDIO_CMD as output
send_cmd:
out pins, 1 side 0 [D0] ; Write output on falling edge of CLK
jmp X-- send_cmd side 1 [D1]
prep_resp:
set pindirs, 0 side 0 [D0] ; Set SDIO_CMD as input
out X, 8 side 1 [D1] ; Get number of bits in response
nop side 0 [D0] ; For clock alignment
jmp !X resp_done side 1 [D1] ; Check if we expect a response
wait_resp:
nop side 0 [D0]
jmp PIN wait_resp side 1 [D1] ; Loop until SDIO_CMD = 0
; Note: input bits are read at the same time as we write CLK=0.
; Because the host controls the clock, the read happens before
; the card sees the falling clock edge. This gives maximum time
; for the data bit to settle.
read_resp:
in PINS, 1 side 0 [D0] ; Read input data bit
jmp X-- read_resp side 1 [D1] ; Loop to receive all data bits
resp_done:
push side 0 [D0] ; Push the remaining part of response
; State machine 1 is used to send and receive data blocks.
; Pin mapping for this state machine:
; - IN / OUT: SDIO_D0-D3
; - GPIO defined at beginning of this file: SDIO_CLK
; Data reception program
; This program will wait for initial start of block token and then
; receive a data block. The application must set number of nibbles
; to receive minus 1 to Y register before running this program.
.program sdio_data_rx
wait_start:
mov X, Y ; Reinitialize number of nibbles to receive
wait 0 pin 0 ; Wait for zero state on D0
wait 1 pin SDIO_CLK_PIN_D0_OFFSET [CLKDIV-1] ; Wait for rising edge and then whole clock cycle
rx_data:
in PINS, 4 [CLKDIV-2] ; Read nibble
jmp X--, rx_data
; Data transmission program
;
; Before running this program, pindirs should be set as output
; and register X should be initialized with the number of nibbles
; to send minus 1 (typically 8 + 1024 + 16 + 1 - 1 = 1048)
; and register Y with the number of response bits minus 1 (typically 31).
;
; Words written to TX FIFO must be:
; - Word 0: start token 0xFFFFFFF0
; - Word 1-128: transmitted data (512 bytes)
; - Word 129-130: CRC checksum
; - Word 131: end token 0xFFFFFFFF
;
; After the card reports idle status, RX FIFO will get a word that
; contains the D0 line response from card.
.program sdio_data_tx
wait 0 pin SDIO_CLK_PIN_D0_OFFSET
wait 1 pin SDIO_CLK_PIN_D0_OFFSET [CLKDIV + D1 - 1]; Synchronize so that write occurs on falling edge
tx_loop:
out PINS, 4 [D0] ; Write nibble and wait for whole clock cycle
jmp X-- tx_loop [D1]
set pindirs, 0x00 [D0] ; Set data bus as input
.wrap_target
response_loop:
in PINS, 1 [D1] ; Read D0 on rising edge
jmp Y--, response_loop [D0]
wait_idle:
wait 1 pin 0 [D1] ; Wait for card to indicate idle condition
push [D0] ; Push the response token
.wrap
\ No newline at end of file
/* 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>
#include <stddef.h>
#include <stdint.h>
//
// Pico includes
#include "pico/stdlib.h"
#include "pico/mutex.h"
#include "pico/types.h"
//
#include "hardware/dma.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "hardware/spi.h"
//
#include "my_debug.h"
#include "sd_timeouts.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SPI_FILL_CHAR (0xFF)
// "Class" representing SPIs
typedef struct spi_t {
spi_inst_t *hw_inst; // SPI HW
uint miso_gpio; // SPI MISO GPIO number (not pin number)
uint mosi_gpio;
uint sck_gpio;
uint baud_rate;
/* The different modes of the Motorola SPI protocol are:
- Mode 0: When CPOL and CPHA are both 0, data sampled at the leading rising edge of the
clock pulse and shifted out on the falling edge. This is the most common mode for SPI bus
communication.
- Mode 1: When CPOL is 0 and CPHA is 1, data sampled at the trailing falling edge and
shifted out on the rising edge.
- Mode 2: When CPOL is 1 and CPHA is 0, data sampled at the leading falling edge
and shifted out on the rising edge.
- Mode 3: When CPOL is 1 and CPHA is 1, data sampled at the trailing rising edge and
shifted out on the falling edge. */
uint spi_mode;
bool no_miso_gpio_pull_up;
/* Drive strength levels for GPIO outputs:
GPIO_DRIVE_STRENGTH_2MA,
GPIO_DRIVE_STRENGTH_4MA,
GPIO_DRIVE_STRENGTH_8MA,
GPIO_DRIVE_STRENGTH_12MA */
bool set_drive_strength;
enum gpio_drive_strength mosi_gpio_drive_strength;
enum gpio_drive_strength sck_gpio_drive_strength;
bool use_static_dma_channels;
uint tx_dma;
uint rx_dma;
/* The following fields are not part of the configuration. They are dynamically assigned. */
dma_channel_config tx_dma_cfg;
dma_channel_config rx_dma_cfg;
mutex_t mutex;
bool initialized;
} spi_t;
void spi_transfer_start(spi_t *spi_p, const uint8_t *tx, uint8_t *rx, size_t length);
uint32_t calculate_transfer_time_ms(spi_t *spi_p, uint32_t bytes);
bool spi_transfer_wait_complete(spi_t *spi_p, uint32_t timeout_ms);
bool spi_transfer(spi_t *spi_p, const uint8_t *tx, uint8_t *rx, size_t length);
bool my_spi_init(spi_t *spi_p);
static inline void spi_lock(spi_t *spi_p) {
myASSERT(mutex_is_initialized(&spi_p->mutex));
mutex_enter_blocking(&spi_p->mutex);
}
static inline void spi_unlock(spi_t *spi_p) {
myASSERT(mutex_is_initialized(&spi_p->mutex));
mutex_exit(&spi_p->mutex);
}
/*
This uses the Pico LED to show SD card activity.
You can use it to get a rough idea of utilization.
Warning: Pico W uses GPIO 25 for SPI communication to the CYW43439.
You can enable this by putting something like
add_compile_definitions(USE_LED=1)
in CMakeLists.txt, for example.
*/
#if !defined(NO_PICO_LED) && defined(USE_LED) && USE_LED && defined(PICO_DEFAULT_LED_PIN)
# define LED_INIT() \
{ \
gpio_init(PICO_DEFAULT_LED_PIN); \
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); \
}
# define LED_ON() gpio_put(PICO_DEFAULT_LED_PIN, 1)
# define LED_OFF() gpio_put(PICO_DEFAULT_LED_PIN, 0)
#else
# define LED_ON()
# define LED_OFF()
# define LED_INIT()
#endif
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/* sd_card_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 "sd_card.h"
#ifdef __cplusplus
extern "C" {
#endif
void sd_spi_ctor(sd_card_t *sd_card_p); // Constructor for sd_card_t
uint32_t sd_go_idle_state(sd_card_t *sd_card_p);
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/* 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 <inttypes.h>
#include <stdint.h>
#include <string.h>
//
#include "hardware/gpio.h"
//
#include "my_debug.h"
#include "delays.h"
#include "my_spi.h"
//
#if !defined(USE_DBG_PRINTF) || defined(NDEBUG)
# pragma GCC diagnostic ignored "-Wunused-variable"
#endif
//
#include "sd_spi.h"
// #define TRACE_PRINTF(fmt, args...)
// #define TRACE_PRINTF printf
void sd_spi_go_high_frequency(sd_card_t *sd_card_p) {
uint actual = spi_set_baudrate(sd_card_p->spi_if_p->spi->hw_inst, sd_card_p->spi_if_p->spi->baud_rate);
DBG_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual);
}
void sd_spi_go_low_frequency(sd_card_t *sd_card_p) {
uint actual = spi_set_baudrate(sd_card_p->spi_if_p->spi->hw_inst, 400 * 1000); // Actual frequency: 398089
DBG_PRINTF("%s: Actual frequency: %lu\n", __FUNCTION__, (long)actual);
}
/*
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 *sd_card_p) {
if ((uint)-1 == sd_card_p->spi_if_p->ss_gpio) return;
bool old_ss = gpio_get(sd_card_p->spi_if_p->ss_gpio);
// Set DI and CS high and apply 74 or more clock pulses to SCLK:
gpio_put(sd_card_p->spi_if_p->ss_gpio, 1);
uint8_t ones[10];
memset(ones, 0xFF, sizeof ones);
uint32_t start = millis();
do {
spi_transfer(sd_card_p->spi_if_p->spi, ones, NULL, sizeof ones);
} while (millis() - start < 1);
gpio_put(sd_card_p->spi_if_p->ss_gpio, old_ss);
}
/* [] END OF FILE */
/* 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.
*/
#pragma once
#include <stdint.h>
//
#include "pico/stdlib.h"
//
#include "delays.h"
#include "my_debug.h"
#include "my_spi.h"
#include "sd_card.h"
#include "sd_timeouts.h"
#ifdef NDEBUG
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Transfer tx to SPI while receiving SPI to rx.
tx or rx can be NULL if not important. */
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 *sd_card_p);
static inline uint8_t sd_spi_read(sd_card_t *sd_card_p) {
uint8_t received = SPI_FILL_CHAR;
uint32_t start = millis();
while (!spi_is_writable(sd_card_p->spi_if_p->spi->hw_inst) &&
millis() - start < sd_timeouts.sd_spi_read)
tight_loop_contents();
myASSERT(spi_is_writable(sd_card_p->spi_if_p->spi->hw_inst));
int num = spi_read_blocking(sd_card_p->spi_if_p->spi->hw_inst, SPI_FILL_CHAR, &received, 1);
myASSERT(1 == num);
return received;
}
static inline void sd_spi_write(sd_card_t *sd_card_p, const uint8_t value) {
uint32_t start = millis();
while (!spi_is_writable(sd_card_p->spi_if_p->spi->hw_inst) &&
millis() - start < sd_timeouts.sd_spi_write)
tight_loop_contents();
myASSERT(spi_is_writable(sd_card_p->spi_if_p->spi->hw_inst));
int num = spi_write_blocking(sd_card_p->spi_if_p->spi->hw_inst, &value, 1);
myASSERT(1 == num);
}
static inline uint8_t sd_spi_write_read(sd_card_t *sd_card_p, const uint8_t value) {
uint8_t received = SPI_FILL_CHAR;
uint32_t start = millis();
while (!spi_is_writable(sd_card_p->spi_if_p->spi->hw_inst) &&
millis() - start < sd_timeouts.sd_spi_write_read)
tight_loop_contents();
myASSERT(spi_is_writable(sd_card_p->spi_if_p->spi->hw_inst));
int num = spi_write_read_blocking(sd_card_p->spi_if_p->spi->hw_inst, &value, &received, 1);
myASSERT(1 == num);
return received;
}
// Would do nothing if sd_card_p->spi_if_p->ss_gpio were set to GPIO_FUNC_SPI.
static inline void sd_spi_select(sd_card_t *sd_card_p) {
if ((uint)-1 == sd_card_p->spi_if_p->ss_gpio) return;
gpio_put(sd_card_p->spi_if_p->ss_gpio, 0);
// See http://elm-chan.org/docs/mmc/mmc_e.html#spibus
sd_spi_write(sd_card_p, SPI_FILL_CHAR);
LED_ON();
}
static inline void sd_spi_deselect(sd_card_t *sd_card_p) {
if ((uint)-1 == sd_card_p->spi_if_p->ss_gpio) return;
gpio_put(sd_card_p->spi_if_p->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.
*/
sd_spi_write(sd_card_p, SPI_FILL_CHAR);
}
static inline void sd_spi_lock(sd_card_t *sd_card_p) { spi_lock(sd_card_p->spi_if_p->spi); }
static inline void sd_spi_unlock(sd_card_t *sd_card_p) { spi_unlock(sd_card_p->spi_if_p->spi); }
static inline void sd_spi_acquire(sd_card_t *sd_card_p) {
sd_spi_lock(sd_card_p);
sd_spi_select(sd_card_p);
}
static inline void sd_spi_release(sd_card_t *sd_card_p) {
sd_spi_deselect(sd_card_p);
sd_spi_unlock(sd_card_p);
}
/* Transfer tx to SPI while receiving SPI to rx.
tx or rx can be NULL if not important. */
static inline void sd_spi_transfer_start(sd_card_t *sd_card_p, const uint8_t *tx, uint8_t *rx,
size_t length) {
return spi_transfer_start(sd_card_p->spi_if_p->spi, tx, rx, length);
}
static inline bool sd_spi_transfer_wait_complete(sd_card_t *sd_card_p, uint32_t timeout_ms) {
return spi_transfer_wait_complete(sd_card_p->spi_if_p->spi, timeout_ms);
}
static inline bool sd_spi_transfer(sd_card_t *sd_card_p, const uint8_t *tx, uint8_t *rx,
size_t length) {
return spi_transfer(sd_card_p->spi_if_p->spi, tx, rx, length);
}
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
#include "hw_config.h"
#include "sd_card.h"
//
#include "dma_interrupts.h"
static void dma_irq_handler(const uint DMA_IRQ_num, io_rw_32 *dma_hw_ints_p) {
// Iterate through all of the SD cards
for (size_t i = 0; i < sd_get_num(); ++i) {
sd_card_t *sd_card_p = sd_get_by_num(i);
if (!sd_card_p)
continue;
uint irq_num = 0, channel = 0;
if (SD_IF_SDIO == sd_card_p->type) {
irq_num = sd_card_p->sdio_if_p->DMA_IRQ_num;
channel = sd_card_p->sdio_if_p->state.SDIO_DMA_CHB;
}
// Is this channel requesting interrupt?
if (irq_num == DMA_IRQ_num && (*dma_hw_ints_p & (1 << channel))) {
*dma_hw_ints_p = 1 << channel; // Clear it.
if (SD_IF_SDIO == sd_card_p->type) {
sdio_irq_handler(sd_card_p);
}
}
}
}
static void __not_in_flash_func(dma_irq_handler_0)() {
dma_irq_handler(DMA_IRQ_0, &dma_hw->ints0);
}
static void __not_in_flash_func(dma_irq_handler_1)() {
dma_irq_handler(DMA_IRQ_1, &dma_hw->ints1);
}
/* Adding the interrupt request handler
Only add it once.
Otherwise, space is wasted in irq_add_shared_handler's table.
Also, each core maintains its own table of interrupt vectors,
and we don't want both cores to call the IRQ handler.
*/
typedef struct ih_added_rec_t {
uint num;
bool added;
} ih_added_rec_t;
static ih_added_rec_t ih_added_recs[] = {
{DMA_IRQ_0, false},
{DMA_IRQ_1, false}};
static bool is_handler_added(const uint num) {
for (size_t i = 0; i < count_of(ih_added_recs); ++i)
if (num == ih_added_recs[i].num)
return ih_added_recs[i].added;
return false;
}
static void mark_handler_added(const uint num) {
size_t i;
for (i = 0; i < count_of(ih_added_recs); ++i)
if (num == ih_added_recs[i].num) {
ih_added_recs[i].added = true;
break;
}
myASSERT(i < count_of(ih_added_recs));
}
void dma_irq_add_handler(const uint num, bool exclusive) {
if (!is_handler_added(num)) {
static void (*irq_handler)();
switch (num) {
case DMA_IRQ_0:
irq_handler = dma_irq_handler_0;
break;
case DMA_IRQ_1:
irq_handler = dma_irq_handler_1;
break;
default:
myASSERT(false);
}
if (exclusive) {
irq_set_exclusive_handler(num, *irq_handler);
} else {
irq_add_shared_handler(
num, *irq_handler,
PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
}
irq_set_enabled(num, true); // Enable IRQ in NVIC
mark_handler_added(num);
}
}
#pragma once
#include "pico.h"
#ifdef __cplusplus
extern "C" {
#endif
void dma_irq_add_handler(const uint num, bool exclusive);
#ifdef __cplusplus
}
#endif
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
#define CLKDIV 4
#define SDIO_CLK_PIN_D0_OFFSET 30
// ------------ //
// sdio_cmd_clk //
// ------------ //
#define sdio_cmd_clk_wrap_target 0
#define sdio_cmd_clk_wrap 17
static const uint16_t sdio_cmd_clk_program_instructions[] = {
// .wrap_target
0xb1e3, // 0: mov osr, null side 1 [1]
0xa14d, // 1: mov y, !status side 0 [1]
0x1161, // 2: jmp !y, 1 side 1 [1]
0x6160, // 3: out null, 32 side 0 [1]
0x7128, // 4: out x, 8 side 1 [1]
0xe101, // 5: set pins, 1 side 0 [1]
0xf181, // 6: set pindirs, 1 side 1 [1]
0x6101, // 7: out pins, 1 side 0 [1]
0x1147, // 8: jmp x--, 7 side 1 [1]
0xe180, // 9: set pindirs, 0 side 0 [1]
0x7128, // 10: out x, 8 side 1 [1]
0xa142, // 11: nop side 0 [1]
0x1131, // 12: jmp !x, 17 side 1 [1]
0xa142, // 13: nop side 0 [1]
0x11cd, // 14: jmp pin, 13 side 1 [1]
0x4101, // 15: in pins, 1 side 0 [1]
0x114f, // 16: jmp x--, 15 side 1 [1]
0x8120, // 17: push block side 0 [1]
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program sdio_cmd_clk_program = {
.instructions = sdio_cmd_clk_program_instructions,
.length = 18,
.origin = -1,
};
static inline pio_sm_config sdio_cmd_clk_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + sdio_cmd_clk_wrap_target, offset + sdio_cmd_clk_wrap);
sm_config_set_sideset(&c, 1, false, false);
return c;
}
#endif
// ------------ //
// sdio_data_rx //
// ------------ //
#define sdio_data_rx_wrap_target 0
#define sdio_data_rx_wrap 4
static const uint16_t sdio_data_rx_program_instructions[] = {
// .wrap_target
0xa022, // 0: mov x, y
0x2020, // 1: wait 0 pin, 0
0x23be, // 2: wait 1 pin, 30 [3]
0x4204, // 3: in pins, 4 [2]
0x0043, // 4: jmp x--, 3
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program sdio_data_rx_program = {
.instructions = sdio_data_rx_program_instructions,
.length = 5,
.origin = -1,
};
static inline pio_sm_config sdio_data_rx_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + sdio_data_rx_wrap_target, offset + sdio_data_rx_wrap);
return c;
}
#endif
// ------------ //
// sdio_data_tx //
// ------------ //
#define sdio_data_tx_wrap_target 5
#define sdio_data_tx_wrap 8
static const uint16_t sdio_data_tx_program_instructions[] = {
0x203e, // 0: wait 0 pin, 30
0x24be, // 1: wait 1 pin, 30 [4]
0x6104, // 2: out pins, 4 [1]
0x0142, // 3: jmp x--, 2 [1]
0xe180, // 4: set pindirs, 0 [1]
// .wrap_target
0x4101, // 5: in pins, 1 [1]
0x0185, // 6: jmp y--, 5 [1]
0x21a0, // 7: wait 1 pin, 0 [1]
0x8120, // 8: push block [1]
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program sdio_data_tx_program = {
.instructions = sdio_data_tx_program_instructions,
.length = 9,
.origin = -1,
};
static inline pio_sm_config sdio_data_tx_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + sdio_data_tx_wrap_target, offset + sdio_data_tx_wrap);
return c;
}
#endif
/* 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.
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
//
#include <hardware/pio.h>
#include "hardware/gpio.h"
#include "pico/mutex.h"
//
#include "ff.h"
//
#include "SDIO/rp2040_sdio.h"
#include "SPI/my_spi.h"
#include "SPI/sd_card_spi.h"
#include "diskio.h"
#include "sd_card_constants.h"
#include "sd_regs.h"
#include "util.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum { SD_IF_NONE, SD_IF_SPI, SD_IF_SDIO } sd_if_t;
typedef struct sd_spi_if_state_t {
bool ongoing_mlt_blk_wrt;
uint32_t cont_sector_wrt;
uint32_t n_wrt_blks_reqd;
} sd_spi_if_state_t;
typedef struct sd_spi_if_t {
spi_t *spi;
// Slave select is here instead of in spi_t because multiple SDs can share an SPI.
uint ss_gpio; // Slave select for this SD card
// Drive strength levels for GPIO outputs:
// GPIO_DRIVE_STRENGTH_2MA
// GPIO_DRIVE_STRENGTH_4MA
// GPIO_DRIVE_STRENGTH_8MA
// GPIO_DRIVE_STRENGTH_12MA
bool set_drive_strength;
enum gpio_drive_strength ss_gpio_drive_strength;
sd_spi_if_state_t state;
} sd_spi_if_t;
typedef struct sd_sdio_if_t {
// See sd_driver\SDIO\rp2040_sdio.pio for SDIO_CLK_PIN_D0_OFFSET
uint CLK_gpio; // Must be (D0_gpio + SDIO_CLK_PIN_D0_OFFSET) % 32
uint CMD_gpio;
uint D0_gpio; // D0
uint D1_gpio; // Must be D0 + 1
uint D2_gpio; // Must be D0 + 2
uint D3_gpio; // Must be D0 + 3
PIO SDIO_PIO; // either pio0 or pio1
uint DMA_IRQ_num; // DMA_IRQ_0 or DMA_IRQ_1
bool use_exclusive_DMA_IRQ_handler;
uint baud_rate;
// Drive strength levels for GPIO outputs:
// GPIO_DRIVE_STRENGTH_2MA
// GPIO_DRIVE_STRENGTH_4MA
// GPIO_DRIVE_STRENGTH_8MA
// GPIO_DRIVE_STRENGTH_12MA
bool set_drive_strength;
enum gpio_drive_strength CLK_gpio_drive_strength;
enum gpio_drive_strength CMD_gpio_drive_strength;
enum gpio_drive_strength D0_gpio_drive_strength;
enum gpio_drive_strength D1_gpio_drive_strength;
enum gpio_drive_strength D2_gpio_drive_strength;
enum gpio_drive_strength D3_gpio_drive_strength;
/* The following fields are not part of the configuration.
They are state variables, and are dynamically assigned. */
sd_sdio_if_state_t state;
} sd_sdio_if_t;
typedef struct sd_card_state_t {
DSTATUS m_Status; // Card status
card_type_t card_type; // Assigned dynamically
CSD_t CSD; // Card-Specific Data register.
CID_t CID; // Card IDentification register
uint32_t sectors; // Assigned dynamically
mutex_t mutex;
FATFS fatfs;
bool mounted;
#if FF_STR_VOLUME_ID
char drive_prefix[32];
#else
char drive_prefix[4];
#endif
} sd_card_state_t;
typedef struct sd_card_t sd_card_t;
// "Class" representing SD Cards
struct sd_card_t {
sd_if_t type; // Interface type
union {
sd_spi_if_t *spi_if_p;
sd_sdio_if_t *sdio_if_p;
};
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
bool card_detect_use_pull;
bool card_detect_pull_hi;
/* The following fields are state variables and not part of the configuration.
They are dynamically assigned. */
sd_card_state_t state;
DSTATUS (*init)(sd_card_t *sd_card_p);
void (*deinit)(sd_card_t *sd_card_p);
block_dev_err_t (*write_blocks)(sd_card_t *sd_card_p, const uint8_t *buffer,
uint32_t ulSectorNumber, uint32_t blockCnt);
block_dev_err_t (*read_blocks)(sd_card_t *sd_card_p, uint8_t *buffer,
uint32_t ulSectorNumber, uint32_t ulSectorCount);
block_dev_err_t (*sync)(sd_card_t *sd_card_p);
uint32_t (*get_num_sectors)(sd_card_t *sd_card_p);
// Useful when use_card_detect is false - call periodically to check for presence of SD card
// Returns true if and only if SD card was sensed on the bus
bool (*sd_test_com)(sd_card_t *sd_card_p);
};
void sd_lock(sd_card_t *sd_card_p);
void sd_unlock(sd_card_t *sd_card_p);
bool sd_is_locked(sd_card_t *sd_card_p);
bool sd_init_driver();
bool sd_card_detect(sd_card_t *sd_card_p);
void cidDmp(sd_card_t *sd_card_p, printer_t printer);
void csdDmp(sd_card_t *sd_card_p, printer_t printer);
bool sd_allocation_unit(sd_card_t *sd_card_p, size_t *au_size_bytes_p);
sd_card_t *sd_get_by_drive_prefix(const char *const name);
// sd_init_driver() must be called before this:
char const *sd_get_drive_prefix(sd_card_t *sd_card_p);
#ifdef __cplusplus
}
#endif
/* [] END OF FILE */
/* sd_card_constants.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
#ifdef __cplusplus
extern "C" {
#endif
/*!< Block size supported for SD card is 512 bytes */
// Only HC block size is supported.
static const size_t sd_block_size = 512;
typedef enum {
SD_BLOCK_DEVICE_ERROR_NONE = 0,
SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK = 1 << 0, /*!< operation would block */
SD_BLOCK_DEVICE_ERROR_UNSUPPORTED = 1 << 1, /*!< unsupported operation */
SD_BLOCK_DEVICE_ERROR_PARAMETER = 1 << 2, /*!< invalid parameter */
SD_BLOCK_DEVICE_ERROR_NO_INIT = 1 << 3, /*!< uninitialized */
SD_BLOCK_DEVICE_ERROR_NO_DEVICE = 1 << 4, /*!< device is missing or not connected */
SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED = 1 << 5, /*!< write protected */
SD_BLOCK_DEVICE_ERROR_UNUSABLE = 1 << 6, /*!< unusable card */
SD_BLOCK_DEVICE_ERROR_NO_RESPONSE = 1 << 7, /*!< No response from device */
SD_BLOCK_DEVICE_ERROR_CRC = 1 << 8, /*!< CRC error */
SD_BLOCK_DEVICE_ERROR_ERASE = 1 << 9, /*!< Erase error: reset/sequence */
SD_BLOCK_DEVICE_ERROR_WRITE = 1 << 10 /*!< Write error: !SPI_DATA_ACCEPTED */
} block_dev_err_t;
/** Represents the different SD/MMC card types */
typedef enum {
SDCARD_NONE = 0, /**< No card is present */
SDCARD_V1 = 1, /**< v1.x Standard Capacity */
SDCARD_V2 = 2, /**< v2.x Standard capacity SD card */
SDCARD_V2HC = 3, /**< v2.x High capacity SD card */
CARD_UNKNOWN = 4 /**< Unknown or unsupported card */
} card_type_t;
/* On the wire, convert to hex and add 0x40 for transmitter bit.
e.g.: CMD17_READ_SINGLE_BLOCK: 17 = 0x11; 0x11 | 0x40 = 0x51 */
// Supported SD Card Commands
typedef enum { /* Number on wire in parens */
CMD_NOT_SUPPORTED = -1, /* Command not supported error */
CMD0_GO_IDLE_STATE = 0, /* Resets the SD Memory Card */
CMD1_SEND_OP_COND = 1, /* Sends host capacity support */
CMD2_ALL_SEND_CID = 2, /* Asks any card to send the CID. */
CMD3_SEND_RELATIVE_ADDR = 3, /* Ask the card to publish a new RCA. */
CMD6_SWITCH_FUNC = 6, /* Check and Switches card function */
CMD7_SELECT_CARD = 7, /* SELECT/DESELECT_CARD - toggles between the stand-by and transfer states. */
CMD8_SEND_IF_COND = 8, /* Supply voltage info */
CMD9_SEND_CSD = 9, /* Provides Card Specific data */
CMD10_SEND_CID = 10, /* Provides Card Identification */
CMD12_STOP_TRANSMISSION = 12, /* Forces the card to stop transmission */
CMD13_SEND_STATUS = 13, /* (0x4D) Card responds with status */
CMD16_SET_BLOCKLEN = 16, /* Length for SC card is set */
CMD17_READ_SINGLE_BLOCK = 17, /* (0x51) Read single block of data */
CMD18_READ_MULTIPLE_BLOCK = 18, /* (0x52) Continuously Card transfers data blocks to host
until interrupted by a STOP_TRANSMISSION command */
CMD24_WRITE_BLOCK = 24, /* (0x58) Write single block of data */
CMD25_WRITE_MULTIPLE_BLOCK = 25, /* (0x59) Continuously writes blocks of data
until 'Stop Tran' token is sent */
CMD27_PROGRAM_CSD = 27, /* Programming bits of CSD */
CMD32_ERASE_WR_BLK_START_ADDR = 32, /* Sets the address of the first write
block to be erased. */
CMD33_ERASE_WR_BLK_END_ADDR = 33, /* Sets the address of the last write
block of the continuous range to be erased.*/
CMD38_ERASE = 38, /* Erases all previously selected write blocks */
CMD55_APP_CMD = 55, /* Extend to Applications specific commands */
CMD56_GEN_CMD = 56, /* General Purpose Command */
CMD58_READ_OCR = 58, /* Read OCR register of card */
CMD59_CRC_ON_OFF = 59, /* Turns the CRC option on or off*/
// App Commands
ACMD6_SET_BUS_WIDTH = 6,
ACMD13_SD_STATUS = 13,
ACMD22_SEND_NUM_WR_BLOCKS = 22,
ACMD23_SET_WR_BLK_ERASE_COUNT = 23,
ACMD41_SD_SEND_OP_COND = 41,
ACMD42_SET_CLR_CARD_DETECT = 42,
ACMD51_SEND_SCR = 51,
} cmdSupported;
//------------------------------------------------------------------------------
///* 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 */
//};
#ifdef __cplusplus
}
#endif
/* FatFsSd.cpp
Copyright 2023 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 "FatFsSd.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
using namespace FatFsNs;
/*
See FatFs - Generic FAT Filesystem Module, "Application Interface",
http://elm-chan.org/fsw/ff/00index_e.html
*/
std::vector<SdCard> FatFs::SdCards;
/* Put a formatted string to the file */
int File::printf(const TCHAR* format, ...) {
va_list arg;
va_start(arg, format);
char temp[64];
char* buffer = temp;
size_t len = vsnprintf(temp, sizeof(temp), format, arg);
va_end(arg);
if (len > sizeof(temp) - 1) {
buffer = new char[len + 1];
if (!buffer) {
return 0;
}
va_start(arg, format);
int vrc = vsnprintf(buffer, len + 1, format, arg);
// Notice that only when this returned value is non-negative and less than n,
// the string has been completely written.
assert(vrc >= 0 && vrc < len + 1);
va_end(arg);
}
UINT bw;
FRESULT fr = f_write(&fil, buffer, len, &bw);
int rc = bw;
if (FR_OK != fr) {
rc = -1;
}
if (buffer != temp) {
delete[] buffer;
}
return rc;
}
bool FatFs::begin() {
if (!sd_init_driver())
return false;
for (size_t i = 0; i < sd_get_num(); ++i) {
sd_card_t* sd_card_p = sd_get_by_num(i);
if (!sd_card_p) return false;
// See http://elm-chan.org/fsw/ff/doc/dstat.html
int dstatus = sd_card_p->init(sd_card_p);
if (dstatus & STA_NOINIT) return false;
}
return true;
}
size_t __attribute__((weak)) sd_get_num() {
return FatFs::SdCard_get_num();
}
sd_card_t * __attribute__((weak)) sd_get_by_num(size_t num) {
return (FatFs::SdCard_get_by_num(num)->m_sd_card_p);
}
/* f_util.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 <assert.h>
#include <stdio.h>
//
#include "ff.h"
const char *FRESULT_str(FRESULT i) {
switch (i) {
case FR_OK:
return "Succeeded";
case FR_DISK_ERR:
return "A hard error occurred in the low level disk I/O layer";
case FR_INT_ERR:
return "Assertion failed";
case FR_NOT_READY:
return "The physical drive cannot work";
case FR_NO_FILE:
return "Could not find the file";
case FR_NO_PATH:
return "Could not find the path";
case FR_INVALID_NAME:
return "The path name format is invalid";
case FR_DENIED:
return "Access denied due to prohibited access or directory full";
case FR_EXIST:
return "Access denied due to prohibited access (exists)";
case FR_INVALID_OBJECT:
return "The file/directory object is invalid";
case FR_WRITE_PROTECTED:
return "The physical drive is write protected";
case FR_INVALID_DRIVE:
return "The logical drive number is invalid";
case FR_NOT_ENABLED:
return "The volume has no work area (mount)";
case FR_NO_FILESYSTEM:
return "There is no valid FAT volume";
case FR_MKFS_ABORTED:
return "The f_mkfs() aborted due to any problem";
case FR_TIMEOUT:
return "Could not get a grant to access the volume within defined "
"period";
case FR_LOCKED:
return "The operation is rejected according to the file sharing "
"policy";
case FR_NOT_ENOUGH_CORE:
return "LFN working buffer could not be allocated";
case FR_TOO_MANY_OPEN_FILES:
return "Number of open files > FF_FS_LOCK";
case FR_INVALID_PARAMETER:
return "Given parameter is invalid";
default:
return "Unknown";
}
}
FRESULT delete_node (
TCHAR* path, /* Path name buffer with the sub-directory to delete */
UINT sz_buff, /* Size of path name buffer (items) */
FILINFO* fno /* Name read buffer */
)
{
UINT i, j;
FRESULT fr;
DIR dir;
fr = f_opendir(&dir, path); /* Open the sub-directory to make it empty */
if (fr != FR_OK) return fr;
for (i = 0; path[i]; i++) ; /* Get current path length */
path[i++] = '/';
for (;;) {
fr = f_readdir(&dir, fno); /* Get a directory item */
if (fr != FR_OK || !fno->fname[0]) break; /* End of directory? */
j = 0;
do { /* Make a path name */
if (i + j >= sz_buff) { /* Buffer over flow? */
fr = 100; break; /* Fails with 100 when buffer overflow */
}
path[i + j] = fno->fname[j];
} while (fno->fname[j++]);
if (fno->fattrib & AM_DIR) { /* Item is a sub-directory */
fr = delete_node(path, sz_buff, fno);
} else { /* Item is a file */
fr = f_unlink(path);
}
if (fr != FR_OK) break;
}
path[--i] = 0; /* Restore the path name */
f_closedir(&dir);
if (fr == FR_OK) fr = f_unlink(path); /* Delete the empty sub-directory */
return fr;
}
void ls(const char *dir) {
char cwdbuf[FF_LFN_BUF] = {0};
FRESULT fr; /* Return value */
char const *p_dir;
if (dir[0]) {
p_dir = dir;
} else {
fr = f_getcwd(cwdbuf, sizeof cwdbuf);
if (FR_OK != fr) {
printf("f_getcwd error: %s (%d)\n", FRESULT_str(fr), fr);
return;
}
p_dir = cwdbuf;
}
printf("Directory Listing: %s\n", p_dir);
DIR dj = {}; /* Directory object */
FILINFO fno = {}; /* File information */
assert(p_dir);
fr = f_findfirst(&dj, &fno, p_dir, "*");
if (FR_OK != fr) {
printf("f_findfirst error: %s (%d)\n", FRESULT_str(fr), fr);
return;
}
while (fr == FR_OK && fno.fname[0]) { /* Repeat while an item is found */
/* Create a string that includes the file name, the file size and the
attributes string. */
const char *pcWritableFile = "writable file",
*pcReadOnlyFile = "read only file",
*pcDirectory = "directory";
const char *pcAttrib;
/* Point pcAttrib to a string that describes the file. */
if (fno.fattrib & AM_DIR) {
pcAttrib = pcDirectory;
} else if (fno.fattrib & AM_RDO) {
pcAttrib = pcReadOnlyFile;
} else {
pcAttrib = pcWritableFile;
}
/* Create a string that includes the file name, the file size and the
attributes string. */
printf("%s [%s] [size=%llu]\n", fno.fname, pcAttrib, fno.fsize);
fr = f_findnext(&dj, &fno); /* Search for next item */
}
f_closedir(&dj);
}
/*
* file_stream.c
*
* Created on: Jun 20, 2024
* Author: carlk
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//
#include "pico/stdlib.h"
//
#include "ff.h"
//
#include "f_util.h"
#include "my_debug.h"
//
#include "file_stream.h"
typedef struct {
FIL file;
} cookie_t;
// functions.read should return -1 on failure, or else the number of bytes read (0 on EOF).
// It is similar to read, except that cookie will be passed as the first argument.
static ssize_t cookie_read_function(void *vcookie_p, char *buf, size_t n) {
cookie_t *cookie_p = vcookie_p;
FIL *file_p = &cookie_p->file;
UINT br;
FRESULT fr = f_read(file_p, buf, n, &br);
if (FR_OK != fr) {
DBG_PRINTF("f_read error: %s\n", FRESULT_str(fr));
return -1;
}
return br;
}
// functions.write should return -1 on failure, or else the number of bytes written.
// It is similar to write, except that cookie will be passed as the first argument.
static ssize_t cookie_write_function(void *vcookie_p, const char *buf, size_t n) {
cookie_t *cookie_p = vcookie_p;
FIL *file_p = &cookie_p->file;
UINT bw;
FRESULT fr = f_write(file_p, buf, n, &bw);
if (FR_OK != fr) {
DBG_PRINTF("f_read error: %s\n", FRESULT_str(fr));
return -1;
}
return bw;
}
// functions.seek should return -1 on failure, and 0 on success,
// with off set to the current file position.
// It is a cross between lseek and fseek, with the whence argument interpreted in the same
// manner.
static int cookie_seek_function(void *vcookie_p, off_t *off, int whence) {
cookie_t *cookie_p = vcookie_p;
FIL *file_p = &cookie_p->file;
FRESULT fr = FR_OK;
switch (whence) {
case SEEK_SET:
fr = f_lseek(file_p, *off);
break;
case SEEK_CUR:
fr = f_lseek(file_p, f_tell(file_p) + *off);
break;
case SEEK_END:
fr = f_lseek(file_p, f_size(file_p) + *off);
break;
default:
ASSERT_CASE_NOT(whence);
}
if (FR_OK != fr) {
DBG_PRINTF("f_lseek error: %s\n", FRESULT_str(fr));
return -1;
} else {
*off = f_tell(file_p);
return 0;
}
}
// functions.close should return -1 on failure, or 0 on success.
// It is similar to close, except that cookie will be passed as the first argument.
// A failed close will still invalidate the stream.
static int cookie_close_function(void *vcookie_p) {
cookie_t *cookie_p = vcookie_p;
FIL *file_p = &cookie_p->file;
FRESULT fr = f_close(file_p);
free(vcookie_p);
if (FR_OK != fr) {
DBG_PRINTF("f_close error: %s\n", FRESULT_str(fr));
return -1;
} else {
return 0;
}
}
FILE *open_file_stream(const char *pathname, const char *pcMode) {
cookie_t *cookie_p = malloc(sizeof(cookie_t));
if (!cookie_p) {
return NULL;
}
BYTE mode = 0;
// POSIX FatFs
if (0 == strcmp("r", pcMode))
mode = FA_READ;
else if (0 == strcmp("r+", pcMode))
mode = FA_READ | FA_WRITE;
else if (0 == strcmp("w", pcMode))
mode = FA_CREATE_ALWAYS | FA_WRITE;
else if (0 == strcmp("w+", pcMode))
mode = FA_CREATE_ALWAYS | FA_WRITE | FA_READ;
else if (0 == strcmp("a", pcMode))
mode = FA_OPEN_APPEND | FA_WRITE;
else if (0 == strcmp("a+", pcMode))
mode = FA_OPEN_APPEND | FA_WRITE | FA_READ;
else if (0 == strcmp("wx", pcMode))
mode = FA_CREATE_NEW | FA_WRITE;
else if (0 == strcmp("w+x", pcMode))
mode = FA_CREATE_NEW | FA_WRITE | FA_READ;
FRESULT fr = f_open(&cookie_p->file, pathname, mode);
if (FR_OK != fr) {
DBG_PRINTF("f_lseek error: %s\n", FRESULT_str(fr));
return NULL;
}
cookie_io_functions_t iofs = {cookie_read_function, cookie_write_function,
cookie_seek_function, cookie_close_function};
/* create the stream */
FILE *file = fopencookie(cookie_p, pcMode, iofs);
if (!file) cookie_close_function(cookie_p);
return (file);
}
/* glue.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.
*/
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
//
//
#include "hw_config.h"
#include "my_debug.h"
#include "sd_card.h"
//
#include "diskio.h" /* Declarations of disk functions */
#define TRACE_PRINTF(fmt, args...)
//#define TRACE_PRINTF printf // task_printf
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status(BYTE pdrv /* Physical drive number to identify the drive */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
sd_card_detect(sd_card_p); // Fast: just a GPIO read
return sd_card_p->state.m_Status; // See http://elm-chan.org/fsw/ff/doc/dstat.html
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize(
BYTE pdrv /* Physical drive number to identify the drive */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
bool ok = sd_init_driver();
if (!ok) return RES_NOTRDY;
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
DSTATUS ds = disk_status(pdrv);
if (STA_NODISK & ds)
return ds;
// See http://elm-chan.org/fsw/ff/doc/dstat.html
return sd_card_p->init(sd_card_p);
}
static int sdrc2dresult(int sd_rc) {
switch (sd_rc) {
case SD_BLOCK_DEVICE_ERROR_NONE:
return RES_OK;
case SD_BLOCK_DEVICE_ERROR_UNUSABLE:
case SD_BLOCK_DEVICE_ERROR_NO_RESPONSE:
case SD_BLOCK_DEVICE_ERROR_NO_INIT:
case SD_BLOCK_DEVICE_ERROR_NO_DEVICE:
return RES_NOTRDY;
case SD_BLOCK_DEVICE_ERROR_PARAMETER:
case SD_BLOCK_DEVICE_ERROR_UNSUPPORTED:
return RES_PARERR;
case SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED:
return RES_WRPRT;
case SD_BLOCK_DEVICE_ERROR_CRC:
case SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK:
case SD_BLOCK_DEVICE_ERROR_ERASE:
case SD_BLOCK_DEVICE_ERROR_WRITE:
default:
return RES_ERROR;
}
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read(BYTE pdrv, /* Physical drive number to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
int rc = sd_card_p->read_blocks(sd_card_p, buff, sector, count);
return sdrc2dresult(rc);
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write(BYTE pdrv, /* Physical drive number to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
int rc = sd_card_p->write_blocks(sd_card_p, buff, sector, count);
return sdrc2dresult(rc);
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl(BYTE pdrv, /* Physical drive number (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *sd_card_p = sd_get_by_num(pdrv);
if (!sd_card_p) return RES_PARERR;
switch (cmd) {
case GET_SECTOR_COUNT: { // Retrieves number of available sectors, the
// largest allowable LBA + 1, on the drive
// into the LBA_t variable pointed by buff.
// This command is used by f_mkfs and f_fdisk
// function to determine the size of
// volume/partition to be created. It is
// required when FF_USE_MKFS == 1.
static LBA_t n;
n = sd_card_p->get_num_sectors(sd_card_p);
*(LBA_t *)buff = n;
if (!n) return RES_ERROR;
return RES_OK;
}
case GET_BLOCK_SIZE: { // Retrieves erase block size of the flash
// memory media in unit of sector into the DWORD
// variable pointed by buff. The allowable value
// is 1 to 32768 in power of 2. Return 1 if the
// erase block size is unknown or non flash
// memory media. This command is used by only
// f_mkfs function and it attempts to align data
// area on the erase block boundary. It is
// required when FF_USE_MKFS == 1.
static DWORD bs = 1;
*(DWORD *)buff = bs;
return RES_OK;
}
case CTRL_SYNC:
sd_card_p->sync(sd_card_p);
return RES_OK;
default:
return RES_PARERR;
}
}
/* my_debug.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 <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
//
#if PICO_RP2040
#include "RP2040.h"
#else
#include "RP2350.h"
#endif
#include "pico/stdlib.h"
//
#include "crash.h"
//
#include "my_debug.h"
/* Function Attribute ((weak))
The weak attribute causes a declaration of an external symbol to be emitted as a weak symbol
rather than a global. This is primarily useful in defining library functions that can be
overridden in user code, though it can also be used with non-function declarations. The
overriding symbol must have the same type as the weak symbol.
https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html
You can override these functions in your application to redirect "stdout"-type messages.
*/
/* Single string output callbacks */
void __attribute__((weak)) put_out_error_message(const char *s) { (void)s; }
void __attribute__((weak)) put_out_info_message(const char *s) { (void)s; }
void __attribute__((weak)) put_out_debug_message(const char *s) { (void)s; }
/* "printf"-style output callbacks */
#if defined(USE_PRINTF) && USE_PRINTF
int __attribute__((weak))
error_message_printf(const char *func, int line,
const char *fmt, ...)
{
printf("%s:%d: ", func, line);
va_list args;
va_start(args, fmt);
int cw = vprintf(fmt, args);
va_end(args);
stdio_flush();
return cw;
}
int __attribute__((weak)) error_message_printf_plain(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int cw = vprintf(fmt, args);
va_end(args);
stdio_flush();
return cw;
}
int __attribute__((weak)) info_message_printf(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
int cw = vprintf(fmt, args);
va_end(args);
return cw;
}
int __attribute__((weak))
debug_message_printf(const char *func, int line,
const char *fmt, ...)
{
#if defined(NDEBUG) || !USE_DBG_PRINTF
(void) func;
(void) line;
#endif
va_list args;
va_start(args, fmt);
int cw = vprintf(fmt, args);
va_end(args);
stdio_flush();
return cw;
}
#else
/* These will truncate at 256 bytes. You can tell by checking the return code. */
int __attribute__((weak))
error_message_printf(const char *func, int line,
const char *fmt, ...)
{
char buf[256] = {0};
va_list args;
va_start(args, fmt);
int cw = vsnprintf(buf, sizeof(buf), fmt, args);
put_out_error_message(buf);
va_end(args);
return cw;
}
int __attribute__((weak)) error_message_printf_plain(const char *fmt, ...) {
char buf[256] = {0};
va_list args;
va_start(args, fmt);
int cw = vsnprintf(buf, sizeof(buf), fmt, args);
put_out_info_message(buf);
va_end(args);
return cw;
}
int __attribute__((weak)) info_message_printf(const char *fmt, ...) {
char buf[256] = {0};
va_list args;
va_start(args, fmt);
int cw = vsnprintf(buf, sizeof(buf), fmt, args);
put_out_info_message(buf);
va_end(args);
return cw;
}
int __attribute__((weak))
debug_message_printf(const char *func, int line,
const char *fmt, ...)
{
char buf[256] = {0};
va_list args;
va_start(args, fmt);
int cw = vsnprintf(buf, sizeof(buf), fmt, args);
put_out_debug_message(buf);
va_end(args);
return cw;
}
#endif
void __attribute__((weak)) my_assert_func(const char *file, int line, const char *func,
const char *pred) {
error_message_printf_plain("assertion \"%s\" failed: file \"%s\", line %d, function: %s\n",
pred, file, line, func);
__disable_irq(); /* Disable global interrupts. */
capture_assert(file, line, func, pred);
}
void assert_case_not_func(const char *file, int line, const char *func, int v) {
char pred[128];
snprintf(pred, sizeof pred, "case not %d", v);
my_assert_func(file, line, func, pred);
}
void assert_case_is(const char *file, int line, const char *func, int v, int expected) {
char pred[128];
snprintf(pred, sizeof pred, "%d is %d", v, expected);
my_assert_func(file, line, func, pred);
}
void dump8buf(char *buf, size_t buf_sz, uint8_t *pbytes, size_t nbytes) {
int n = 0;
for (size_t byte_ix = 0; byte_ix < nbytes; ++byte_ix) {
for (size_t col = 0; col < 32 && byte_ix < nbytes; ++col, ++byte_ix) {
n += snprintf(buf + n, buf_sz - n, "%02hhx ", pbytes[byte_ix]);
myASSERT(0 < n && n < (int)buf_sz);
}
n += snprintf(buf + n, buf_sz - n, "\n");
myASSERT(0 < n && n < (int)buf_sz);
}
}
void hexdump_8(const char *s, const uint8_t *pbytes, size_t nbytes) {
IMSG_PRINTF("\n%s(%s, 0x%p, %zu)\n", __FUNCTION__, s, pbytes, nbytes);
stdio_flush();
size_t col = 0;
for (size_t byte_ix = 0; byte_ix < nbytes; ++byte_ix) {
IMSG_PRINTF("%02hhx ", pbytes[byte_ix]);
if (++col > 31) {
IMSG_PRINTF("\n");
col = 0;
}
stdio_flush();
}
}
// nwords is size in WORDS!
void hexdump_32(const char *s, const uint32_t *pwords, size_t nwords) {
IMSG_PRINTF("\n%s(%s, 0x%p, %zu)\n", __FUNCTION__, s, pwords, nwords);
stdio_flush();
size_t col = 0;
for (size_t word_ix = 0; word_ix < nwords; ++word_ix) {
IMSG_PRINTF("%08lx ", pwords[word_ix]);
if (++col > 7) {
IMSG_PRINTF("\n");
col = 0;
}
stdio_flush();
}
}
// nwords is size in bytes
bool compare_buffers_8(const char *s0, const uint8_t *pbytes0, const char *s1,
const uint8_t *pbytes1, const size_t nbytes) {
/* Verify the data. */
if (0 != memcmp(pbytes0, pbytes1, nbytes)) {
hexdump_8(s0, pbytes0, nbytes);
hexdump_8(s1, pbytes1, nbytes);
return false;
}
return true;
}
// nwords is size in WORDS!
bool compare_buffers_32(const char *s0, const uint32_t *pwords0, const char *s1,
const uint32_t *pwords1, const size_t nwords) {
/* Verify the data. */
if (0 != memcmp(pwords0, pwords1, nwords * sizeof(uint32_t))) {
hexdump_32(s0, pwords0, nwords);
hexdump_32(s1, pwords1, nwords);
return false;
}
return true;
}
/* [] END OF FILE */
/* my_rtc.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 <assert.h>
#include <stdbool.h>
#include <time.h>
//
#include "pico/aon_timer.h"
#include "pico/stdio.h"
#include "pico/stdlib.h"
#include "pico/util/datetime.h"
#if HAS_RP2040_RTC
# include "hardware/rtc.h"
#endif
//
#include "crc.h"
#include "ff.h"
//
#include "my_rtc.h"
time_t epochtime;
// Make an attempt to save a recent time stamp across reset:
typedef struct rtc_save {
uint32_t signature;
struct timespec ts;
char checksum; // last, not included in checksum
} rtc_save_t;
static rtc_save_t rtc_save __attribute__((section(".uninitialized_data")));
static bool get_time(struct timespec *ts) {
if (!aon_timer_is_running()) return false;
aon_timer_get_time(ts);
return true;
}
/**
* @brief Update the epochtime variable from the always-on timer
*
* If the always-on timer is running, copy its current value to the epochtime variable.
* Also, copy the current always-on timer value to the rtc_save structure and
* calculate a checksum of the structure.
*/
static void update_epochtime() {
bool ok = get_time(&rtc_save.ts);
if (!ok) return;
// Set the signature to the magic number
rtc_save.signature = 0xBABEBABE;
// Calculate the checksum of the structure
rtc_save.checksum = crc7((uint8_t *)&rtc_save, offsetof(rtc_save_t, checksum));
// Copy the seconds part of the always-on timer to the epochtime variable
epochtime = rtc_save.ts.tv_sec;
}
/**
* @brief Get the current time in seconds since the Epoch.
* *
* @param[in] pxTime If not NULL, the current time is copied here.
* @return The current time in seconds since the Epoch.
*/
time_t time(time_t *pxTime) {
update_epochtime();
if (pxTime) {
*pxTime = epochtime;
}
return epochtime;
}
/**
* @brief Initialize the always-on timer and save its value to the rtc_save structure
*
* If the always-on timer is already running, this function does nothing.
* Otherwise, it initializes the RTC if it is available and checks if the saved
* time is valid. If the saved time is valid, it sets the always-on timer to the saved
* value.
*/
void time_init() {
// If the always-on timer is already running, return immediately
if (aon_timer_is_running()) return;
// Initialize the RTC if it is available
#if HAS_RP2040_RTC
rtc_init();
#endif
// Check if the saved time is valid
char xor_checksum = crc7((uint8_t *)&rtc_save, offsetof(rtc_save_t, checksum));
bool ok = rtc_save.signature == 0xBABEBABE && rtc_save.checksum == xor_checksum;
// If the saved time is valid, set the always-on timer
if (ok) aon_timer_set_time(&rtc_save.ts);
}
/**
* @brief Get the current time in the FAT time format
*
* The FAT file system uses a specific date and time format that is different
* from the one used by the standard C library. This function converts the
* current time to the FAT time format.
*
* @return The current time in the FAT time format (DWORD)
*/
DWORD get_fattime(void) {
struct timespec ts;
bool ok = get_time(&ts);
if (!ok) return 0;
struct tm t;
localtime_r(&ts.tv_sec, &t);
DWORD fattime = 0;
// bit31:25
// Year origin from the 1980 (0..127, e.g. 37 for 2017)
// tm_year int years since 1900
int yr = t.tm_year + 1900 - 1980;
assert(yr >= 0 && yr <= 127);
fattime |= (0b01111111 & yr) << 25;
// bit24:21
// Month (1..12)
// tm_mon int months since January 0-11
uint8_t mo = t.tm_mon + 1;
assert(mo >= 1 && mo <= 12);
fattime |= (0b00001111 & mo) << 21;
// bit20:16
// Day of the month (1..31)
// tm_mday int day of the month 1-31
uint8_t da = t.tm_mday;
assert(da >= 1 && da <= 31);
fattime |= (0b00011111 & da) << 16;
// bit15:11
// Hour (0..23)
// tm_hour int hours since midnight 0-23
uint8_t hr = t.tm_hour;
assert(hr >= 0 && hr <= 23);
fattime |= (0b00011111 & hr) << 11;
// bit10:5
// Minute (0..59)
// tm_min int minutes after the hour 0-59
uint8_t mi = t.tm_min;
assert(mi >= 0 && mi <= 59);
fattime |= (0b00111111 & mi) << 5;
// bit4:0
// Second / 2 (0..29, e.g. 25 for 50)
// tm_sec int seconds after the minute 0-61*
uint8_t sd = t.tm_sec / 2;
assert(sd >= 0 && sd <= 30); // The extra range is to accommodate for leap seconds
fattime |= (0b00011111 & sd);
return fattime;
}
/* rtc.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 <time.h>
//
#include "pico/aon_timer.h"
#include "pico/stdio.h"
#include "pico/stdlib.h"
#include "pico/util/datetime.h"
//
#include "ff.h"
#include "util.h" // calculate_checksum
//
#include "rtc.h"
time_t epochtime;
// Make an attempt to save a recent time stamp across reset:
typedef struct rtc_save {
uint32_t signature;
struct timespec ts;
uint32_t checksum; // last, not included in checksum
} rtc_save_t;
static rtc_save_t rtc_save __attribute__((section(".uninitialized_data")));
static void update_epochtime() {
struct tm timeinfo;
aon_timer_get_time(&rtc_save.ts);
rtc_save.signature = 0xBABEBABE;
localtime_r(&rtc_save.ts.tv_sec, &timeinfo);
rtc_save.checksum = calculate_checksum((uint32_t *)&rtc_save,
offsetof(rtc_save_t, checksum));
epochtime = mktime(&timeinfo);
// configASSERT(-1 != epochtime);
}
time_t time(time_t *pxTime) {
update_epochtime();
if (pxTime) {
*pxTime = epochtime;
}
return epochtime;
}
void time_init() {
struct timespec ts;
aon_timer_get_time(&ts);
struct tm t, t2;
localtime_r(&ts.tv_sec, &t);
localtime_r(&rtc_save.ts.tv_sec, &t2);
if (t.tm_year != t2.tm_year) {
uint32_t xor_checksum = calculate_checksum(
(uint32_t *)&rtc_save, offsetof(rtc_save_t, checksum));
if (rtc_save.signature == 0xBABEBABE &&
rtc_save.checksum == xor_checksum) {
// Set rtc
aon_timer_set_time(&rtc_save.ts);
}
}
}
// Called by FatFs:
DWORD get_fattime(void) {
struct timespec ts;
aon_timer_get_time(&ts);
struct tm t;
localtime_r(&ts.tv_sec, &t);
DWORD fattime = 0;
// bit31:25
// Year origin from the 1980 (0..127, e.g. 37 for 2017)
uint8_t yr = t.tm_year - 1980;
fattime |= (0b01111111 & yr) << 25;
// bit24:21
// Month (1..12)
uint8_t mo = t.tm_mon;
fattime |= (0b00001111 & mo) << 21;
// bit20:16
// Day of the month (1..31)
uint8_t da = t.tm_mday;
fattime |= (0b00011111 & da) << 16;
// bit15:11
// Hour (0..23)
uint8_t hr = t.tm_hour;
fattime |= (0b00011111 & hr) << 11;
// bit10:5
// Minute (0..59)
uint8_t mi = t.tm_min;
fattime |= (0b00111111 & mi) << 5;
// bit4:0
// Second / 2 (0..29, e.g. 25 for 50)
uint8_t sd = t.tm_sec / 2;
fattime |= (0b00011111 & sd);
return fattime;
}
#include "sd_timeouts.h"
sd_timeouts_t sd_timeouts __attribute__((weak)) = {
.sd_command = 2000, // Timeout in ms for response
.sd_command_retries = 3, // Times SPI cmd is retried when there is no response
.sd_lock = 8000, // Timeout in ms for response
.sd_spi_read = 1000, // Timeout in ms for response
.sd_spi_write = 1000, // Timeout in ms for response
.sd_spi_write_read = 1000, // Timeout in ms for response
.spi_lock = 4000, // Timeout in ms for response
.rp2040_sdio_command_R1 = 5, // Timeout in ms for response
.rp2040_sdio_command_R2 = 2, // Timeout in ms for response
.rp2040_sdio_command_R3 = 2, // Timeout in ms for response
.rp2040_sdio_rx_poll = 1000, // Timeout in ms for response
.rp2040_sdio_tx_poll = 5000, // Timeout in ms for response
.sd_sdio_begin = 1000, // Timeout in ms for response
.sd_sdio_stopTransmission = 200, // Timeout in ms for response
};
/*
* util.c
*
* Created on: Apr 12, 2022
* Author: carlk
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
//
#include "util.h"
int gcd(int a,int b) {
int R;
while ((a % b) > 0) {
R = a % b;
a = b;
b = R;
}
return b;
}
char const* uint8_binary_str(uint8_t number) {
static char b[sizeof number * 8 + 1];
memset(b, 0, sizeof b);
for (size_t i = 0; i < sizeof number * 8; ++i) {
unsigned int mask = 1 << i;
if (number & mask)
b[sizeof number * 8 - 1 - i] = '1';
else
b[sizeof number * 8 - 1 - i] = '0';
}
return b;
}
char const* uint_binary_str(unsigned int number) {
static char b[sizeof number * 8 + 1];
memset(b, 0, sizeof b);
for (size_t i = 0; i < sizeof number * 8; ++i) {
unsigned int mask = 1 << i;
if (number & mask)
b[sizeof number * 8 - 1 - i] = '1';
else
b[sizeof number * 8 - 1 - i] = '0';
}
return b;
}
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
endif ()
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
endif()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
GIT_SUBMODULES_RECURSE FALSE
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
else ()
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
endif ()
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment