Start work on SPI testing framework
This commit is contained in:
parent
d6402ff656
commit
ad4621cb2e
|
@ -1,2 +1,3 @@
|
|||
*-backups/
|
||||
build/
|
||||
hostbuild/
|
||||
|
|
|
@ -23,3 +23,28 @@ pico_enable_stdio_usb(uart_test 1)
|
|||
pico_enable_stdio_uart(uart_test 0)
|
||||
|
||||
pico_add_extra_outputs(uart_test)
|
||||
|
||||
add_executable(blink_test
|
||||
src/blink_test.c
|
||||
)
|
||||
|
||||
target_link_libraries(blink_test pico_stdlib)
|
||||
|
||||
pico_enable_stdio_usb(blink_test 1)
|
||||
pico_enable_stdio_uart(blink_test 0)
|
||||
|
||||
pico_add_extra_outputs(blink_test)
|
||||
|
||||
add_executable(spi_test
|
||||
src/spi_test.c
|
||||
)
|
||||
|
||||
target_link_libraries(spi_test
|
||||
pico_stdlib
|
||||
hardware_spi
|
||||
)
|
||||
|
||||
pico_enable_stdio_usb(spi_test 1)
|
||||
pico_enable_stdio_uart(spi_test 0)
|
||||
|
||||
pico_add_extra_outputs(spi_test)
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
|
||||
#define PICO_FLASH_SIZE_BYTES (16*1024*1024)
|
||||
|
||||
#define PICO_DEFAULT_SPI 0
|
||||
#define PICO_DEFAULT_SPI_SCK_PIN 1
|
||||
#define PICO_DEFAULT_SPI_TX_PIN 3
|
||||
#define PICO_DEFAULT_SPI_RX_PIN 0
|
||||
#define PICO_DEFAULT_SPI_CSN_PIN 2
|
||||
|
||||
#include "boards/pico.h"
|
||||
|
||||
#undef PICO_BOOT_STAGE2_CHOOSE_W25Q080
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(walkie_talkies_host)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(spi_parse_test
|
||||
../src/spi_parser_test.c
|
||||
)
|
|
@ -1,11 +1,17 @@
|
|||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#define TEST_GPIO 8
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
gpio_init(TEST_GPIO);
|
||||
gpio_set_dir(TEST_GPIO, GPIO_OUT);
|
||||
bool val = true;
|
||||
while (true) {
|
||||
printf("test!\n");
|
||||
sleep_ms(250);
|
||||
gpio_put(TEST_GPIO, val);
|
||||
val = !val;
|
||||
sleep_ms(25);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
#ifndef SPI_PARSER_H_
|
||||
#define SPI_PARSER_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// probably should include stdint but I'm not sure how that'd
|
||||
// interact with Pico SDK's stuff, so I'll leave it out
|
||||
|
||||
static int maybe_nibble(char c) {
|
||||
if ((c >= 'a') && (c <= 'f')) {
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
if ((c >= '0') && (c <= '9')) {
|
||||
return c - '0';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void spi_register_write(uint16_t address, const uint8_t *buf, uint16_t len);
|
||||
static void spi_register_read(uint16_t address, uint8_t *buf, uint16_t len);
|
||||
|
||||
static void parse_and_execute_cmd(const uint8_t* s, int c_len) {
|
||||
if (c_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int is_write = (s[0] == 'w');
|
||||
int is_read = (s[0] == 'r');
|
||||
if (!is_write && !is_read) {
|
||||
puts("invalid cmd; no r/w");
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = 1;
|
||||
|
||||
uint16_t addr = 0;
|
||||
int len = 0;
|
||||
uint8_t buf[64];
|
||||
|
||||
for (int i = 0; i < 4 && (i + 1) < c_len; i++) {
|
||||
int mn = maybe_nibble(s[i + offset]);
|
||||
if (mn < 0) {
|
||||
puts("invalid cmd; bad addr");
|
||||
return;
|
||||
}
|
||||
addr = (addr << 4) | (mn & 0xf);
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
offset += 1; // skip a character, some kind of whitespace is allowed
|
||||
// variable length entry, read until
|
||||
int readlen = 0;
|
||||
int mn = maybe_nibble(s[offset]);
|
||||
while ((mn >= 0) && (offset < c_len)) {
|
||||
len = (len << 4) | (mn & 0xf);
|
||||
++offset;
|
||||
mn = maybe_nibble(s[offset]);
|
||||
}
|
||||
|
||||
if (len > sizeof(buf)) {
|
||||
len = sizeof(buf);
|
||||
}
|
||||
|
||||
const uint8_t tohex[] = "0123456789abcdef";
|
||||
|
||||
if (is_read) {
|
||||
// we're good, time to execute
|
||||
spi_register_read(addr, buf, len);
|
||||
uint8_t output_str[3*sizeof(buf) + 1];
|
||||
for (int i = 0; i < len; i++) {
|
||||
output_str[3*i] = tohex[(buf[i] >> 4) & 0xf];
|
||||
output_str[3*i + 1] = tohex[buf[i] & 0xf];
|
||||
output_str[3*i + 2] = ' ';
|
||||
}
|
||||
output_str[3*len - 1] = '\n';
|
||||
output_str[3*len] = 0;
|
||||
puts(output_str);
|
||||
return;
|
||||
}
|
||||
++offset; // skip whitespace character
|
||||
|
||||
if (is_write) {
|
||||
// parse write data one nibble at a time until it reaches the end
|
||||
// or len characters have been read
|
||||
int nibble_count = 0;
|
||||
int cur_byte = 0;
|
||||
for (; (nibble_count < 2*len) && (offset < c_len); offset++) {
|
||||
int mn = maybe_nibble(s[offset]);
|
||||
if (mn >= 0) {
|
||||
cur_byte = (cur_byte << 4) | (mn & 0xf);
|
||||
++nibble_count;
|
||||
if ((nibble_count % 2) == 0) {
|
||||
buf[(nibble_count/2) - 1] = cur_byte & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nibble_count < 2*len) {
|
||||
puts("parse error: not enough data for write\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spi_register_write(addr, buf, len);
|
||||
uint8_t output_s[3*sizeof(buf) + 4 + 9];
|
||||
output_s[0] = 'w';
|
||||
output_s[1] = ' ';
|
||||
for (int i = 0; i < 4; i++) {
|
||||
output_s[2+i] = tohex[(addr >> (4*(3-i))) & 0xf];
|
||||
}
|
||||
output_s[2+4] = ' ';
|
||||
|
||||
uint8_t* output_str = output_s + 7;
|
||||
for (int i = 0; i < len; i++) {
|
||||
output_str[3*i] = tohex[(buf[i] >> 4) & 0xf];
|
||||
output_str[3*i + 1] = tohex[buf[i] & 0xf];
|
||||
output_str[3*i + 2] = ' ';
|
||||
}
|
||||
output_str[3*len - 1] = '\n';
|
||||
output_str[3*len] = 0;
|
||||
|
||||
puts(output_s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // SPI_PARSER_H_
|
|
@ -0,0 +1,107 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spi_parser.h"
|
||||
|
||||
int reg_written = 0;
|
||||
int reg_read = 0;
|
||||
|
||||
int reg_addr = 0;
|
||||
int reg_len = 0;
|
||||
uint8_t reg_buf[64];
|
||||
|
||||
static void spi_register_write(
|
||||
uint16_t address, const uint8_t *buf, uint16_t len) {
|
||||
reg_written = 1;
|
||||
reg_addr = address;
|
||||
reg_len = len;
|
||||
memcpy(reg_buf, buf, len);
|
||||
}
|
||||
|
||||
static void spi_register_read(uint16_t address, uint8_t *buf, uint16_t len) {
|
||||
reg_read = 1;
|
||||
reg_addr = address;
|
||||
reg_len = len;
|
||||
}
|
||||
|
||||
static void reset_reg() {
|
||||
reg_written = 0;
|
||||
reg_read = 0;
|
||||
reg_addr = 0;
|
||||
reg_len = 0;
|
||||
}
|
||||
|
||||
struct test_case {
|
||||
const uint8_t* s;
|
||||
int s_len;
|
||||
int is_read;
|
||||
int is_write;
|
||||
int addr;
|
||||
int len;
|
||||
const uint8_t* expected;
|
||||
};
|
||||
|
||||
static int check_reg(struct test_case* tc) {
|
||||
if (tc->is_read) {
|
||||
return reg_read && (reg_addr == tc->addr) && (reg_len == tc->len);
|
||||
} else if (tc->is_write) {
|
||||
return reg_written && (reg_addr == tc->addr) && (reg_len == tc->len) && (memcmp(reg_buf, tc->expected, tc->len) == 0);
|
||||
}
|
||||
return !reg_read && !reg_written;
|
||||
}
|
||||
|
||||
static void dump_expected(struct test_case* tc) {
|
||||
if (tc->is_read && !reg_read) {
|
||||
printf("read bit not set, ");
|
||||
} else if (tc->is_write && !reg_written) {
|
||||
printf("write bit not set, ");
|
||||
} else {
|
||||
if (reg_read) {
|
||||
printf("read bit SET, should\'nt be \n");
|
||||
return;
|
||||
}
|
||||
if (reg_written) {
|
||||
printf("read bit SET, should\'nt be \n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (tc->addr != reg_addr) {
|
||||
printf("got addr %04x, expected %04x, ", reg_addr, tc->addr);
|
||||
}
|
||||
if (tc->len != reg_len) {
|
||||
printf("got len %04x, expected %04x, ", reg_len, tc->len);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
const uint8_t test0[] = "r0102 1\n";
|
||||
const uint8_t test1[] = "r1234 1a\n";
|
||||
const uint8_t test2[] = "wffe7 3 11 22 33\n";
|
||||
const uint8_t test2_buf[] = {0x11, 0x22, 0x33};
|
||||
const uint8_t test3[] = "\n";
|
||||
const uint8_t test4[] = "w1ae7 8 1122 3344 ffee e0 f5\n";
|
||||
const uint8_t test4_buf[] = {0x11, 0x22, 0x33, 0x44, 0xff, 0xee, 0xe0, 0xf5};
|
||||
struct test_case cases[] = {
|
||||
{test0, sizeof(test0), 1, 0, 0x0102, 1, NULL},
|
||||
{test1, sizeof(test1), 1, 0, 0x1234, 0x1a, NULL},
|
||||
{test2, sizeof(test2), 0, 1, 0xffe7, 0x3, test2_buf},
|
||||
{test3, sizeof(test3), 0, 0, 0, 0, NULL},
|
||||
{test4, sizeof(test4), 0, 1, 0x1ae7, 8, test4_buf},
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) {
|
||||
reset_reg();
|
||||
parse_and_execute_cmd(cases[i].s, cases[i].s_len);
|
||||
if (!check_reg(&cases[i])) {
|
||||
printf("E: test failed\n");
|
||||
dump_expected(&cases[i]);
|
||||
return -1;
|
||||
} else {
|
||||
printf("I: test %d passed\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "hardware/spi.h"
|
||||
|
||||
#include "spi_parser.h"
|
||||
|
||||
#define AT86_RSTN 7
|
||||
|
||||
static void cs_select() {
|
||||
asm volatile("nop \n nop \n nop");
|
||||
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 0); // Active low
|
||||
asm volatile("nop \n nop \n nop");
|
||||
}
|
||||
|
||||
static void cs_deselect() {
|
||||
asm volatile("nop \n nop \n nop");
|
||||
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
|
||||
asm volatile("nop \n nop \n nop");
|
||||
}
|
||||
|
||||
static void spi_register_write(uint16_t address, const uint8_t *buf, uint16_t len) {
|
||||
uint8_t cmd[2] = { address >> 8, address };
|
||||
cmd[0] &= ~(3 << 6); // mask out top two bits
|
||||
// to set MODE to read
|
||||
cs_select();
|
||||
spi_write_blocking(PICO_DEFAULT_SPI, cmd, 2);
|
||||
spi_write_blocking(PICO_DEFAULT_SPI, buf, len);
|
||||
cs_deselect();
|
||||
}
|
||||
|
||||
static void spi_register_read(uint16_t address, uint8_t *buf, uint16_t len) {
|
||||
uint8_t cmd[2] = { address >> 8, address };
|
||||
cmd[0] &= ~(3 << 6); // mask out top two bits
|
||||
cmd[0] |= (2 << 6);
|
||||
// set MODE[0] to set to write
|
||||
cs_select();
|
||||
spi_write_blocking(PICO_DEFAULT_SPI, cmd, 2);
|
||||
spi_read_blocking(PICO_DEFAULT_SPI, 0, buf, len);
|
||||
cs_deselect();
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
// 10 MHz
|
||||
spi_init(PICO_DEFAULT_SPI, 10*1000*1000);
|
||||
gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
|
||||
gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
|
||||
gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
|
||||
|
||||
gpio_init(PICO_DEFAULT_SPI_CSN_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT);
|
||||
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
|
||||
|
||||
gpio_set_dir(AT86_RSTN, GPIO_OUT);
|
||||
gpio_put(AT86_RSTN, 1);
|
||||
|
||||
uint8_t cmd[64];
|
||||
int cmd_idx = 0;
|
||||
while (true) {
|
||||
if (stdio_usb_connected()) {
|
||||
// UART parse loop
|
||||
int maybe_ch = getchar_timeout_us(1000);
|
||||
if (maybe_ch != PICO_ERROR_TIMEOUT) {
|
||||
if (maybe_ch == 0x0a) {
|
||||
cmd[cmd_idx] = 0;
|
||||
putchar(maybe_ch);
|
||||
// process command
|
||||
parse_and_execute_cmd(cmd, cmd_idx);
|
||||
cmd_idx = 0;
|
||||
} else if (maybe_ch == 0x1b) {
|
||||
putchar('^');
|
||||
putchar('\n');
|
||||
cmd_idx = 0;
|
||||
} else {
|
||||
putchar(maybe_ch);
|
||||
cmd[cmd_idx] = maybe_ch;
|
||||
if (cmd_idx < sizeof(cmd)-1) {
|
||||
++cmd_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sleep_ms(10);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue