Commit ba31e3ab by tatsukiishikawa

init first labs

parent ef8e8a37
Showing with 4866 additions and 0 deletions
build
!.vscode/*
# == 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()
# ====================================================================================
set(PICO_BOARD pico_w CACHE STRING "Board type")
cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)
project(pico-10dof-imu_project)
pico_sdk_init()
add_subdirectory(icm20948)
include_directories(./icm20948)
add_executable(imu
main.c
)
pico_enable_stdio_usb(imu 1)
pico_enable_stdio_uart(imu 1)
pico_add_extra_outputs(imu)
# Pull in our pico_stdlib which pulls in commonly used features
target_link_libraries(imu pico_stdlib icm20948 hardware_i2c pico_multicore)
aux_source_directory(. DIR_icm20948_SRCS)
add_library(icm20948 ${DIR_icm20948_SRCS})
target_link_libraries(icm20948 PUBLIC hardware_i2c pico_stdlib)
#ifndef __ICM20948__H
#define __ICM20948__H
#include <stdio.h>
#include <math.h>
#include "hardware/i2c.h"
#include "pico/stdlib.h"
//typedef uint8_t bool;
#define true 1
#define false 0
/* define ICM-20948 Device I2C address*/
#define I2C_ADD_ICM20948 0x68
#define I2C_ADD_ICM20948_AK09916 0x0C
#define I2C_ADD_ICM20948_AK09916_READ 0x80
#define I2C_ADD_ICM20948_AK09916_WRITE 0x00
/* define ICM-20948 Register */
/* user bank 0 register */
#define REG_ADD_WIA 0x00
#define REG_VAL_WIA 0xEA
#define REG_ADD_USER_CTRL 0x03
#define REG_VAL_BIT_DMP_EN 0x80
#define REG_VAL_BIT_FIFO_EN 0x40
#define REG_VAL_BIT_I2C_MST_EN 0x20
#define REG_VAL_BIT_I2C_IF_DIS 0x10
#define REG_VAL_BIT_DMP_RST 0x08
#define REG_VAL_BIT_DIAMOND_DMP_RST 0x04
#define REG_ADD_PWR_MIGMT_1 0x06
#define REG_VAL_ALL_RGE_RESET 0x80
#define REG_VAL_RUN_MODE 0x01 //Non low-power mode
#define REG_ADD_LP_CONFIG 0x05
#define REG_ADD_PWR_MGMT_1 0x06
#define REG_ADD_PWR_MGMT_2 0x07
#define REG_ADD_ACCEL_XOUT_H 0x2D
#define REG_ADD_ACCEL_XOUT_L 0x2E
#define REG_ADD_ACCEL_YOUT_H 0x2F
#define REG_ADD_ACCEL_YOUT_L 0x30
#define REG_ADD_ACCEL_ZOUT_H 0x31
#define REG_ADD_ACCEL_ZOUT_L 0x32
#define REG_ADD_GYRO_XOUT_H 0x33
#define REG_ADD_GYRO_XOUT_L 0x34
#define REG_ADD_GYRO_YOUT_H 0x35
#define REG_ADD_GYRO_YOUT_L 0x36
#define REG_ADD_GYRO_ZOUT_H 0x37
#define REG_ADD_GYRO_ZOUT_L 0x38
#define REG_ADD_EXT_SENS_DATA_00 0x3B
#define REG_ADD_REG_BANK_SEL 0x7F
#define REG_VAL_REG_BANK_0 0x00
#define REG_VAL_REG_BANK_1 0x10
#define REG_VAL_REG_BANK_2 0x20
#define REG_VAL_REG_BANK_3 0x30
/* user bank 1 register */
/* user bank 2 register */
#define REG_ADD_GYRO_SMPLRT_DIV 0x00
#define REG_ADD_GYRO_CONFIG_1 0x01
#define REG_VAL_BIT_GYRO_DLPCFG_2 0x10 /* bit[5:3] */
#define REG_VAL_BIT_GYRO_DLPCFG_4 0x20 /* bit[5:3] */
#define REG_VAL_BIT_GYRO_DLPCFG_6 0x30 /* bit[5:3] */
#define REG_VAL_BIT_GYRO_FS_250DPS 0x00 /* bit[2:1] */
#define REG_VAL_BIT_GYRO_FS_500DPS 0x02 /* bit[2:1] */
#define REG_VAL_BIT_GYRO_FS_1000DPS 0x04 /* bit[2:1] */
#define REG_VAL_BIT_GYRO_FS_2000DPS 0x06 /* bit[2:1] */
#define REG_VAL_BIT_GYRO_DLPF 0x01 /* bit[0] */
#define REG_ADD_ACCEL_SMPLRT_DIV_2 0x11
#define REG_ADD_ACCEL_CONFIG 0x14
#define REG_VAL_BIT_ACCEL_DLPCFG_2 0x10 /* bit[5:3] */
#define REG_VAL_BIT_ACCEL_DLPCFG_4 0x20 /* bit[5:3] */
#define REG_VAL_BIT_ACCEL_DLPCFG_6 0x30 /* bit[5:3] */
#define REG_VAL_BIT_ACCEL_FS_2g 0x00 /* bit[2:1] */
#define REG_VAL_BIT_ACCEL_FS_4g 0x02 /* bit[2:1] */
#define REG_VAL_BIT_ACCEL_FS_8g 0x04 /* bit[2:1] */
#define REG_VAL_BIT_ACCEL_FS_16g 0x06 /* bit[2:1] */
#define REG_VAL_BIT_ACCEL_DLPF 0x01 /* bit[0] */
/* user bank 3 register */
#define REG_ADD_I2C_SLV0_ADDR 0x03
#define REG_ADD_I2C_SLV0_REG 0x04
#define REG_ADD_I2C_SLV0_CTRL 0x05
#define REG_VAL_BIT_SLV0_EN 0x80
#define REG_VAL_BIT_MASK_LEN 0x07
#define REG_ADD_I2C_SLV0_DO 0x06
#define REG_ADD_I2C_SLV1_ADDR 0x07
#define REG_ADD_I2C_SLV1_REG 0x08
#define REG_ADD_I2C_SLV1_CTRL 0x09
#define REG_ADD_I2C_SLV1_DO 0x0A
/* define ICM-20948 Register end */
/* define ICM-20948 MAG Register */
#define REG_ADD_MAG_WIA1 0x00
#define REG_VAL_MAG_WIA1 0x48
#define REG_ADD_MAG_WIA2 0x01
#define REG_VAL_MAG_WIA2 0x09
#define REG_ADD_MAG_ST2 0x10
#define REG_ADD_MAG_DATA 0x11
#define REG_ADD_MAG_CNTL2 0x31
#define REG_VAL_MAG_MODE_PD 0x00
#define REG_VAL_MAG_MODE_SM 0x01
#define REG_VAL_MAG_MODE_10HZ 0x02
#define REG_VAL_MAG_MODE_20HZ 0x04
#define REG_VAL_MAG_MODE_50HZ 0x05
#define REG_VAL_MAG_MODE_100HZ 0x08
#define REG_VAL_MAG_MODE_ST 0x10
/* define ICM-20948 MAG Register end */
#define MAG_DATA_LEN 6
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
IMU_EN_SENSOR_TYPE_NULL = 0,
IMU_EN_SENSOR_TYPE_ICM20948,
IMU_EN_SENSOR_TYPE_MAX
}IMU_EN_SENSOR_TYPE;
typedef struct imu_st_angles_data_tag
{
float fYaw;
float fPitch;
float fRoll;
}IMU_ST_ANGLES_DATA;
typedef struct imu_st_sensor_data_tag
{
int16_t s16X;
int16_t s16Y;
int16_t s16Z;
}IMU_ST_SENSOR_DATA;
typedef struct icm20948_st_avg_data_tag
{
uint8_t u8Index;
int16_t s16AvgBuffer[8];
}ICM20948_ST_AVG_DATA;
void imuInit(IMU_EN_SENSOR_TYPE *penMotionSensorType);
void imuDataAccGyrGet(IMU_ST_SENSOR_DATA *pstGyroRawData,
IMU_ST_SENSOR_DATA *pstAccelRawData);
void imuDataGet(IMU_ST_ANGLES_DATA *pstAngles,
IMU_ST_SENSOR_DATA *pstGyroRawData,
IMU_ST_SENSOR_DATA *pstAccelRawData,
IMU_ST_SENSOR_DATA *pstMagnRawData);
void icm20948GyroRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z);
void icm20948AccelRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z);
void icm20948GyroFastRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z);
void icm20948AccelFastRead(int16_t* ps16X, int16_t* ps16Y, int16_t* ps16Z);
char I2C_ReadOneByte(char reg);
void I2C_WriteOneByte(char reg, char val);
#ifdef __cplusplus
}
#endif
#endif
#include "icm20948.h"
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/time.h"
#include "pico/multicore.h" // only used by main_4
#include "pico/util/queue.h" // only used by main_4
int main_1(void)
{
stdio_init_all();
IMU_EN_SENSOR_TYPE enMotionSensorType;
IMU_ST_ANGLES_DATA stAngles;
IMU_ST_SENSOR_DATA stGyroRawData;
IMU_ST_SENSOR_DATA stAccelRawData;
IMU_ST_SENSOR_DATA stMagnRawData;
imuInit(&enMotionSensorType);
if (IMU_EN_SENSOR_TYPE_ICM20948 == enMotionSensorType) {
printf("Motion sensor is ICM-20948\n");
} else {
printf("Motion sensor NULL\n");
}
uint64_t t_prev = time_us_64();
while (1) {
imuDataGet(&stAngles, &stGyroRawData, &stAccelRawData, &stMagnRawData);
uint64_t t_now = time_us_64();
uint64_t dt_us = (t_now - t_prev);
t_prev = t_now;
float hz = (dt_us > 0) ? (1000000.0f / (float)dt_us) : 0.0f;
printf("\r\n/-------------------------------------------------------------/\r\n");
printf("Roll: %.2f Pitch: %.2f Yaw: %.2f | Sample Rate: %.1f Hz\r\n",
stAngles.fRoll, stAngles.fPitch, stAngles.fYaw, hz);
//sleep_ms(100);
}
return 0;
}
int main_2(void)
{
stdio_init_all();
IMU_EN_SENSOR_TYPE type;
IMU_ST_SENSOR_DATA g, a;
imuInit(&type);
if (IMU_EN_SENSOR_TYPE_ICM20948 == type) {
printf("Motion sensor is ICM-20948\n");
} else {
printf("Motion sensor NULL\n");
}
uint64_t t_prev = time_us_64();
while (1) {
int16_t gx, gy, gz, ax, ay, az;
icm20948AccelRead(&ax, &ay, &az);
icm20948GyroRead (&gx, &gy, &gz);
uint64_t t_now = time_us_64();
uint64_t dt_us = (t_now - t_prev);
t_prev = t_now;
float hz = (dt_us > 0) ? (1000000.0f / (float)dt_us) : 0.0f;
printf("ACC [mg-ish raw]: X=%d Y=%d Z=%d | GYRO [LSB]: X=%d Y=%d Z=%d | %.1f Hz\r\n",
ax, ay, az, gx, gy, gz, hz);
// sleep_ms(5); // small delay to avoid spamming
}
return 0;
}
int main_3(void)
{
stdio_init_all();
IMU_EN_SENSOR_TYPE type;
imuInit(&type);
if (IMU_EN_SENSOR_TYPE_ICM20948 == type) {
printf("Motion sensor is ICM-20948 (FAST read path)\n");
} else {
printf("Motion sensor NULL\n");
}
uint64_t t_prev = time_us_64();
while (1) {
int16_t gx, gy, gz, ax, ay, az;
// Option A: call the fast functions directly
icm20948AccelFastRead(&ax, &ay, &az);
icm20948GyroFastRead (&gx, &gy, &gz);
// // Option B: if you prefer the wrapper:
// IMU_ST_SENSOR_DATA g, a;
// imuDataOnlyGet(&g, &a);
// gx=g.s16X; gy=g.s16Y; gz=g.s16Z;
// ax=a.s16X; ay=a.s16Y; az=a.s16Z;
uint64_t t_now = time_us_64();
uint64_t dt_us = (t_now - t_prev);
t_prev = t_now;
float hz = (dt_us > 0) ? (1000000.0f / (float)dt_us) : 0.0f;
printf("FAST ACC: X=%d Y=%d Z=%d | FAST GYRO: X=%d Y=%d Z=%d | %.1f Hz\r\n",
ax, ay, az, gx, gy, gz, hz);
// No sleep => let it run as fast as possible
}
return 0;
}
typedef struct {
int16_t ax, ay, az;
int16_t gx, gy, gz;
uint32_t t_us; // timestamp (lower 32 bits is fine for short runs)
} Sample;
static queue_t sample_q;
static void core1_reader(void)
{
IMU_EN_SENSOR_TYPE type;
// Important: init I2C/IMU on this core as well if your drivers require per-core context.
// Usually one init on core0 is fine if both cores share the same hardware state,
// but to be safe we at least check sensor here.
// If needed, comment out the next line.
// imuInit(&type);
uint32_t t_prev = (uint32_t)time_us_64();
while (1) {
IMU_ST_SENSOR_DATA stGyroRawData, stAccelRawData;
// int16_t gx, gy, gz, ax, ay, az;
// icm20948AccelFastRead(&ax, &ay, &az);
// icm20948GyroFastRead (&gx, &gy, &gz);
imuDataAccGyrGet(&stGyroRawData, &stAccelRawData);
uint32_t t_now = (uint32_t)time_us_64();
Sample s = { stAccelRawData.s16X, stAccelRawData.s16Y, stAccelRawData.s16Z,
stGyroRawData.s16X, stGyroRawData.s16Y, stGyroRawData.s16Z,
t_now };
queue_add_blocking(&sample_q, &s);
// (Optional) pace the producer slightly if needed
// sleep_us(500); // ~2 kHz -> uncomment to throttle
}
}
int main_4(void)
{
stdio_init_all();
IMU_EN_SENSOR_TYPE type;
imuInit(&type);
if (IMU_EN_SENSOR_TYPE_ICM20948 == type) {
printf("Motion sensor is ICM-20948 (multicore)\n");
} else {
printf("Motion sensor NULL\n");
}
// Queue can hold up to N samples; adjust for your bandwidth
queue_init(&sample_q, sizeof(Sample), 64);
// Launch core1 reader
multicore_launch_core1(core1_reader);
uint32_t t_prev = (uint32_t)time_us_64();
while (1) {
Sample s;
queue_remove_blocking(&sample_q, &s);
uint32_t t_now = (uint32_t)time_us_64();
uint32_t dt_us = (t_now - t_prev);
t_prev = t_now;
float hz = (dt_us > 0) ? (1000000.0f / (float)dt_us) : 0.0f;
printf("ACC: X=%d Y=%d Z=%d | GYRO: X=%d Y=%d Z=%d | RX Rate: %.1f Hz\r\n",
s.ax, s.ay, s.az, s.gx, s.gy, s.gz, hz);
// No sleep: printing rate is now bounded mostly by USB/serial throughput.
}
return 0;
}
# 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})
build
!.vscode/*
# == 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()
# ====================================================================================
set(PICO_BOARD pico CACHE STRING "Board type")
set(PROGRAM_NAME lcd_draw)
cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)
project(Pico-ResTouch-X_X_code)
pico_sdk_init()
add_subdirectory(lib/config)
add_subdirectory(lib/lcd)
add_subdirectory(lib/font)
include_directories(./lib/config)
include_directories(./lib/lcd)
include_directories(./lib/font)
add_executable(${PROGRAM_NAME}
main.c
)
# enable usb output, disable uart output
pico_enable_stdio_usb(${PROGRAM_NAME} 1)
pico_enable_stdio_uart(${PROGRAM_NAME} 0)
# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(${PROGRAM_NAME})
target_link_libraries(${PROGRAM_NAME}
lcd
font
config
pico_stdlib
hardware_spi
hardware_pwm
)
# 查找当前目录下的所有源文件
# 并将名称保存到 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_
#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);
#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
#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);
#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
#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);
#endif
#ifndef __LCD_TOUCH_H_
#define __LCD_TOUCH_H_
#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"
#define TP_PRESS_DOWN 0x80
#define TP_PRESSED 0x40
//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;
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 );
#endif
#include "LCD_Driver.h"
#include "LCD_Touch.h"
#include "LCD_GUI.h"
#include "DEV_Config.h"
#include <stdio.h>
#include "hardware/watchdog.h"
#include "pico/stdlib.h"
int main(void)
{
uint8_t counter = 0;
System_Init();
LCD_SCAN_DIR lcd_scan_dir = SCAN_DIR_DFT;
LCD_Init(lcd_scan_dir,1000);
TP_Init(lcd_scan_dir);
LCD_SCAN_DIR bmp_scan_dir = D2U_R2L;
TP_GetAdFac();
TP_Dialog();
uint16_t cnt=0;
while(1){
for(cnt=1000;cnt>2;cnt--)
{
LCD_SetBackLight(1000);
TP_DrawBoard();
}
}
return 0;
}
\ No newline at end of file
# 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})
build
!.vscode/*
# == 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()
# ====================================================================================
set(PICO_BOARD pico CACHE STRING "Board type")
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
project(main C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# --- Executable and microphone sources ---
add_executable(main
main.c
${CMAKE_CURRENT_LIST_DIR}/src/pdm_microphone.c
${CMAKE_CURRENT_LIST_DIR}/src/OpenPDM2PCM/OpenPDMFilter.c
)
# Generate PIO header for the mic
pico_generate_pio_header(main
${CMAKE_CURRENT_LIST_DIR}/src/pdm_microphone.pio
)
# Include directories (note the src/include path gets added)
target_include_directories(main PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src
${CMAKE_CURRENT_LIST_DIR}/src/include
${CMAKE_CURRENT_LIST_DIR}/src/OpenPDM2PCM
${CMAKE_CURRENT_BINARY_DIR} # for staged "pico/pdm_microphone.h"
)
# --- Stage a virtual include so "pico/pdm_microphone.h" resolves ---
set(PDM_HDR_SRC ${CMAKE_CURRENT_LIST_DIR}/src/include/pico/pdm_microphone.h)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pico/pdm_microphone.h
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/pico
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PDM_HDR_SRC}
${CMAKE_CURRENT_BINARY_DIR}/pico/pdm_microphone.h
DEPENDS ${PDM_HDR_SRC}
COMMENT "Staging pico/pdm_microphone.h from src/include into build dir"
VERBATIM
)
add_custom_target(pdm_header ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/pico/pdm_microphone.h
)
add_dependencies(main pdm_header)
# Link libraries
target_link_libraries(main PRIVATE
pico_stdlib
hardware_dma
hardware_pio
)
# Program metadata and I/O
pico_set_program_name(main "main")
pico_set_program_version(main "0.1")
pico_enable_stdio_uart(main 0)
pico_enable_stdio_usb(main 1)
# Create UF2, bin, etc.
pico_add_extra_outputs(main)
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "stdlib.h"
#include "src/include/pico/pdm_microphone.h"
#include "tusb.h"
#define AUDIO_SAMPLE_RATE 16000
#define SAMPLE_BUFFER_SIZE 256
pdm_microphone_config config = {
// PDM ID
.pdm_id = 0,
.dma_irq = DMA_IRQ_0,
// PIO
.pio = pio0,
// GPIO pin for the PDM DAT signal
.gpio_data = 18,
// GPIO pin for the PDM CLK signal
.gpio_clk = 19,
// sample rate in Hz
.sample_rate = AUDIO_SAMPLE_RATE,
// number of samples to buffer
.sample_buffer_size = SAMPLE_BUFFER_SIZE,
};
pdm_mic_obj* pdm_mic;
int16_t sample_buffer[SAMPLE_BUFFER_SIZE];
volatile bool data_valid = false;
void on_pdm_samples_ready(uint8_t pdm_id) {
(void)pdm_id;
data_valid = true;
}
int main(void)
{
stdio_init_all();
while (!tud_cdc_connected()) {
sleep_ms(10);
}
printf("hello PDM microphone\n");
pdm_mic = pdm_microphone_init(&config);
if (!pdm_mic) {
printf("PDM microphone initialization failed!\n");
while (1) { tight_loop_contents(); }
}
printf("PDM microphone initialized\n");
pdm_microphone_set_samples_ready_handler(pdm_mic, on_pdm_samples_ready);
if (pdm_microphone_start(pdm_mic) != 0) {
printf("PDM microphone start failed!\n");
while (1) { tight_loop_contents(); }
}
printf("PDM microphone started\n");
uint32_t printed = 0;
while (true) {
if (data_valid) {
data_valid = false;
int n = pdm_microphone_read(pdm_mic, sample_buffer, SAMPLE_BUFFER_SIZE);
if (n > 0) {
for (int i = 0; i < n; i++) {
printf("%04x\n", (uint16_t)sample_buffer[i]);
}
}
}
sleep_ms(1);
}
}
\ No newline at end of file
# 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})
/**
*******************************************************************************
* @file OpenPDMFilter.c
* @author CL
* @version V1.0.0
* @date 9-September-2015
* @brief Open PDM audio software decoding Library.
* This Library is used to decode and reconstruct the audio signal
* produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
*******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT 2018 STMicroelectronics</center></h2>
*
* 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.
*******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "OpenPDMFilter.h"
/* Variables -----------------------------------------------------------------*/
uint32_t div_const = 0;
int64_t sub_const = 0;
uint32_t sinc[DECIMATION_MAX * SINCN];
uint32_t sinc1[DECIMATION_MAX];
uint32_t sinc2[DECIMATION_MAX * 2];
uint32_t coef[SINCN][DECIMATION_MAX];
#ifdef USE_LUT
int32_t lut[256][DECIMATION_MAX / 8][SINCN];
#endif
/* Functions -----------------------------------------------------------------*/
#ifdef USE_LUT
int32_t filter_table_mono_64(uint8_t *data, uint8_t sincn)
{
return (int32_t)
lut[data[0]][0][sincn] +
lut[data[1]][1][sincn] +
lut[data[2]][2][sincn] +
lut[data[3]][3][sincn] +
lut[data[4]][4][sincn] +
lut[data[5]][5][sincn] +
lut[data[6]][6][sincn] +
lut[data[7]][7][sincn];
}
int32_t filter_table_stereo_64(uint8_t *data, uint8_t sincn)
{
return (int32_t)
lut[data[0]][0][sincn] +
lut[data[2]][1][sincn] +
lut[data[4]][2][sincn] +
lut[data[6]][3][sincn] +
lut[data[8]][4][sincn] +
lut[data[10]][5][sincn] +
lut[data[12]][6][sincn] +
lut[data[14]][7][sincn];
}
int32_t filter_table_mono_128(uint8_t *data, uint8_t sincn)
{
return (int32_t)
lut[data[0]][0][sincn] +
lut[data[1]][1][sincn] +
lut[data[2]][2][sincn] +
lut[data[3]][3][sincn] +
lut[data[4]][4][sincn] +
lut[data[5]][5][sincn] +
lut[data[6]][6][sincn] +
lut[data[7]][7][sincn] +
lut[data[8]][8][sincn] +
lut[data[9]][9][sincn] +
lut[data[10]][10][sincn] +
lut[data[11]][11][sincn] +
lut[data[12]][12][sincn] +
lut[data[13]][13][sincn] +
lut[data[14]][14][sincn] +
lut[data[15]][15][sincn];
}
int32_t filter_table_stereo_128(uint8_t *data, uint8_t sincn)
{
return (int32_t)
lut[data[0]][0][sincn] +
lut[data[2]][1][sincn] +
lut[data[4]][2][sincn] +
lut[data[6]][3][sincn] +
lut[data[8]][4][sincn] +
lut[data[10]][5][sincn] +
lut[data[12]][6][sincn] +
lut[data[14]][7][sincn] +
lut[data[16]][8][sincn] +
lut[data[18]][9][sincn] +
lut[data[20]][10][sincn] +
lut[data[22]][11][sincn] +
lut[data[24]][12][sincn] +
lut[data[26]][13][sincn] +
lut[data[28]][14][sincn] +
lut[data[30]][15][sincn];
}
int32_t (* filter_tables_64[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_64, filter_table_stereo_64};
int32_t (* filter_tables_128[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_128, filter_table_stereo_128};
#else
int32_t filter_table(uint8_t *data, uint8_t sincn, TPDMFilter_InitStruct *param)
{
uint8_t c, i;
uint16_t data_index = 0;
uint32_t *coef_p = &coef[sincn][0];
int32_t F = 0;
uint8_t decimation = param->Decimation;
uint8_t channels = param->In_MicChannels;
for (i = 0; i < decimation; i += 8) {
c = data[data_index];
F += ((c >> 7) ) * coef_p[i ] +
((c >> 6) & 0x01) * coef_p[i + 1] +
((c >> 5) & 0x01) * coef_p[i + 2] +
((c >> 4) & 0x01) * coef_p[i + 3] +
((c >> 3) & 0x01) * coef_p[i + 4] +
((c >> 2) & 0x01) * coef_p[i + 5] +
((c >> 1) & 0x01) * coef_p[i + 6] +
((c ) & 0x01) * coef_p[i + 7];
data_index += channels;
}
return F;
}
#endif
void convolve(uint32_t Signal[/* SignalLen */], unsigned short SignalLen,
uint32_t Kernel[/* KernelLen */], unsigned short KernelLen,
uint32_t Result[/* SignalLen + KernelLen - 1 */])
{
uint16_t n;
for (n = 0; n < SignalLen + KernelLen - 1; n++)
{
unsigned short kmin, kmax, k;
Result[n] = 0;
kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0;
kmax = (n < SignalLen - 1) ? n : SignalLen - 1;
for (k = kmin; k <= kmax; k++) {
Result[n] += Signal[k] * Kernel[n - k];
}
}
}
void Open_PDM_Filter_Init(TPDMFilter_InitStruct *Param)
{
uint16_t i, j;
int64_t sum = 0;
uint8_t decimation = Param->Decimation;
for (i = 0; i < SINCN; i++) {
Param->Coef[i] = 0;
Param->bit[i] = 0;
}
for (i = 0; i < decimation; i++) {
sinc1[i] = 1;
}
Param->OldOut = Param->OldIn = Param->OldZ = 0;
Param->LP_ALFA = (Param->LP_HZ != 0 ? (uint16_t) (Param->LP_HZ * 256 / (Param->LP_HZ + Param->Fs / (2 * 3.14159))) : 0);
Param->HP_ALFA = (Param->HP_HZ != 0 ? (uint16_t) (Param->Fs * 256 / (2 * 3.14159 * Param->HP_HZ + Param->Fs)) : 0);
Param->FilterLen = decimation * SINCN;
sinc[0] = 0;
sinc[decimation * SINCN - 1] = 0;
convolve(sinc1, decimation, sinc1, decimation, sinc2);
convolve(sinc2, decimation * 2 - 1, sinc1, decimation, &sinc[1]);
for(j = 0; j < SINCN; j++) {
for (i = 0; i < decimation; i++) {
coef[j][i] = sinc[j * decimation + i];
sum += sinc[j * decimation + i];
}
}
sub_const = sum >> 1;
div_const = sub_const * Param->MaxVolume / 32768 / FILTER_GAIN;
div_const = (div_const == 0 ? 1 : div_const);
#ifdef USE_LUT
/* Look-Up Table. */
uint16_t c, d, s;
for (s = 0; s < SINCN; s++)
{
uint32_t *coef_p = &coef[s][0];
for (c = 0; c < 256; c++)
for (d = 0; d < decimation / 8; d++)
lut[c][d][s] = ((c >> 7) ) * coef_p[d * 8 ] +
((c >> 6) & 0x01) * coef_p[d * 8 + 1] +
((c >> 5) & 0x01) * coef_p[d * 8 + 2] +
((c >> 4) & 0x01) * coef_p[d * 8 + 3] +
((c >> 3) & 0x01) * coef_p[d * 8 + 4] +
((c >> 2) & 0x01) * coef_p[d * 8 + 5] +
((c >> 1) & 0x01) * coef_p[d * 8 + 6] +
((c ) & 0x01) * coef_p[d * 8 + 7];
}
#endif
}
void Open_PDM_Filter_64(uint8_t* data, uint16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
{
uint8_t i, data_out_index;
uint8_t channels = Param->In_MicChannels;
uint8_t data_inc = ((DECIMATION_MAX >> 4) * channels);
int64_t Z, Z0, Z1, Z2;
int64_t OldOut, OldIn, OldZ;
OldOut = Param->OldOut;
OldIn = Param->OldIn;
OldZ = Param->OldZ;
#ifdef USE_LUT
uint8_t j = channels - 1;
#endif
for (i = 0, data_out_index = 0; i < Param->Fs / 1000; i++, data_out_index += channels) {
#ifdef USE_LUT
Z0 = filter_tables_64[j](data, 0);
Z1 = filter_tables_64[j](data, 1);
Z2 = filter_tables_64[j](data, 2);
#else
Z0 = filter_table(data, 0, Param);
Z1 = filter_table(data, 1, Param);
Z2 = filter_table(data, 2, Param);
#endif
Z = Param->Coef[1] + Z2 - sub_const;
Param->Coef[1] = Param->Coef[0] + Z1;
Param->Coef[0] = Z0;
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
OldIn = Z;
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
Z = OldZ * volume;
Z = RoundDiv(Z, div_const);
Z = SaturaLH(Z, -32700, 32700);
dataOut[data_out_index] = Z;
data += data_inc;
}
Param->OldOut = OldOut;
Param->OldIn = OldIn;
Param->OldZ = OldZ;
}
void Open_PDM_Filter_128(uint8_t* data, uint16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
{
uint8_t i, data_out_index;
uint8_t channels = Param->In_MicChannels;
uint8_t data_inc = ((DECIMATION_MAX >> 3) * channels);
int64_t Z, Z0, Z1, Z2;
int64_t OldOut, OldIn, OldZ;
OldOut = Param->OldOut;
OldIn = Param->OldIn;
OldZ = Param->OldZ;
#ifdef USE_LUT
uint8_t j = channels - 1;
#endif
for (i = 0, data_out_index = 0; i < Param->Fs / 1000; i++, data_out_index += channels) {
#ifdef USE_LUT
Z0 = filter_tables_128[j](data, 0);
Z1 = filter_tables_128[j](data, 1);
Z2 = filter_tables_128[j](data, 2);
#else
Z0 = filter_table(data, 0, Param);
Z1 = filter_table(data, 1, Param);
Z2 = filter_table(data, 2, Param);
#endif
Z = Param->Coef[1] + Z2 - sub_const;
Param->Coef[1] = Param->Coef[0] + Z1;
Param->Coef[0] = Z0;
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
OldIn = Z;
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
Z = OldZ * volume;
Z = RoundDiv(Z, div_const);
Z = SaturaLH(Z, -32700, 32700);
dataOut[data_out_index] = Z;
data += data_inc;
}
Param->OldOut = OldOut;
Param->OldIn = OldIn;
Param->OldZ = OldZ;
}
/**
*******************************************************************************
* @file OpenPDMFilter.h
* @author CL
* @version V1.0.0
* @date 9-September-2015
* @brief Header file for Open PDM audio software decoding Library.
* This Library is used to decode and reconstruct the audio signal
* produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
*******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT 2018 STMicroelectronics</center></h2>
*
* 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.
*******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __OPENPDMFILTER_H
#define __OPENPDMFILTER_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
/* Definitions ---------------------------------------------------------------*/
/*
* Enable to use a Look-Up Table to improve performances while using more FLASH
* and RAM memory.
* Note: Without Look-Up Table up to stereo@16KHz configuration is supported.
*/
#define USE_LUT
#define SINCN 3
#define DECIMATION_MAX 128
#ifdef PICO_BUILD
#define FILTER_GAIN Param->Gain
#else
#define FILTER_GAIN 16
#endif
#define HTONS(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
(((uint16_t)(A) & 0x00ff) << 8))
#define RoundDiv(a, b) (((a)>0)?(((a)+(b)/2)/(b)):(((a)-(b)/2)/(b)))
#define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N)))
/* Types ---------------------------------------------------------------------*/
typedef struct {
/* Public */
float LP_HZ;
float HP_HZ;
uint16_t Fs;
uint8_t In_MicChannels;
uint8_t Out_MicChannels;
uint8_t Decimation;
uint8_t MaxVolume;
#ifdef PICO_BUILD
uint8_t Gain;
#endif
/* Private */
uint32_t Coef[SINCN];
uint16_t FilterLen;
int64_t OldOut, OldIn, OldZ;
uint16_t LP_ALFA;
uint16_t HP_ALFA;
uint16_t bit[5];
uint16_t byte;
} TPDMFilter_InitStruct;
/* Exported functions ------------------------------------------------------- */
void Open_PDM_Filter_Init(TPDMFilter_InitStruct *init_struct);
void Open_PDM_Filter_64(uint8_t* data, uint16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct);
void Open_PDM_Filter_128(uint8_t* data, uint16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct);
#ifdef __cplusplus
}
#endif
#endif // __OPENPDMFILTER_H
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/*
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#include <stdlib.h>
#include <string.h>
#include "hardware/adc.h"
#include "hardware/clocks.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "pico/analog_microphone.h"
#define ANALOG_RAW_BUFFER_COUNT 2
static struct {
struct analog_microphone_config config;
int dma_channel;
uint16_t* raw_buffer[ANALOG_RAW_BUFFER_COUNT];
volatile int raw_buffer_write_index;
volatile int raw_buffer_read_index;
uint buffer_size;
int16_t bias;
uint dma_irq;
analog_samples_ready_handler_t samples_ready_handler;
} analog_mic;
static void analog_dma_handler();
int analog_microphone_init(const struct analog_microphone_config* config) {
memset(&analog_mic, 0x00, sizeof(analog_mic));
memcpy(&analog_mic.config, config, sizeof(analog_mic.config));
if (config->gpio < 26 || config->gpio > 29) {
return -1;
}
size_t raw_buffer_size = config->sample_buffer_size * sizeof(analog_mic.raw_buffer[0][0]);
analog_mic.buffer_size = config->sample_buffer_size;
analog_mic.bias = ((int16_t)((config->bias_voltage * 4095) / 3.3));
for (int i = 0; i < ANALOG_RAW_BUFFER_COUNT; i++) {
analog_mic.raw_buffer[i] = malloc(raw_buffer_size);
if (analog_mic.raw_buffer[i] == NULL) {
analog_microphone_deinit();
return -1;
}
}
analog_mic.dma_channel = dma_claim_unused_channel(true);
if (analog_mic.dma_channel < 0) {
analog_microphone_deinit();
return -1;
}
float clk_div = (clock_get_hz(clk_adc) / (1.0 * config->sample_rate)) - 1;
dma_channel_config dma_channel_cfg = dma_channel_get_default_config(analog_mic.dma_channel);
channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_16);
channel_config_set_read_increment(&dma_channel_cfg, false);
channel_config_set_write_increment(&dma_channel_cfg, true);
channel_config_set_dreq(&dma_channel_cfg, DREQ_ADC);
analog_mic.dma_irq = DMA_IRQ_0;
dma_channel_configure(
analog_mic.dma_channel,
&dma_channel_cfg,
analog_mic.raw_buffer[0],
&adc_hw->fifo,
analog_mic.buffer_size,
false
);
adc_gpio_init(config->gpio);
adc_init();
adc_select_input(config->gpio - 26);
adc_fifo_setup(
true, // Write each completed conversion to the sample FIFO
true, // Enable DMA data request (DREQ)
1, // DREQ (and IRQ) asserted when at least 1 sample present
false, // We won't see the ERR bit because of 8 bit reads; disable.
false // Don't shift each sample to 8 bits when pushing to FIFO
);
adc_set_clkdiv(clk_div);
}
void analog_microphone_deinit() {
for (int i = 0; i < ANALOG_RAW_BUFFER_COUNT; i++) {
if (analog_mic.raw_buffer[i]) {
free(analog_mic.raw_buffer[i]);
analog_mic.raw_buffer[i] = NULL;
}
}
if (analog_mic.dma_channel > -1) {
dma_channel_unclaim(analog_mic.dma_channel);
analog_mic.dma_channel = -1;
}
}
int analog_microphone_start() {
irq_set_enabled(analog_mic.dma_irq, true);
irq_set_exclusive_handler(analog_mic.dma_irq, analog_dma_handler);
if (analog_mic.dma_irq == DMA_IRQ_0) {
dma_channel_set_irq0_enabled(analog_mic.dma_channel, true);
} else if (analog_mic.dma_irq == DMA_IRQ_1) {
dma_channel_set_irq1_enabled(analog_mic.dma_channel, true);
} else {
return -1;
}
analog_mic.raw_buffer_write_index = 0;
analog_mic.raw_buffer_read_index = 0;
dma_channel_transfer_to_buffer_now(
analog_mic.dma_channel,
analog_mic.raw_buffer[0],
analog_mic.buffer_size
);
adc_run(true); // start running the adc
}
void analog_microphone_stop() {
adc_run(false); // stop running the adc
dma_channel_abort(analog_mic.dma_channel);
if (analog_mic.dma_irq == DMA_IRQ_0) {
dma_channel_set_irq0_enabled(analog_mic.dma_channel, false);
} else if (analog_mic.dma_irq == DMA_IRQ_1) {
dma_channel_set_irq1_enabled(analog_mic.dma_channel, false);
}
irq_set_enabled(analog_mic.dma_irq, false);
}
static void analog_dma_handler() {
// clear IRQ
if (analog_mic.dma_irq == DMA_IRQ_0) {
dma_hw->ints0 = (1u << analog_mic.dma_channel);
} else if (analog_mic.dma_irq == DMA_IRQ_1) {
dma_hw->ints1 = (1u << analog_mic.dma_channel);
}
// get the current buffer index
analog_mic.raw_buffer_read_index = analog_mic.raw_buffer_write_index;
// get the next capture index to send the dma to start
analog_mic.raw_buffer_write_index = (analog_mic.raw_buffer_write_index + 1) % ANALOG_RAW_BUFFER_COUNT;
// give the channel a new buffer to write to and re-trigger it
dma_channel_transfer_to_buffer_now(
analog_mic.dma_channel,
analog_mic.raw_buffer[analog_mic.raw_buffer_write_index],
analog_mic.buffer_size
);
if (analog_mic.samples_ready_handler) {
analog_mic.samples_ready_handler();
}
}
void analog_microphone_set_samples_ready_handler(analog_samples_ready_handler_t handler) {
analog_mic.samples_ready_handler = handler;
}
int analog_microphone_read(int16_t* buffer, size_t samples) {
if (samples > analog_mic.config.sample_buffer_size) {
samples = analog_mic.config.sample_buffer_size;
}
if (analog_mic.raw_buffer_write_index == analog_mic.raw_buffer_read_index) {
return 0;
}
uint16_t* in = analog_mic.raw_buffer[analog_mic.raw_buffer_read_index];
int16_t* out = buffer;
int16_t bias = analog_mic.bias;
analog_mic.raw_buffer_read_index++;
for (int i = 0; i < samples; i++) {
*out++ = *in++ - bias;
}
return samples;
}
; Receive a mono or stereo I2S audio stream as stereo
; This is 64 bits per sample; can be altered by modifying the "set" params,
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
;
; Autopull must be enabled, with threshold set to 64.
; Since I2S is MSB-first, shift direction should be to left.
; Hence the format of the FIFO word is:
;
; | 31 : 0 | 31 : 0 |
; | sample ws=0 | sample ws=1 |
;
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
; Fractional divider will probably be needed to get correct bit clock period,
; but for common syslck freqs this should still give a constant word select period.
;
; One output pin is used for the data output.
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
; Send 32 bit words to the PIO for mono, 64 bit words for stereo
.program audio_i2s_rx_32b
.side_set 2
; /--- LRCLK
; |/-- BCLK
; ||
.wrap_target
set x, 30 side 0b00
left_channel:
in pins, 1 side 0b01
jmp x-- left_channel side 0b00
in pins, 1 side 0b11
set x, 30 side 0b10
right_channel:
in pins, 1 side 0b11
jmp x-- right_channel side 0b10
in pins, 1 side 0b01
.wrap
% c-sdk {
%}
\ No newline at end of file
; Transmit a mono or stereo I2S audio stream as stereo
; This is 16 bits per sample; can be altered by modifying the "set" params,
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
;
; Autopull must be enabled, with threshold set to 32.
; Since I2S is MSB-first, shift direction should be to left.
; Hence the format of the FIFO word is:
;
; | 31 : 16 | 15 : 0 |
; | sample ws=0 | sample ws=1 |
;
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
; Fractional divider will probably be needed to get correct bit clock period,
; but for common syslck freqs this should still give a constant word select period.
;
; One output pin is used for the data output.
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
; Send 16 bit words to the PIO for mono, 32 bit words for stereo
.program audio_i2s_tx_16b
.side_set 2
; /--- LRCLK
; |/-- BCLK
; ||
.wrap_target
set x, 14 side 0b01
left_channel:
out pins, 1 side 0b00
jmp x-- left_channel side 0b01
out pins, 1 side 0b10
set x, 14 side 0b11
right_channel:
out pins, 1 side 0b10
jmp x-- right_channel side 0b11
public entry_point:
out pins, 1 side 0b00
.wrap
% c-sdk {
static inline void audio_i2s_tx_16b_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
pio_sm_config sm_config = audio_i2s_tx_16b_program_get_default_config(offset);
sm_config_set_out_pins(&sm_config, data_pin, 1);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_out_shift(&sm_config, false, true, 32);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_tx_16b_offset_entry_point));
}
%}
\ No newline at end of file
; Transmit a mono or stereo I2S audio stream as stereo
; This is 64 bits per sample; can be altered by modifying the "set" params,
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
;
; Autopull must be enabled, with threshold set to 64.
; Since I2S is MSB-first, shift direction should be to left.
; Hence the format of the FIFO word is:
;
; | 31 : 0 | 31 : 0 |
; | sample ws=0 | sample ws=1 |
;
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
; Fractional divider will probably be needed to get correct bit clock period,
; but for common syslck freqs this should still give a constant word select period.
;
; One output pin is used for the data output.
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
; Send 32 bit words to the PIO for mono, 64 bit words for stereo
.program audio_i2s_tx_32b
.side_set 2
; /--- LRCLK
; |/-- BCLK
; ||
.wrap_target
set x, 30 side 0b01
left_channel:
out pins, 1 side 0b00
jmp x-- left_channel side 0b01
out pins, 1 side 0b10
set x, 30 side 0b11
right_channel:
out pins, 1 side 0b10
jmp x-- right_channel side 0b11
public entry_point:
out pins, 1 side 0b00
.wrap
% c-sdk {
static inline void audio_i2s_tx_32b_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
pio_sm_config sm_config = audio_i2s_tx_32b_program_get_default_config(offset);
sm_config_set_out_pins(&sm_config, data_pin, 1);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_out_shift(&sm_config, false, true, 32);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_tx_32b_offset_entry_point));
}
%}
\ No newline at end of file
#include "pico/dc_offset_filter.h"
void dc_offset_filter_init(dc_offset_filter_t* self, int32_t apply_delay_samples){
self->sample_mean_value_sum = 0;
self->sample_mean_value_cntr = 0;
self->sample_mean_value = 0;
self->apply_delay_samples = apply_delay_samples;
}
int32_t dc_offset_filter_main(dc_offset_filter_t* self, int32_t input_sample, bool update_mean_val){
int32_t sample_mean_value_approx;
// Update mean value
if((update_mean_val == true) && (self->sample_mean_value_cntr >= self->apply_delay_samples)){
self->sample_mean_value = self->sample_mean_value_sum / self->sample_mean_value_cntr;
}
// Use mean value if we have enough input samples
if(self->sample_mean_value_cntr <= self->apply_delay_samples)
sample_mean_value_approx = 0;
else
sample_mean_value_approx = self->sample_mean_value;
// increase counters
self->sample_mean_value_sum = self->sample_mean_value_sum + input_sample;
self->sample_mean_value_cntr ++;
// return output value subtracting mean value:
return input_sample - sample_mean_value_approx;
}
\ No newline at end of file
/*
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#ifndef _PICO_ANALOG_MICROPHONE_H_
#define _PICO_ANALOG_MICROPHONE_H_
typedef void (*analog_samples_ready_handler_t)(void);
struct analog_microphone_config {
uint gpio;
float bias_voltage;
uint sample_rate;
uint sample_buffer_size;
};
int analog_microphone_init(const struct analog_microphone_config* config);
void analog_microphone_deinit();
int analog_microphone_start();
void analog_microphone_stop();
void analog_microphone_set_samples_ready_handler(analog_samples_ready_handler_t handler);
int analog_microphone_read(int16_t* buffer, size_t samples);
#endif
#ifndef DC_OFFSET_FILTER__H
#define DC_OFFSET_FILTER__H
#include <stdio.h>
#include <stdbool.h>
typedef struct _dc_offset_filter_t {
int64_t sample_mean_value_sum;
int32_t sample_mean_value_cntr;
int32_t sample_mean_value;
int32_t apply_delay_samples;
} dc_offset_filter_t;
void dc_offset_filter_init(dc_offset_filter_t* self, int32_t apply_delay_samples);
int32_t dc_offset_filter_main(dc_offset_filter_t* self, int32_t input_sample, bool update_mean_val);
#endif //DC_OFFSET_FILTER__H
#ifndef DEFAULT_I2S_BOARD_DEFINES__H
#define DEFAULT_I2S_BOARD_DEFINES__H
//-------------------------
// I2s defines
//-------------------------
#define I2S_MIC_INMP441
#ifdef I2S_MIC_INMP441
#ifndef I2S_MIC_SD
#define I2S_MIC_SD 14
#endif //I2S_MIC_SD
#ifndef I2S_MIC_SCK
#define I2S_MIC_SCK 15
#endif //I2S_MIC_SCK
#ifndef I2S_MIC_WS
#define I2S_MIC_WS (I2S_MIC_SCK+1) // needs to be I2S_MIC_SCK +1
#endif //I2S_MIC_WS
#else //I2S_MIC_INMP441
#ifndef I2S_MIC_SPH_DC_OFFSET
#define I2S_MIC_SPH_DC_OFFSET 0xf8c80000
#endif //I2S_MIC_SPH_DC_OFFSET
#ifndef I2S_MIC_SD
#define I2S_MIC_SD 10
#endif //I2S_MIC_SD
#ifndef I2S_MIC_SCK
#define I2S_MIC_SCK 11
#endif //I2S_MIC_SCK
#ifndef I2S_MIC_WS
#define I2S_MIC_WS (I2S_MIC_SCK+1) // needs to be I2S_MIC_SCK +1
#endif //I2S_MIC_WS
#endif //I2S_MIC_INMP441
#ifndef I2S_MIC_BPS
#define I2S_MIC_BPS 32 // 24 is not valid in this implementation, but INMP441 outputs 24 bits samples
#endif //I2S_MIC_BPS
#ifndef I2S_MIC_RATE_DEF
#define I2S_MIC_RATE_DEF (16000)
#endif //I2S_MIC_RATE_DEF
#ifndef I2S_SPK_SD
#define I2S_SPK_SD 2
#endif //I2S_SPK_SD
#ifndef I2S_SPK_SCK
#define I2S_SPK_SCK 3
#endif //I2S_SPK_SCK
#ifndef I2S_SPK_WS
#define I2S_SPK_WS (I2S_SPK_SCK+1) // needs to be SPK_SCK +1
#endif //I2S_SPK_WS
#ifndef I2S_SPK_BPS
#define I2S_SPK_BPS 32
#endif //I2S_SPK_BPS
#ifndef I2S_SPK_RATE_DEF
#define I2S_SPK_RATE_DEF (48000)
#endif //I2S_SPK_RATE_DEF
typedef struct {
uint32_t left;
uint32_t right;
} i2s_32b_audio_sample;
typedef struct {
uint16_t left;
uint16_t right;
} i2s_16b_audio_sample;
//-------------------------
#endif //DEFAULT_I2S_BOARD_DEFINES__H
\ No newline at end of file
#ifndef MACHINE_I2S__H
#define MACHINE_I2S__H
#include <stdlib.h>
#include <string.h>
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "pico/ring_buf.h"
// Notes on this port's specific implementation of I2S:
// - the DMA IRQ handler is used to implement the asynchronous background operations, for non-blocking mode
// - the PIO is used to drive the I2S bus signals
// - all sample data transfers use non-blocking DMA
// - the DMA controller is configured with 2 DMA channels in chained mode
#define MAX_I2S_RP2 (2)
// The DMA buffer size was empirically determined. It is a tradeoff between:
// 1. memory use (smaller buffer size desirable to reduce memory footprint)
// 2. interrupt frequency (larger buffer size desirable to reduce interrupt frequency)
#define SIZEOF_DMA_BUFFER_IN_BYTES (96*8*2) // Max frequency is 96000. in worst case. 1ms contains 96 samples. Each sample is 8 bytes. Need to hold 2 buffers of this size
#define SIZEOF_HALF_DMA_BUFFER_IN_BYTES (SIZEOF_DMA_BUFFER_IN_BYTES / 2)
#define I2S_NUM_DMA_CHANNELS (2)
// For non-blocking mode, to avoid underflow/overflow, sample data is written/read to/from the ring buffer at a rate faster
// than the DMA transfer rate
#define NON_BLOCKING_RATE_MULTIPLIER (4)
#define SIZEOF_NON_BLOCKING_COPY_IN_BYTES (SIZEOF_HALF_DMA_BUFFER_IN_BYTES * NON_BLOCKING_RATE_MULTIPLIER)
#define NUM_I2S_USER_FORMATS (4)
#define I2S_RX_FRAME_SIZE_IN_BYTES (8)
#define SAMPLES_PER_FRAME (2)
#define PIO_INSTRUCTIONS_PER_BIT (2)
#define STATIC static
#define mp_hal_pin_obj_t uint
#ifndef m_new
#define m_new(type, num) ((type *)(malloc(sizeof(type) * (num))))
#endif //m_new
#ifndef m_new_obj
#define m_new_obj(type) (m_new(type, 1))
#endif //m_new_obj
typedef enum {
RX,
TX
} i2s_mode_t;
typedef enum {
MONO,
STEREO
} format_t;
typedef enum {
BLOCKING,
NON_BLOCKING,
UASYNCIO
} io_mode_t;
typedef enum {
GP_INPUT = 0,
GP_OUTPUT = 1
} gpio_dir_t;
// Buffer protocol
typedef struct _mp_buffer_info_t {
void *buf; // can be NULL if len == 0
size_t len; // in bytes
int typecode; // as per binary.h
} mp_buffer_info_t;
typedef struct _non_blocking_descriptor_t {
mp_buffer_info_t appbuf;
uint32_t index;
bool copy_in_progress;
} non_blocking_descriptor_t;
typedef struct _machine_i2s_obj_t {
uint8_t i2s_id;
mp_hal_pin_obj_t sck;
mp_hal_pin_obj_t ws;
mp_hal_pin_obj_t sd;
i2s_mode_t mode;
int8_t bits;
format_t format;
int32_t rate;
int32_t ibuf;
io_mode_t io_mode;
PIO pio;
uint8_t sm;
const pio_program_t *pio_program;
uint prog_offset;
int dma_channel[I2S_NUM_DMA_CHANNELS];
uint8_t dma_buffer[SIZEOF_DMA_BUFFER_IN_BYTES];
ring_buf_t ring_buffer;
uint8_t *ring_buffer_storage;
non_blocking_descriptor_t non_blocking_descriptor;
uint32_t sizeof_half_dma_buffer_in_bytes;
uint32_t sizeof_non_blocking_copy_in_bytes;
} machine_i2s_obj_t;
machine_i2s_obj_t* create_machine_i2s(uint8_t i2s_id,
mp_hal_pin_obj_t sck, mp_hal_pin_obj_t ws, mp_hal_pin_obj_t sd,
i2s_mode_t i2s_mode, int8_t i2s_bits, format_t i2s_format,
int32_t ring_buffer_len, int32_t i2s_rate);
int machine_i2s_read_stream(machine_i2s_obj_t *self, void *buf_in, size_t size);
int machine_i2s_write_stream(machine_i2s_obj_t *self, void *buf_in, size_t size);
//void update_pio_frequency(machine_i2s_obj_t *self, uint32_t sample_freq);
#endif //MACHINE_I2S__H
\ No newline at end of file
/*
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#ifndef _PICO_PDM_MICROPHONE_H_
#define _PICO_PDM_MICROPHONE_H_
#include "hardware/pio.h"
#include "OpenPDMFilter.h"
#define MAX_PDM_RP2 (2)
#ifndef STATIC
#define STATIC static
#endif //STATIC
#ifndef m_new
#define m_new(type, num) ((type *)(malloc(sizeof(type) * (num))))
#endif //m_new
#ifndef m_new_obj
#define m_new_obj(type) (m_new(type, 1))
#endif //m_new_obj
#define PDM_DECIMATION 64
#define PDM_RAW_BUFFER_COUNT 2
typedef void (*pdm_samples_ready_handler_t)(uint8_t pdm_id);
typedef struct __pdm_microphone_config{
uint8_t pdm_id;
PIO pio;
uint dma_irq;
uint gpio_data;
uint gpio_clk;
uint sample_rate;
uint sample_buffer_size;
} pdm_microphone_config;
typedef struct __pdm_mic_obj{
uint8_t pdm_id;
pdm_microphone_config* config;
uint pio_sm;
uint pio_sm_offset;
int dma_channel;
uint8_t* raw_buffer[PDM_RAW_BUFFER_COUNT];
volatile int raw_buffer_write_index;
volatile int raw_buffer_read_index;
uint raw_buffer_size;
TPDMFilter_InitStruct filter;
uint16_t filter_volume;
pdm_samples_ready_handler_t samples_ready_handler;
} pdm_mic_obj;
pdm_mic_obj* pdm_microphone_init(pdm_microphone_config* config);
void pdm_microphone_deinit(pdm_mic_obj *pdm_mic);
int pdm_microphone_start(pdm_mic_obj *pdm_mic);
void pdm_microphone_stop(pdm_mic_obj *pdm_mic);
void pdm_microphone_set_samples_ready_handler(pdm_mic_obj *pdm_mic, pdm_samples_ready_handler_t handler);
void pdm_microphone_set_filter_max_volume(pdm_mic_obj *pdm_mic, uint8_t max_volume);
void pdm_microphone_set_filter_gain(pdm_mic_obj *pdm_mic, uint8_t gain);
void pdm_microphone_set_filter_volume(pdm_mic_obj *pdm_mic, uint16_t volume);
int pdm_microphone_read(pdm_mic_obj *pdm_mic, int16_t* buffer, size_t samples);
#endif
#ifndef RING_BUF__H
#define RING_BUF__H
#include <stdio.h>
#include <stdbool.h>
typedef struct _ring_buf_t {
uint8_t *buffer;
size_t head;
size_t tail;
size_t size;
} ring_buf_t;
void ringbuf_init(ring_buf_t *rbuf, uint8_t *buffer, size_t size);
bool ringbuf_push(ring_buf_t *rbuf, uint8_t data);
bool ringbuf_pop(ring_buf_t *rbuf, uint8_t *data);
bool ringbuf_is_empty(ring_buf_t *rbuf);
bool ringbuf_is_full(ring_buf_t *rbuf);
size_t ringbuf_available_data(ring_buf_t *rbuf);
size_t ringbuf_available_space(ring_buf_t *rbuf);
#endif //RING_BUF__H
\ No newline at end of file
#pragma once
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "stdbool.h"
// todo this seemed like aood guess, but is not correct
static const uint16_t db_to_vol[91] = {
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002,
0x0002, 0x0002, 0x0003, 0x0003, 0x0004, 0x0004, 0x0005, 0x0005,
0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000e,
0x0010, 0x0012, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, 0x0024,
0x0029, 0x002e, 0x0033, 0x003a, 0x0041, 0x0049, 0x0052, 0x005c,
0x0067, 0x0074, 0x0082, 0x0092, 0x00a4, 0x00b8, 0x00ce, 0x00e7,
0x0104, 0x0124, 0x0147, 0x016f, 0x019c, 0x01ce, 0x0207, 0x0246,
0x028d, 0x02dd, 0x0337, 0x039b, 0x040c, 0x048a, 0x0518, 0x05b7,
0x066a, 0x0732, 0x0813, 0x090f, 0x0a2a, 0x0b68, 0x0ccc, 0x0e5c,
0x101d, 0x1214, 0x1449, 0x16c3, 0x198a, 0x1ca7, 0x2026, 0x2413,
0x287a, 0x2d6a, 0x32f5, 0x392c, 0x4026, 0x47fa, 0x50c3, 0x5a9d,
0x65ac, 0x7214, 0x7fff
};
// actually windows doesn't seem to like this in the middle, so set top range to 0db
#define CENTER_VOLUME_INDEX 91
#define ENCODE_DB(x) ((uint16_t)(int16_t)((x)*256))
#define MIN_VOLUME ENCODE_DB(-CENTER_VOLUME_INDEX)
#define DEFAULT_VOLUME ENCODE_DB(0)
#define MAX_VOLUME ENCODE_DB(count_of(db_to_vol)-CENTER_VOLUME_INDEX)
#define VOLUME_RESOLUTION ENCODE_DB(1)
uint16_t vol_to_db_convert(bool channel_mute, uint16_t channel_volume);
\ No newline at end of file
/*
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "pdm_microphone.pio.h"
#include "pico/pdm_microphone.h"
STATIC pdm_mic_obj* pdm_mic_inst[MAX_PDM_RP2] = {NULL, NULL};
STATIC const PIO pio_instances[NUM_PIOS] = {pio0, pio1};
void pdm_dma_handler(uint8_t pdm_id);
STATIC void pdm_dma_irq0_handler();
STATIC void pdm_dma_irq1_handler();
pdm_mic_obj* pdm_microphone_init(pdm_microphone_config* config) {
uint8_t pdm_id = config->pdm_id;
if(pdm_id >= MAX_PDM_RP2)
return NULL;
if (config->sample_buffer_size % (config->sample_rate / 1000)) {
return NULL;
}
pdm_mic_obj* pdm_mic;
if(pdm_mic_inst[pdm_id] == NULL){
pdm_mic = m_new_obj(pdm_mic_obj);
pdm_mic_inst[pdm_id] = pdm_mic;
} else {
pdm_mic = pdm_mic_inst[pdm_id];
pdm_microphone_deinit(pdm_mic);
}
memset(pdm_mic, 0x00, sizeof(pdm_mic));
pdm_mic->config = config;
pdm_mic->pdm_id = pdm_id;
// if(pdm_mic->pdm_id == 0)
// {
// pdm_mic->config->dma_irq = DMA_IRQ_0;
// }
// else
// {
// pdm_mic->config->dma_irq = DMA_IRQ_1;
// }
// pdm_mic->config->dma_irq = DMA_IRQ_0;
pdm_mic->raw_buffer_size = config->sample_buffer_size * (PDM_DECIMATION / 8);
for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) {
pdm_mic->raw_buffer[i] = malloc(pdm_mic->raw_buffer_size);
if (pdm_mic->raw_buffer[i] == NULL) {
pdm_microphone_deinit(pdm_mic);
return NULL;
}
}
pdm_mic->dma_channel = dma_claim_unused_channel(true);
if (pdm_mic->dma_channel < 0) {
pdm_microphone_deinit(pdm_mic);
return NULL;
}
pdm_mic->pio_sm_offset = pio_add_program(pdm_mic->config->pio, &pdm_microphone_data_program);
pdm_mic->pio_sm = pio_claim_unused_sm(pdm_mic->config->pio, true);
float clk_div = clock_get_hz(clk_sys) / (config->sample_rate * PDM_DECIMATION * 4.0);
pdm_microphone_data_init(
pdm_mic->config->pio,
pdm_mic->pio_sm,
pdm_mic->pio_sm_offset,
clk_div,
config->gpio_data,
config->gpio_clk
);
dma_channel_config dma_channel_cfg = dma_channel_get_default_config(pdm_mic->dma_channel);
channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_8);
channel_config_set_read_increment(&dma_channel_cfg, false);
channel_config_set_write_increment(&dma_channel_cfg, true);
channel_config_set_dreq(&dma_channel_cfg, pio_get_dreq(pdm_mic->config->pio, pdm_mic->pio_sm, false));
dma_channel_configure(
pdm_mic->dma_channel,
&dma_channel_cfg,
pdm_mic->raw_buffer[0],
(void *)&pdm_mic->config->pio->rxf[pdm_mic->pio_sm],
pdm_mic->raw_buffer_size,
false
);
pdm_mic->filter.Fs = config->sample_rate;
pdm_mic->filter.LP_HZ = config->sample_rate / 2;
pdm_mic->filter.HP_HZ = 10;
pdm_mic->filter.In_MicChannels = 1;
pdm_mic->filter.Out_MicChannels = 1;
pdm_mic->filter.Decimation = PDM_DECIMATION;
pdm_mic->filter.MaxVolume = 64;
pdm_mic->filter.Gain = 16;
pdm_mic->filter_volume = pdm_mic->filter.MaxVolume;
pdm_mic->raw_buffer_write_index = 0;
pdm_mic->raw_buffer_read_index = 0;
return pdm_mic;
}
void pdm_microphone_deinit(pdm_mic_obj *pdm_mic) {
for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) {
if (pdm_mic->raw_buffer[i]) {
free(pdm_mic->raw_buffer[i]);
pdm_mic->raw_buffer[i] = NULL;
}
}
if (pdm_mic->dma_channel > -1) {
dma_channel_unclaim(pdm_mic->dma_channel);
pdm_mic->dma_channel = -1;
}
pdm_mic_inst[pdm_mic->pdm_id] == NULL;
}
static int irq0_handler_cntr = 0;
static int irq1_handler_cntr = 0;
int pdm_microphone_start(pdm_mic_obj *pdm_mic) {
irq_set_enabled(pdm_mic->config->dma_irq, true);
dma_irqn_acknowledge_channel(pdm_mic->config->dma_irq, pdm_mic->dma_channel);
dma_irqn_set_channel_enabled(pdm_mic->config->dma_irq, pdm_mic->dma_channel, true);
if(pdm_mic->config->dma_irq == DMA_IRQ_0)
{
dma_channel_set_irq0_enabled(pdm_mic->dma_channel, true);
if(irq0_handler_cntr == 0)
{
irq_set_exclusive_handler(pdm_mic->config->dma_irq, pdm_dma_irq0_handler);
}
irq0_handler_cntr++;
}
else
{
dma_channel_set_irq1_enabled(pdm_mic->dma_channel, true);
if(irq1_handler_cntr == 0)
{
irq_set_exclusive_handler(pdm_mic->config->dma_irq, pdm_dma_irq1_handler);
}
irq1_handler_cntr++;
}
Open_PDM_Filter_Init(&pdm_mic->filter);
pio_sm_set_enabled(
pdm_mic->config->pio,
pdm_mic->pio_sm,
true
);
pdm_mic->raw_buffer_write_index = 0;
pdm_mic->raw_buffer_read_index = 0;
//-------- Strange workaround ------------
dma_channel_transfer_to_buffer_now(
pdm_mic->dma_channel,
pdm_mic->raw_buffer[0],
pdm_mic->raw_buffer_size
);
//----------------------------------------
return 0;
}
void pdm_microphone_stop(pdm_mic_obj *pdm_mic) {
pio_sm_set_enabled(
pdm_mic->config->pio,
pdm_mic->pio_sm,
false
);
dma_channel_abort(pdm_mic->dma_channel);
if (pdm_mic->config->dma_irq == DMA_IRQ_0) {
dma_channel_set_irq0_enabled(pdm_mic->dma_channel, false);
} else if (pdm_mic->config->dma_irq == DMA_IRQ_1) {
dma_channel_set_irq1_enabled(pdm_mic->dma_channel, false);
}
irq_set_enabled(pdm_mic->config->dma_irq, false);
}
STATIC uint dma_map_irq_to_channel(uint irq_index) {
for (uint ch = 0; ch < NUM_DMA_CHANNELS; ch++) {
if ((dma_irqn_get_channel_status(irq_index, ch))) {
return ch;
}
}
// This should never happen
return -1;
}
STATIC void pdm_dma_handle_mic(pdm_mic_obj *pdm_mic)
{
// get the next capture index to send the dma to start
pdm_mic->raw_buffer_read_index = pdm_mic->raw_buffer_write_index;
pdm_mic->raw_buffer_write_index = (pdm_mic->raw_buffer_write_index + 1) % PDM_RAW_BUFFER_COUNT;
// give the channel a new buffer to write to and re-trigger it
dma_channel_transfer_to_buffer_now(
pdm_mic->dma_channel,
pdm_mic->raw_buffer[pdm_mic->raw_buffer_write_index],
pdm_mic->raw_buffer_size
);
// dma_channel_set_write_addr(
// pdm_mic->dma_channel,
// pdm_mic->raw_buffer[pdm_mic->raw_buffer_write_index],
// false);
if (pdm_mic->samples_ready_handler) {
pdm_mic->samples_ready_handler(pdm_mic->pdm_id);
}
}
void pdm_dma_handler(uint8_t irq_index) {
for(uint pdm_idx =0; pdm_idx < MAX_PDM_RP2; pdm_idx++) {
if((pdm_mic_inst[pdm_idx] != NULL) && (pdm_mic_inst[pdm_idx]->config->dma_irq == irq_index)){
uint ch = pdm_mic_inst[pdm_idx]->dma_channel;
if ((dma_irqn_get_channel_status(irq_index, ch))) {
pdm_mic_obj *pdm_mic = pdm_mic_inst[pdm_idx];
pdm_dma_handle_mic(pdm_mic);
// clear IRQ
dma_irqn_acknowledge_channel(irq_index, ch);
}
}
}
}
STATIC void pdm_dma_irq0_handler()
{
pdm_dma_handler(DMA_IRQ_0);
}
STATIC void pdm_dma_irq1_handler()
{
pdm_dma_handler(DMA_IRQ_1);
}
void pdm_microphone_set_samples_ready_handler(pdm_mic_obj *pdm_mic, pdm_samples_ready_handler_t handler) {
pdm_mic->samples_ready_handler = handler;
}
void pdm_microphone_set_filter_max_volume(pdm_mic_obj *pdm_mic, uint8_t max_volume) {
pdm_mic->filter.MaxVolume = max_volume;
}
void pdm_microphone_set_filter_gain(pdm_mic_obj *pdm_mic, uint8_t gain) {
pdm_mic->filter.Gain = gain;
}
void pdm_microphone_set_filter_volume(pdm_mic_obj *pdm_mic, uint16_t volume) {
pdm_mic->filter_volume = volume;
}
int pdm_microphone_read(pdm_mic_obj *pdm_mic, int16_t* buffer, size_t samples) {
int filter_stride = (pdm_mic->filter.Fs / 1000);
samples = (samples / filter_stride) * filter_stride;
if (samples > pdm_mic->config->sample_buffer_size) {
samples = pdm_mic->config->sample_buffer_size;
}
if (pdm_mic->raw_buffer_write_index == pdm_mic->raw_buffer_read_index) {
return 0;
}
uint8_t* in = pdm_mic->raw_buffer[pdm_mic->raw_buffer_read_index];
int16_t* out = buffer;
// get the current buffer index
pdm_mic->raw_buffer_read_index = (pdm_mic->raw_buffer_read_index + 1) % PDM_RAW_BUFFER_COUNT;
for (int i = 0; i < samples; i += filter_stride) {
#if PDM_DECIMATION == 64
Open_PDM_Filter_64(in, out, pdm_mic->filter_volume, &pdm_mic->filter);
#elif PDM_DECIMATION == 128
Open_PDM_Filter_128(in, out, pdm_mic->filter_volume, &pdm_mic->filter);
#else
#error "Unsupported PDM_DECIMATION value!"
#endif
in += filter_stride * (PDM_DECIMATION / 8);
out += filter_stride;
}
return samples;
}
/*
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
.program pdm_microphone_data
.side_set 1
.wrap_target
nop side 0
in pins, 1 side 0
push iffull noblock side 1
nop side 1
.wrap
% c-sdk {
static inline void pdm_microphone_data_init(PIO pio, uint sm, uint offset, float clk_div, uint data_pin, uint clk_pin) {
pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true);
pio_gpio_init(pio, clk_pin);
pio_gpio_init(pio, data_pin);
//gpio_pull_up(pin); //?????
pio_sm_config c = pdm_microphone_data_program_get_default_config(offset);
sm_config_set_in_pins(&c, data_pin); // Data in pi
sm_config_set_sideset_pins(&c, clk_pin); // Clock controlled by side set
// Shift to left, autopush disabled
sm_config_set_in_shift(&c, false, false, 8);
// Join RX channed to have deeper fifo. TX is not used
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
// Set clock divider
sm_config_set_clkdiv(&c, clk_div);
pio_sm_init(pio, sm, offset, &c);
// Need to call from app to sync microphones
//pio_sm_set_enabled(pio, sm, true);
}
%}
#include "pico/ring_buf.h"
// Ring Buffer
// Thread safe when used with these constraints:
// - Single Producer, Single Consumer
// - Sequential atomic operations
// One byte of capacity is used to detect buffer empty/full
void ringbuf_init(ring_buf_t *rbuf, uint8_t *buffer, size_t size) {
rbuf->buffer = buffer;
rbuf->size = size;
rbuf->head = 0;
rbuf->tail = 0;
}
bool ringbuf_push(ring_buf_t *rbuf, uint8_t data) {
size_t next_tail = (rbuf->tail + 1) % rbuf->size;
if (next_tail != rbuf->head) {
rbuf->buffer[rbuf->tail] = data;
rbuf->tail = next_tail;
return true;
}
// full
return false;
}
bool ringbuf_pop(ring_buf_t *rbuf, uint8_t *data) {
stdio_flush();
if (rbuf->head == rbuf->tail) {
// empty
return false;
}
*data = rbuf->buffer[rbuf->head];
rbuf->head = (rbuf->head + 1) % rbuf->size;
return true;
}
bool ringbuf_is_empty(ring_buf_t *rbuf) {
return rbuf->head == rbuf->tail;
}
bool ringbuf_is_full(ring_buf_t *rbuf) {
return ((rbuf->tail + 1) % rbuf->size) == rbuf->head;
}
size_t ringbuf_available_data(ring_buf_t *rbuf) {
return (rbuf->tail - rbuf->head + rbuf->size) % rbuf->size;
}
size_t ringbuf_available_space(ring_buf_t *rbuf) {
return rbuf->size - ringbuf_available_data(rbuf) - 1;
}
\ No newline at end of file
#include "pico/volume_ctrl.h"
uint16_t vol_to_db_convert(bool channel_mute, uint16_t channel_volume){
if(channel_mute)
return 0;
// todo interpolate
channel_volume += CENTER_VOLUME_INDEX * 256;
if (channel_volume < 0) channel_volume = 0;
if (channel_volume >= count_of(db_to_vol) * 256) channel_volume = count_of(db_to_vol) * 256 - 1;
uint16_t vol_mul = db_to_vol[((uint16_t)channel_volume) >> 8u];
return vol_mul;
}
\ No newline at end of file
# == 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 imu_data_logger)
include(pico_sdk_import.cmake)
project(${PROGRAM_NAME} C CXX ASM)
# Initialise the Pico SDK
pico_sdk_init()
add_subdirectory(sd_card_driver build)
# 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} "3.3.1")
pico_enable_stdio_usb(${PROGRAM_NAME} 1)
target_include_directories(${PROGRAM_NAME} PUBLIC
include/
)
target_link_libraries(${PROGRAM_NAME}
sd_custom_driver
hardware_clocks
hardware_adc
)
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").
static spi_t spis[] = { // One for each RP2040 SPI component used
{ // spis[0]
.hw_inst = spi0, // RP2040 SPI component
.sck_gpio = 5, // GPIO number (not Pico pin number)
.mosi_gpio = 18,
.miso_gpio = 19,
.set_drive_strength = true,
.mosi_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA,
.sck_gpio_drive_strength = GPIO_DRIVE_STRENGTH_12MA,
.no_miso_gpio_pull_up = true,
// .baud_rate = 125 * 1000 * 1000 / 10 // 12500000 Hz
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
// .baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
},
{ // spis[1]
.hw_inst = spi1, // RP2040 SPI component
.miso_gpio = 5, // GPIO number (not Pico pin number)
.sck_gpio = 18,
.mosi_gpio = 19,
.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 / 12
//.baud_rate = 125 * 1000 * 1000 / 10 // 12500000 Hz
//.baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
.baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
// .baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
}
};
/* SPI Interfaces */
static sd_spi_if_t spi_ifs[] = {
{ // spi_ifs[0]
.spi = &spis[0], // Pointer to the SPI driving this card
.ss_gpio = 22, // The SPI slave select GPIO for this SD card
.set_drive_strength = true,
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
},
{ // spi_ifs[1]
.spi = &spis[1], // Pointer to the SPI driving this card
.ss_gpio = 22, // The SPI slave select GPIO for this SD card
.set_drive_strength = true,
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
},
{ // spi_ifs[2]
.spi = &spis[1], // Pointer to the SPI driving this card
.ss_gpio = 22, // The SPI slave select GPIO for this SD card
.set_drive_strength = true,
.ss_gpio_drive_strength = GPIO_DRIVE_STRENGTH_2MA
}
};
/* 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[] = {
{ // sdio_ifs[0]
.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 = pio1,
.DMA_IRQ_num = DMA_IRQ_1,
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
.baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
// .baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
// .baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
},
{ // sdio_ifs[1]
.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,
.DMA_IRQ_num = DMA_IRQ_1,
// .baud_rate = 125 * 1000 * 1000 / 8 // 15625000 Hz
.baud_rate = 125 * 1000 * 1000 / 7 // 17857143 Hz
// .baud_rate = 125 * 1000 * 1000 / 6 // 20833333 Hz
// .baud_rate = 125 * 1000 * 1000 / 5 // 25000000 Hz
//.baud_rate = 125 * 1000 * 1000 / 4 // 31250000 Hz
}
};
/* Hardware Configuration of the SD Card "objects"
These correspond to SD card sockets
*/
static sd_card_t sd_cards[] = { // One for each SD card
#ifdef SPI_SD0
{ // sd_cards[0]: Socket sd0
.type = SD_IF_SPI,
.spi_if_p = &spi_ifs[0], // Pointer to the SPI interface driving this card
// SD Card detect:
.use_card_detect = true,
.card_detect_gpio = 23,
.card_detected_true = 0, // What the GPIO read returns when a card is
// present.
.card_detect_use_pull = true,
.card_detect_pull_hi = true
},
#else
{ // sd_cards[0]: Socket sd0
.type = SD_IF_SDIO,
.sdio_if_p = &sdio_ifs[0], // Pointer to the SPI interface driving this card
// SD Card detect:
.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
},
#endif
{ // sd_cards[1]: Socket sd1
.type = SD_IF_SPI,
.spi_if_p = &spi_ifs[1], // Pointer to the SPI interface driving this card
// SD Card detect:
.use_card_detect = false,
.card_detect_gpio = 14,
.card_detected_true = 0, // What the GPIO read returns when a card is
// present.
.card_detect_use_pull = true,
.card_detect_pull_hi = true
},
{ // sd_cards[2]: Socket sd2
.type = SD_IF_SPI,
.spi_if_p = &spi_ifs[2], // Pointer to the SPI interface driving this card
// SD Card detect:
.use_card_detect = false,
.card_detect_gpio = 15,
.card_detected_true = 0, // What the GPIO read returns when a card is
// present.
.card_detect_use_pull = true,
.card_detect_pull_hi = true
},
{ // sd_cards[3]: Socket sd3
.type = SD_IF_SDIO,
.sdio_if_p = &sdio_ifs[1], // Pointer to the interface driving this card
// SD Card detect:
.use_card_detect = false,
.card_detect_gpio = 22,
.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 */
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "ff.h"
#include "sd_card.h"
#include "f_util.h"
#include "hw_config.h"
#define PATH_MAX_LEN 256
// --------- Globals (FatFs requires the FS to outlive the mount) ----------
static FATFS fs; // must be static/global (lives as long as the mount)
static sd_card_t *g_sd = NULL; // active SD card
static const char *g_drive = NULL; // typically "0:"
// ------------------------- Utility / Error -------------------------------
static void die(FRESULT fr, const char *op) {
printf("%s failed: %s (%d)\n", op, FRESULT_str(fr), fr);
while (1) tight_loop_contents();
}
static void loop_forever_msg(const char *msg) {
printf("%s\n", msg);
while (1) tight_loop_contents();
}
static void join_path(char *out, size_t out_sz, const char *drive, const char *rel) {
// drive = "0:" or "0:/", ensure exactly one slash when joining
if (rel && rel[0] == '/') rel++; // avoid double slashes
if (drive && drive[strlen(drive) - 1] == '/')
snprintf(out, out_sz, "%s%s", drive, rel ? rel : "");
else
snprintf(out, out_sz, "%s/%s", drive, rel ? rel : "");
}
// ------------------------- 1) Initialization -----------------------------
static bool sd_init_and_mount(void) {
if (!sd_init_driver()) {
printf("sd_init_driver() failed\n");
return false;
}
g_sd = sd_get_by_num(0);
if (!g_sd) {
printf("No SD config found (sd_get_by_num(0) == NULL)\n");
return false;
}
g_drive = sd_get_drive_prefix(g_sd); // usually "0:"
if (!g_drive) {
printf("sd_get_drive_prefix() returned NULL\n");
return false;
}
FRESULT fr = f_mount(&fs, g_drive, 1);
printf("f_mount -> %s (%d)\n", FRESULT_str(fr), fr);
if (fr == FR_NO_FILESYSTEM) {
BYTE work[4096]; // >= FF_MAX_SS
MKFS_PARM opt = { FM_FAT | FM_SFD, 0, 0, 0, 0 };
fr = f_mkfs(g_drive, &opt, work, sizeof work);
printf("f_mkfs -> %s (%d)\n", FRESULT_str(fr), fr);
if (fr == FR_OK) {
fr = f_mount(&fs, g_drive, 1);
printf("f_mount(after mkfs) -> %s (%d)\n", FRESULT_str(fr), fr);
}
}
if (fr != FR_OK) {
printf("Mount failed: %s (%d)\n", FRESULT_str(fr), fr);
return false;
}
return true;
}
// ------------------------- 2) File creation ------------------------------
static FRESULT create_file(const char *abs_path, FIL *out_file) {
// Creates/truncates a file and opens it for writing
return f_open(out_file, abs_path, FA_WRITE | FA_CREATE_ALWAYS);
}
// ------------------------- 3) File writing -------------------------------
static FRESULT write_to_file(FIL *file, const void *data, UINT len, UINT *bytes_written) {
*bytes_written = 0;
FRESULT fr = f_write(file, data, len, bytes_written);
if (fr == FR_OK) {
fr = f_sync(file); // ensure data hits the card
}
return fr;
}
// ------------------------- 4) File checking/listing ----------------------
typedef struct {
uint32_t files;
uint32_t dirs;
uint64_t total_bytes;
} list_stats_t;
static bool is_dot_or_dotdot(const char *name) {
return (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')));
}
static FRESULT list_dir_recursive(const char *path, list_stats_t *stats) {
DIR dir;
FILINFO fno;
FRESULT fr = f_opendir(&dir, path);
if (fr != FR_OK) {
printf("f_opendir('%s') -> %s (%d)\n", path, FRESULT_str(fr), fr);
return fr;
}
for (;;) {
fr = f_readdir(&dir, &fno);
if (fr != FR_OK) {
printf("f_readdir('%s') -> %s (%d)\n", path, FRESULT_str(fr), fr);
break;
}
if (fno.fname[0] == '\0') break; // end of directory
if (is_dot_or_dotdot(fno.fname)) continue;
if (fno.fattrib & AM_DIR) {
stats->dirs++;
char subpath[PATH_MAX_LEN];
snprintf(subpath, sizeof subpath, "%s/%s", path, fno.fname);
printf("[DIR] %s\n", subpath);
fr = list_dir_recursive(subpath, stats);
if (fr != FR_OK) break;
} else {
stats->files++;
stats->total_bytes += (uint64_t)fno.fsize;
printf("[FILE] %s/%s (%lu bytes)\n", path, fno.fname, (unsigned long)fno.fsize);
}
}
FRESULT frc = f_closedir(&dir);
if (fr == FR_OK && frc != FR_OK) fr = frc;
return fr;
}
// Public checker: lists all files and sizes, and tells if any exist
static FRESULT check_and_list_files(const char *root_drive) {
// Build root path "0:/"
char root[PATH_MAX_LEN];
join_path(root, sizeof root, root_drive, ""); // ensures a trailing slash when we add children
list_stats_t stats = {0};
printf("\n--- SD Card File Listing for '%s' ---\n", root_drive);
FRESULT fr = list_dir_recursive(root_drive, &stats);
if (fr != FR_OK && fr != FR_NO_PATH) {
printf("Directory listing aborted due to error.\n");
return fr;
}
if (stats.files == 0 && stats.dirs == 0) {
printf("No files or directories found on the SD card.\n");
} else if (stats.files == 0) {
printf("No files found (but %u director%s present).\n", stats.dirs, (stats.dirs == 1 ? "y" : "ies"));
} else {
printf("\nSummary: %u file%s in %u director%s, total %llu bytes.\n",
stats.files, (stats.files == 1 ? "" : "s"),
stats.dirs, (stats.dirs == 1 ? "y" : "ies"),
(unsigned long long)stats.total_bytes);
}
return FR_OK;
}
// ------------------------------ Main -------------------------------------
int main(void) {
stdio_init_all();
sleep_ms(1500);
// 1) Init + mount
if (!sd_init_and_mount()) {
loop_forever_msg("SD init/mount failed.");
}
// Build absolute file path: <drive>/test.txt
char path[PATH_MAX_LEN];
join_path(path, sizeof path, g_drive, "test1.txt");
// 2) Create the file
FIL f;
FRESULT fr = create_file(path, &f);
if (fr != FR_OK) die(fr, "f_open(create)");
// 3) Write data
const char *msg = "data writing test!\n";
UINT bw = 0;
fr = write_to_file(&f, msg, (UINT)strlen(msg), &bw);
if (fr != FR_OK || bw != strlen(msg)) die(fr, "f_write/f_sync");
printf("Wrote %u bytes to %s\n", bw, path);
// Close the file
f_close(&f);
// 4) Check and list files (recursively) on the card
fr = check_and_list_files(g_drive);
if (fr != FR_OK) die(fr, "check_and_list_files");
// Optional: unmount
fr = f_unmount(g_drive);
printf("f_unmount -> %s (%d)\n", FRESULT_str(fr), fr);
while (1) { sleep_ms(1000); }
}
# 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})
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 */
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