Skip to content

Commit

Permalink
(merge, #7): Initial MCP3008 support
Browse files Browse the repository at this point in the history
  • Loading branch information
jcxldn authored Oct 7, 2023
2 parents c58a123 + b588479 commit 3dcc9e3
Show file tree
Hide file tree
Showing 12 changed files with 632 additions and 1 deletion.
50 changes: 50 additions & 0 deletions src/core/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
// Source control information embedded at build-time
#include "git.h"

#include "mcp3008/mcp3008.h"
#include "mcp3008/pio/mcp3008_pio.h"
#include "mcp3008/hardware/mcp3008_hardware.h"

extern void *__BUILD_INCLUDES_FLASHLOADER;

#ifdef INCLUDES_FLASHLOADER
Expand Down Expand Up @@ -78,6 +82,52 @@ int main(void)
pvRegisterFlashloaderTask();
#endif

printf("HELLO, world!");

spi_inst_t *spi = spi0;
spi_pinout_t pinout = {
.sck = 2,
.csn = 5,
.miso = 4,
.mosi = 3,
};
spi_dual_inst inst = mcp3008_init_hardware(spi, 3600000, &pinout);
int16_t res = mcp3008_read_hardware(&inst, 1, false);
printf("\nDATA HW_0: %x", res);

spi_inst_t *spi2 = spi1;
spi_pinout_t pinout2 = {
.sck = 10,
.csn = 9,
.miso = 8,
.mosi = 11,
};
spi_dual_inst inst2 = mcp3008_init_hardware(spi2, 3600000, &pinout2);
int16_t res2 = mcp3008_read_hardware(&inst2, 0, false);
printf("\nDATA HW_1: %x", res2);

spi_pinout_t pinout3 = {
.sck = 14,
.csn = 13,
.miso = 12,
.mosi = 15,
};
spi_dual_inst inst3 = mcp3008_init_pio(pio0, 3600000, &pinout3);
uint16_t res3 = mcp3008_read_pio(&inst3, 1, false);
printf("\nDATA PIO_0: %x", res3);

spi_pinout_t pinout4 = {
.sck = 18,
.csn = 17,
.miso = 16,
.mosi = 19,
};
spi_dual_inst inst4 = mcp3008_init_pio(pio1, 3600000, &pinout4);
uint16_t res4 = mcp3008_read_pio(&inst4, 1, false);
printf("\nDATA PIO_1: %x", res4);

printf("\n");

// TinyUSB demos call this after creating tasks.
vTaskStartScheduler();

Expand Down
8 changes: 7 additions & 1 deletion src/core/main.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ add_subdirectory(../lib/cmake-git-version-tracking git) # copy/compile to "git"
# Init SDK
pico_sdk_init()

pico_generate_pio_header(${MAIN} ${CMAKE_CURRENT_LIST_DIR}/mcp3008/pio/spi.pio)


# RTOS config "FreeRTOSConfig.h"
# These sources should be PUBLIC so dependents linking against this library inherit these.
Expand All @@ -43,6 +45,10 @@ target_sources(${MAIN} PUBLIC
${CMAKE_CURRENT_LIST_DIR}/tinyusb-config/usb_descriptors.c
${CMAKE_CURRENT_LIST_DIR}/tasks/bulk.c
${CMAKE_CURRENT_LIST_DIR}/tasks/mcu_temperature.c
${CMAKE_CURRENT_LIST_DIR}/mcp3008/mcp3008.c
${CMAKE_CURRENT_LIST_DIR}/mcp3008/hardware/mcp3008_hardware.c
${CMAKE_CURRENT_LIST_DIR}/mcp3008/pio/mcp3008_pio.c
${CMAKE_CURRENT_LIST_DIR}/mcp3008/pio/pio_spi.c
${CMAKE_CURRENT_LIST_DIR}/config.h
)

Expand All @@ -51,7 +57,7 @@ target_sources(${MAIN} PUBLIC
# pico_malloc, pico_mem_ops - optimized versions of standard libraries.
# memory management: FreeRTOS-Kernel-Heap# required for pvPortMalloc
# tinyusb_device tinyusb_board (https://github.com/raspberrypi/pico-examples/blob/master/usb/device/dev_hid_composite/CMakeLists.txt)
target_link_libraries(${MAIN} pico_stdlib pico_malloc pico_mem_ops hardware_adc pico_unique_id FreeRTOS-Kernel FreeRTOS-Kernel-Heap4 tinyusb_device tinyusb_board cmake_git_version_tracking)
target_link_libraries(${MAIN} hardware_spi hardware_pio pico_stdlib pico_malloc pico_mem_ops hardware_adc pico_unique_id FreeRTOS-Kernel FreeRTOS-Kernel-Heap4 tinyusb_device tinyusb_board cmake_git_version_tracking)

# stdio only on UART (UART0 by default, pins 1 and 2)
pico_enable_stdio_usb(${MAIN} 0)
Expand Down
84 changes: 84 additions & 0 deletions src/core/mcp3008/hardware/mcp3008_hardware.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT

#include "mcp3008_hardware.h"

spi_dual_inst mcp3008_init_hardware(spi_inst_t *spi, uint baudrate, spi_pinout_t *pinout)
{
spi_dual_inst inst = {
.spi_hw = spi,
//.baudrate = baudrate,
.pinout = pinout,
.isHw = true,
};

// Init SPIx at specified baudrate
inst.baudrate = spi_init(inst.spi_hw, baudrate);

// Assign SPI functions to pins
gpio_set_function(inst.pinout->miso, GPIO_FUNC_SPI);
gpio_set_function(inst.pinout->sck, GPIO_FUNC_SPI);
gpio_set_function(inst.pinout->mosi, GPIO_FUNC_SPI);
// gpio_set_function(5, GPIO_FUNC_SPI);

gpio_init(inst.pinout->csn);
gpio_set_dir(inst.pinout->csn, GPIO_OUT);
gpio_put(inst.pinout->csn, 1);

// Create an input buffer and initialise it to zero.
for (uint8_t i = 0; i < BUF_LEN; i++)
{
inst.input_buffer[i] = 0;
}

return inst;
}

// -- Internal function to handle sending data --
uint16_t mcp3008_read_hardware(spi_dual_inst *inst, uint8_t channel, bool differential)
{
if (inst->isHw)
{
// Create a buffer to sent to the device initialised to zero
uint8_t output_buffer[BUF_LEN];
for (uint8_t i = 0; i < BUF_LEN; i++)
{
output_buffer[i] = 0;
}

// Byte 1, leading zeros ending with a start bit
output_buffer[0] = 0x01;

// Byte 2
// Bit 1, differential signal selection
// Bits 2-4 channel select
// Rest of buffer ignored
output_buffer[1] = ((differential ? 0 : 1) << 7 | channel << 4);

// temp
uint8_t input_buffer[BUF_LEN];
for (uint8_t i = 0; i < BUF_LEN; i++)
{
input_buffer[i] = 0;
}

// Send the SPI command

gpio_put(inst->pinout->csn, 0);
__breakpoint();
spi_write_read_blocking(inst->spi_hw, output_buffer, input_buffer, BUF_LEN);
gpio_put(inst->pinout->csn, 1);

// Response
// 13 bits garbage
// 1 bit null (0)
// data, ordered bits 9 to 0
// mask 0x07 last 3 bits
// mask 0x03 last 2 bits
// return (((uint16_t)(input_buffer[1] & 0x03)) << 8) | input_buffer[2];
return (((uint16_t)(input_buffer[1] & 0x03)) << 8) | input_buffer[2];
}
else
{
printf("\n(error) [mcp3008]: PIO instance attempted to call HW handler.");
}
}
14 changes: 14 additions & 0 deletions src/core/mcp3008/hardware/mcp3008_hardware.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT

#ifndef HARDWARE_MCP3008_HARDWARE_H_
#define HARDWARE_MCP3008_HARDWARE_H_

#include "hardware/spi.h"

#include "../pinout.h"
#include "../mcp3008.h"

spi_dual_inst mcp3008_init_hardware(spi_inst_t *spi, uint baudrate, spi_pinout_t *pinout);
uint16_t mcp3008_read_hardware(spi_dual_inst *inst, uint8_t channel, bool differential);

#endif /* HARDWARE_MCP3008_HARDWARE_H_ */
46 changes: 46 additions & 0 deletions src/core/mcp3008/mcp3008.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT

// Accompanying header file
#include "mcp3008.h"

// PIO and hardware mcp3008 functions
#include "./hardware/mcp3008_hardware.h"
#include "./pio/mcp3008_pio.h"

spi_dual_inst mcp3008_init(spi_pinout_t *pinout, bool IshwInstance, bool instance)
{
if (IshwInstance)
{
// Init hardware

// Determine weather to use spi0 or spi1
// If 0 / false, SPI 0.
// If 1 / true, SPI 1.
spi_inst_t *spi = (instance ? spi1 : spi0);

return mcp3008_init_hardware(spi, SPI_CLOCK_SPEED, pinout);
}
else
{
// Init PIO

// Determine weather to use pio0 or pio1
// If 0 / false, PIO 0.
// If 1 / true, PIO 1.
PIO pio = (instance ? pio1 : pio0);

return mcp3008_init_pio(pio, SPI_CLOCK_SPEED, pinout);
}
}

uint16_t mcp3008_read(spi_dual_inst *inst, uint8_t channel, bool differential)
{
if (inst->isHw)
{
return mcp3008_read_hardware(inst, channel, differential);
}
else
{
return mcp3008_read_pio(inst, channel, differential);
}
}
33 changes: 33 additions & 0 deletions src/core/mcp3008/mcp3008.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT

#ifndef MCP3008_MCP3008_H_
#define MCP3008_MCP3008_H_

#include "hardware/spi.h"
#include "pio/pio_spi.h"

#include "pinout.h"

// TODO: can definitely get away with a smaller size
#define BUF_LEN 3

// Datasheet specifies 3.6MHz max
// 8MHz probably works though
// See https://github.com/adafruit/Adafruit_MCP3008/issues/9
#define SPI_CLOCK_SPEED 3600000 // 3.6MHz

typedef struct
{
spi_inst_t *spi_hw;
pio_spi_inst_t spi_pio; // if *spi_pio, read/write operations hang.
uint baudrate;
spi_pinout_t *pinout;
uint8_t input_buffer[BUF_LEN];
bool isHw;
} spi_dual_inst;

// Function definitions
spi_dual_inst mcp3008_init(spi_pinout_t *pinout, bool IshwInstance, bool instance);
uint16_t mcp3008_read(spi_dual_inst *inst, uint8_t channel, bool differential);

#endif /* MCP3008_MCP3008_H_ */
14 changes: 14 additions & 0 deletions src/core/mcp3008/pinout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT

#ifndef MCP3008_PINOUT_H_
#define MCP3008_PINOUT_H_

typedef struct TU_ATTR_PACKED
{
int miso;
int mosi;
int sck;
int csn;
} spi_pinout_t;

#endif /* MCP3008_PINOUT_H_ */
98 changes: 98 additions & 0 deletions src/core/mcp3008/pio/mcp3008_pio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: MIT

#include "mcp3008_pio.h"

// clk_sys enum
#include "hardware/clocks.h"

spi_dual_inst mcp3008_init_pio(PIO pio, uint baudrate, spi_pinout_t *pinout)
{
pio_spi_inst_t spi = {
.pio = pio, // pio to use (pio0, pio1)
.sm = pio_claim_unused_sm(pio, true) // claim first free state machine
};

// Based on: https://medium.com/geekculture/raspberry-pico-programming-with-pio-state-machines-e4610e6b0f29
// *Should* make the spi clock speed correct.
float clock_divider = (float)clock_get_hz(clk_sys) / SPI_CLOCK_SPEED;
// float clkdiv = 31.25f; // 1 MHz @ 125 clk_sys

uint cpha0_prog_offs = pio_add_program(spi.pio, &spi_cpha0_program);

pio_spi_init(
spi.pio, // pio
spi.sm, // state machine (num)
cpha0_prog_offs, // program offsets
8, // number of bits per SPI frame
clock_divider, // Clock divider
0, // cpha
1, // cpol
pinout->sck, // SCK pin,
pinout->mosi, // MOSI pin
pinout->miso // MISO pin
);

// Setup CS pin
gpio_init(pinout->csn);
gpio_set_dir(pinout->csn, GPIO_OUT);
gpio_put(pinout->csn, 1);

spi_dual_inst inst = {
.spi_pio = spi,
.baudrate = baudrate,
.pinout = pinout,
.isHw = false,
};

// Create an input buffer and initialise it to zero.
for (uint8_t i = 0; i < BUF_LEN; i++)
{
inst.input_buffer[i] = 0;
}

return inst;
}

uint16_t mcp3008_read_pio(spi_dual_inst *inst, uint8_t channel, bool differential)
{
if (!inst->isHw)
{

// Create a buffer to sent to the device initialised to zero
uint8_t output_buffer[BUF_LEN];
for (uint8_t i = 0; i < BUF_LEN; i++)
{
output_buffer[i] = 0;
}

// Byte 1, leading zeros ending with a start bit
output_buffer[0] = 0x01;

// Byte 2
// Bit 1, differential signal selection
// Bits 2-4 channel select
// Rest of buffer ignored
output_buffer[1] = ((differential ? 0 : 1) << 7 | channel << 4);

// temp
uint8_t input_buffer[BUF_LEN];
for (uint8_t i = 0; i < BUF_LEN; i++)
{
input_buffer[i] = 0;
}

// Send SPI command
gpio_put(inst->pinout->csn, 0);

pio_spi_write8_read8_blocking(&(inst->spi_pio), output_buffer, input_buffer, BUF_LEN);
gpio_put(inst->pinout->csn, 1);

uint16_t data = (((uint16_t)(input_buffer[1] & 0x03)) << 8) | input_buffer[2];

return data;
}
else
{
printf("\n(error): [mcp3008]: HW instance attempted to call PIO handler.");
}
}
Loading

0 comments on commit 3dcc9e3

Please sign in to comment.