2018-03-15 18:59:44 +01:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/** BusVoodoo SPI mode (code)
* @ file busvoodoo_spi . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ date 2018
* @ note peripherals used : SPI @ ref busvoodoo_spi
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // standard utilities
# include <string.h> // string utilities
/* STM32 (including CM3) libraries */
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/spi.h> // SPI library
/* own libraries */
# include "global.h" // board definitions
# include "print.h" // printing utilities
# include "menu.h" // menu definitions
# include "busvoodoo_global.h" // BusVoodoo definitions
# include "busvoodoo_oled.h" // OLED utilities
# include "busvoodoo_spi.h" // own definitions
/** @defgroup busvoodoo_spi SPI peripheral used for SPI communication
* @ {
*/
# define BUSVOODOO_SPI_ID 2 /**< SPI peripheral */
/** @} */
/** mode setup stage */
static enum busvoodoo_spi_setting_t {
BUSVOODOO_SPI_SETTING_NONE ,
BUSVOODOO_SPI_SETTING_DUPLEX ,
BUSVOODOO_SPI_SETTING_FREQUENCY ,
BUSVOODOO_SPI_SETTING_DATABITS ,
BUSVOODOO_SPI_SETTING_BITORDER ,
BUSVOODOO_SPI_SETTING_MODE ,
BUSVOODOO_SPI_SETTING_DRIVE ,
BUSVOODOO_SPI_SETTING_DONE ,
} busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_NONE ;
/** SPI duplex mode (true = full-duplex, false = bidirectional) */
static bool busvoodoo_spi_duplex = true ;
/** SPI baud rate (corresponding to baud rate control, e.g. 36MHz/(2<<br))) */
static uint8_t busvoodoo_spi_baudrate = 1 ;
/** SPI data frame bit width (8 or 16) */
static uint8_t busvoodoo_spi_databits = 8 ;
2018-03-17 19:42:01 +01:00
/** SPI data frame bit order (true = MSb first, false = LSb first) */
2018-03-15 18:59:44 +01:00
static bool busvoodoo_spi_bitorder = true ;
/** SPI mode (defining clock polarity and phase) */
static uint8_t busvoodoo_spi_standard_mode = 0 ;
/** pin drive mode (true = push-pull, false = open-drain) */
static bool busvoodoo_spi_drive = true ;
/** if embedded pull-up resistors are used */
static bool busvoodoo_spi_pullup = true ;
/** setup SPI mode
* @ param [ out ] prefix terminal prompt prefix
* @ param [ in ] line terminal prompt line to configure mode
* @ return if setup is complete
*/
static bool busvoodoo_spi_setup ( char * * prefix , const char * line )
{
bool complete = false ; // is the setup complete
if ( NULL = = line ) { // first call
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_NONE ; // re-start configuration
}
switch ( busvoodoo_spi_setting ) {
case BUSVOODOO_SPI_SETTING_NONE :
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_DUPLEX ;
printf ( " 1) full-duplex \n " ) ;
printf ( " 2) bidirectional \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " duplex mode (1,2) [%c] " , busvoodoo_spi_duplex ? ' 1 ' : ' 2 ' ) ;
* prefix = busvoodoo_global_string ; // ask for setting
break ;
case BUSVOODOO_SPI_SETTING_DUPLEX :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_FREQUENCY ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t duplex = atoi ( line ) ; // parse setting
if ( 1 = = duplex | | 2 = = duplex ) { // check setting
busvoodoo_spi_duplex = ( 1 = = duplex ) ; // remember setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_FREQUENCY ; // go to next setting
}
}
if ( BUSVOODOO_SPI_SETTING_FREQUENCY = = busvoodoo_spi_setting ) { // if next setting
// SPI2 used APB1 as PCLK, which has a maximum frequency of 36 MHz
for ( uint8_t div = 0 ; div < 8 ; div + + ) {
printf ( " %u) %u kHz \n " , div + 1 , 36000 / ( 2 < < div ) ) ; // print possible frequencies
}
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " frequency (1-9) [%u] " , busvoodoo_spi_baudrate + 1 ) ;
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_SPI_SETTING_FREQUENCY :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_DATABITS ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t baudrate = atoi ( line ) ; // parse setting
if ( baudrate > 0 & & baudrate < = 9 ) { // check setting
busvoodoo_spi_baudrate = baudrate - 1 ; // remember setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_DATABITS ; // go to next setting
}
}
if ( BUSVOODOO_SPI_SETTING_DATABITS = = busvoodoo_spi_setting ) { // if next setting
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " data frame width in bits (8,16) [%u] " , busvoodoo_spi_databits ) ; // prepare next setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_SPI_SETTING_DATABITS :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_BITORDER ; // go to next setting
} else if ( 1 = = strlen ( line ) | | 2 = = strlen ( line ) ) { // setting provided
uint8_t databits = atoi ( line ) ; // parse setting
if ( 8 = = databits | | 16 = = databits ) { // check setting
busvoodoo_spi_databits = databits ; // remember setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_BITORDER ; // go to next setting
}
}
if ( BUSVOODOO_SPI_SETTING_BITORDER = = busvoodoo_spi_setting ) { // if next setting
printf ( " 1) most significant bit first \n " ) ;
printf ( " 2) least significant bit first \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " data frame bit order (1,2) [%c] " , busvoodoo_spi_bitorder ? ' 1 ' : ' 2 ' ) ; // prepare next setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_SPI_SETTING_BITORDER :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_MODE ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t bitorder = atoi ( line ) ; // parse setting
if ( 1 = = bitorder | | 2 = = bitorder ) { // check setting
busvoodoo_spi_bitorder = ( 1 = = bitorder ) ; // remember setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_MODE ; // go to next setting
}
}
if ( BUSVOODOO_SPI_SETTING_MODE = = busvoodoo_spi_setting ) { // if next setting
printf ( " 1) mode 0 (clock polarity: idle low, clock phase: sample data on rising edge) \n " ) ;
printf ( " 2) mode 1 (clock polarity: idle low, clock phase: sample data on falling edge) \n " ) ;
printf ( " 3) mode 2 (clock polarity: idle high, clock phase: sample data on falling edge) \n " ) ;
printf ( " 4) mode 3 (clock polarity: idle high, clock phase: sample data on rising edge) \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " mode (1,2,3,4) [%u] " , busvoodoo_spi_standard_mode + 1 ) ; // prepare next setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_SPI_SETTING_MODE :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_DRIVE ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t mode = atoi ( line ) ; // parse setting
if ( mode > = 1 & & mode < = 4 ) {
busvoodoo_spi_standard_mode = mode - 1 ; // remember setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_DRIVE ; // go to next setting
}
}
if ( BUSVOODOO_SPI_SETTING_DRIVE = = busvoodoo_spi_setting ) { // if next setting
printf ( " 1) push-pull (3.3V) \n " ) ;
2018-03-20 10:46:40 +01:00
printf ( " 2) open-drain, with embedded pull-up resistors (2kO) \n " ) ;
printf ( " 3) open-drain, with external pull-up resistors \n " ) ;
2018-03-15 18:59:44 +01:00
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " drive mode (1,2,3) [%c] " , busvoodoo_spi_drive ? ' 1 ' : ( busvoodoo_spi_pullup ? ' 2 ' : ' 3 ' ) ) ; // show drive mode
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_SPI_SETTING_DRIVE :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_DONE ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t drive = atoi ( line ) ; // parse setting
if ( 1 = = drive | | 2 = = drive | | 3 = = drive ) { // check setting
busvoodoo_spi_drive = ( 1 = = drive ) ; // remember setting
busvoodoo_spi_pullup = ( 2 = = drive ) ; // remember setting
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_DONE ; // go to next setting
}
}
2018-03-20 10:42:59 +01:00
if ( BUSVOODOO_SPI_SETTING_DONE = = busvoodoo_spi_setting ) { // we have all settings, configure SPI
2018-03-15 18:59:44 +01:00
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable clock for SPI alternate function
rcc_periph_clock_enable ( RCC_SPI ( BUSVOODOO_SPI_ID ) ) ; // enable clock for SPI peripheral
spi_reset ( SPI ( BUSVOODOO_SPI_ID ) ) ; // clear SPI values to default
spi_set_baudrate_prescaler ( SPI ( BUSVOODOO_SPI_ID ) , busvoodoo_spi_baudrate ) ; // set baud rate (i.e. frequency)
spi_set_standard_mode ( SPI ( BUSVOODOO_SPI_ID ) , busvoodoo_spi_standard_mode ) ; // set SPI mode
if ( 8 = = busvoodoo_spi_databits ) { // set data frame bit width
spi_set_dff_8bit ( SPI ( BUSVOODOO_SPI_ID ) ) ; // set to 8 bits
} else {
spi_set_dff_16bit ( SPI ( BUSVOODOO_SPI_ID ) ) ; // set to 16 bits
}
if ( busvoodoo_spi_bitorder ) { // set bit order
spi_send_msb_first ( SPI ( BUSVOODOO_SPI_ID ) ) ; // MSb-first
} else {
spi_send_lsb_first ( SPI ( BUSVOODOO_SPI_ID ) ) ; // LSb-first
}
rcc_periph_clock_enable ( RCC_SPI_MOSI_PORT ( BUSVOODOO_SPI_ID ) ) ; // enable clock for GPIO peripheral for MOSI signal
if ( busvoodoo_spi_drive ) {
gpio_set_mode ( SPI_MOSI_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , SPI_MOSI_PIN ( BUSVOODOO_SPI_ID ) ) ; // set MOSI as output
} else {
gpio_set_mode ( SPI_MOSI_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , SPI_MOSI_PIN ( BUSVOODOO_SPI_ID ) ) ; // set MOSI as output
}
if ( busvoodoo_spi_duplex ) {
spi_set_full_duplex_mode ( SPI ( BUSVOODOO_SPI_ID ) ) ; // set full duplex mode
rcc_periph_clock_enable ( RCC_SPI_MISO_PORT ( BUSVOODOO_SPI_ID ) ) ; // enable clock for GPIO peripheral for MISO signal
if ( busvoodoo_spi_drive ) {
2018-03-17 19:42:01 +01:00
gpio_set_mode ( SPI_MISO_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , SPI_MISO_PIN ( BUSVOODOO_SPI_ID ) ) ; // set MISO as input
2018-03-15 18:59:44 +01:00
} else {
2018-03-17 19:42:01 +01:00
gpio_set_mode ( SPI_MISO_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , SPI_MISO_PIN ( BUSVOODOO_SPI_ID ) ) ; // set MISO as input
2018-03-15 18:59:44 +01:00
}
} else {
spi_set_bidirectional_mode ( SPI ( BUSVOODOO_SPI_ID ) ) ; // set bidirectional mode
}
rcc_periph_clock_enable ( RCC_SPI_SCK_PORT ( BUSVOODOO_SPI_ID ) ) ; // enable clock for GPIO peripheral for SCK signal
if ( busvoodoo_spi_drive ) {
2018-03-17 19:42:01 +01:00
gpio_set_mode ( SPI_SCK_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , SPI_SCK_PIN ( BUSVOODOO_SPI_ID ) ) ; // set SCK as output
2018-03-15 18:59:44 +01:00
} else {
2018-03-17 19:42:01 +01:00
gpio_set_mode ( SPI_SCK_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , SPI_SCK_PIN ( BUSVOODOO_SPI_ID ) ) ; // set SCK as output
2018-03-15 18:59:44 +01:00
}
spi_enable_software_slave_management ( SPI ( BUSVOODOO_SPI_ID ) ) ; // control SS by software
2018-03-17 19:42:01 +01:00
spi_set_nss_high ( SPI ( BUSVOODOO_SPI_ID ) ) ; // set NSS high (internally) so we can output
2018-03-15 18:59:44 +01:00
rcc_periph_clock_enable ( RCC_SPI_NSS_PORT ( BUSVOODOO_SPI_ID ) ) ; // enable clock for GPIO peripheral for SS signal
gpio_set ( SPI_NSS_PORT ( BUSVOODOO_SPI_ID ) , SPI_NSS_PIN ( BUSVOODOO_SPI_ID ) ) ; // de-select slave (on high)
if ( busvoodoo_spi_drive ) {
gpio_set_mode ( SPI_NSS_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , SPI_NSS_PIN ( BUSVOODOO_SPI_ID ) ) ; // set NSS as output
} else {
gpio_set_mode ( SPI_NSS_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , SPI_NSS_PIN ( BUSVOODOO_SPI_ID ) ) ; // set NSS as output
}
spi_set_master_mode ( SPI ( BUSVOODOO_SPI_ID ) ) ; // set master mode
spi_enable ( SPI ( BUSVOODOO_SPI_ID ) ) ; // enable SPI
2018-03-20 10:42:59 +01:00
if ( ! busvoodoo_spi_drive & & busvoodoo_spi_pullup ) {
busvoodoo_embedded_pullup ( true ) ; // set embedded pull-ups
printf ( " use LV to set voltage \n " ) ;
2018-03-15 18:59:44 +01:00
}
led_off ( ) ; // disable LED because there is no activity
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_NONE ; // restart settings next time
* prefix = " SPI " ; // display mode
busvoodoo_oled_text_left ( * prefix ) ; // set mode title on OLED display
2018-03-20 10:15:36 +01:00
const char * pinout_io [ 10 ] = { " GND " , " 5V " , " 3V3 " , " LV " , NULL , NULL , " MISO " , " SCK " , " MOSI " , " SS " } ; // SPI mode pinout
2018-03-15 18:59:44 +01:00
if ( ! busvoodoo_spi_duplex ) {
pinout_io [ 6 ] = NULL ; // MISO is not used
pinout_io [ 8 ] = " MOSIMISO " ; // MOSI is also used as MISO
}
for ( uint8_t i = 0 ; i < LENGTH ( pinout_io ) & & i < LENGTH ( busvoodoo_global_pinout_io ) ; i + + ) {
busvoodoo_global_pinout_io [ i ] = pinout_io [ i ] ; // set pin names
}
busvoodoo_oled_text_pinout ( pinout_io , true ) ; // set pinout on display
busvoodoo_oled_update ( ) ; // update display to show text and pinout
complete = true ; // configuration is complete
}
break ;
default : // unknown case
busvoodoo_spi_setting = BUSVOODOO_SPI_SETTING_NONE ; // restart settings next time
break ;
}
return complete ;
}
2018-03-17 19:42:01 +01:00
/** write to SPI
* @ param [ in ] value value to write
*/
static void busvoodoo_spi_write ( uint16_t value )
{
if ( 8 = = busvoodoo_spi_databits ) {
printf ( " write: 0x%02x \n " , ( uint8_t ) value ) ;
} else {
printf ( " write: 0x%04x \n " , value ) ;
}
while ( ! ( SPI_SR ( SPI ( BUSVOODOO_SPI_ID ) ) & SPI_SR_TXE ) ) ; // wait until Tx is empty
( void ) SPI_DR ( SPI ( BUSVOODOO_SPI_ID ) ) ; // clear RXNE flag by reading previously received data
spi_send ( SPI ( BUSVOODOO_SPI_ID ) , value ) ; // send data
if ( busvoodoo_spi_duplex ) {
value = spi_read ( SPI ( BUSVOODOO_SPI_ID ) ) ; // read data
if ( 8 = = busvoodoo_spi_databits ) {
printf ( " read: 0x%02x \n " , ( uint8_t ) value ) ;
} else {
printf ( " read: 0x%04x \n " , value ) ;
}
}
}
/** read from SPI
*/
static void busvoodoo_spi_read ( void )
{
if ( busvoodoo_spi_duplex ) { // in full duplex mode
busvoodoo_spi_write ( 0xffff ) ; // send any value in order to read
} else { // unidirectional mode
gpio_set_mode ( SPI_MOSI_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , SPI_MOSI_PIN ( BUSVOODOO_SPI_ID ) ) ; // set MOSI as input
while ( ! ( SPI_SR ( SPI ( BUSVOODOO_SPI_ID ) ) & SPI_SR_TXE ) ) ; // wait until Tx is empty
( void ) SPI_DR ( SPI ( BUSVOODOO_SPI_ID ) ) ; // clear RXNE flag by reading previously received data
spi_send ( SPI ( BUSVOODOO_SPI_ID ) , 0xffff ) ; // send any value
uint16_t value = spi_read ( SPI ( BUSVOODOO_SPI_ID ) ) ; // read data back
if ( 8 = = busvoodoo_spi_databits ) {
printf ( " read: 0x%02x \n " , ( uint8_t ) value ) ;
} else {
printf ( " read: 0x%04x \n " , value ) ;
}
if ( busvoodoo_spi_drive ) {
gpio_set_mode ( SPI_MOSI_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , SPI_MOSI_PIN ( BUSVOODOO_SPI_ID ) ) ; // set MOSI as output
} else {
gpio_set_mode ( SPI_MOSI_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , SPI_MOSI_PIN ( BUSVOODOO_SPI_ID ) ) ; // set MOSI as output
}
}
}
/** exit SPI mode
2018-03-15 18:59:44 +01:00
*/
static void busvoodoo_spi_exit ( void )
{
spi_reset ( SPI ( BUSVOODOO_SPI_ID ) ) ; // clear SPI values to default
spi_disable ( SPI ( BUSVOODOO_SPI_ID ) ) ; // disable SPI
rcc_periph_clock_disable ( RCC_SPI ( BUSVOODOO_SPI_ID ) ) ; // disable domain clock
gpio_set_mode ( SPI_SCK_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , SPI_SCK_PIN ( BUSVOODOO_SPI_ID ) ) ; // set pin back to floating input
gpio_set_mode ( SPI_NSS_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , SPI_NSS_PIN ( BUSVOODOO_SPI_ID ) ) ; // set pin back to floating input
gpio_set_mode ( SPI_MISO_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , SPI_MISO_PIN ( BUSVOODOO_SPI_ID ) ) ; // set pin back to floating input
gpio_set_mode ( SPI_MOSI_PORT ( BUSVOODOO_SPI_ID ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , SPI_MOSI_PIN ( BUSVOODOO_SPI_ID ) ) ; // set pin back to floating input
busvoodoo_embedded_pullup ( false ) ; // disable embedded pull-ups
}
2018-03-17 19:42:01 +01:00
/** perform SPI action
* @ param [ in ] action action to perform
* @ param [ in ] repetition how many times to perform the action
* @ param [ in ] perform the action ( true ) or just check it ( false )
* @ return true if the action has been performed , false if it is malformed
*/
static bool busvoodoo_spi_action ( const char * action , uint32_t repetition , bool perform )
{
uint32_t length = strlen ( action ) ; // remember length since it will be used a number of times
if ( NULL = = action | | 0 = = length ) { // there is nothing to do
return true ;
}
if ( 1 = = length & & ' r ' = = action [ 0 ] ) { // read data
if ( ! perform ) {
return true ;
}
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_spi_read ( ) ; // write from SPI
}
} else if ( 1 = = length & & ' [ ' = = action [ 0 ] ) { // select slave
if ( ! perform ) {
return true ;
}
printf ( " select slave \n " ) ;
while ( SPI_SR ( SPI ( BUSVOODOO_SPI_ID ) ) & SPI_SR_BSY ) ; // wait until not busy
gpio_clear ( SPI_NSS_PORT ( BUSVOODOO_SPI_ID ) , SPI_NSS_PIN ( BUSVOODOO_SPI_ID ) ) ; // select slave (on low)
} else if ( 1 = = length & & ' ] ' = = action [ 0 ] ) { // deselect slave
if ( ! perform ) {
return true ;
}
printf ( " de-select slave \n " ) ;
while ( SPI_SR ( SPI ( BUSVOODOO_SPI_ID ) ) & SPI_SR_BSY ) ; // wait until not busy
gpio_set ( SPI_NSS_PORT ( BUSVOODOO_SPI_ID ) , SPI_NSS_PIN ( BUSVOODOO_SPI_ID ) ) ; // de-select slave (on high)
} else if ( 1 = = length & & ' u ' = = action [ 0 ] ) { // sleep us
if ( ! perform ) {
return true ;
}
printf ( " wait for %u us \n " , repetition ) ;
sleep_us ( repetition ) ; // sleep
} else if ( 1 = = length & & ' m ' = = action [ 0 ] ) { // sleep ms
if ( ! perform ) {
return true ;
}
printf ( " wait for %u ms \n " , repetition ) ;
sleep_ms ( repetition ) ; // sleep
} else if ( ' 0 ' = = action [ 0 ] ) { // send digit
if ( 1 = = length ) { // just send 0
if ( ! perform ) {
return true ;
}
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_spi_write ( 0 ) ; // write to SPI
}
} else if ( ' x ' = = action [ 1 ] | | ' b ' = = action [ 1 ] ) { // send hex/binary
return busvoodoo_spi_action ( action + 1 , repetition , perform ) ; // just retry without leading 0
} else if ( action [ 1 ] > = ' 0 ' & & action [ 1 ] < = ' 9 ' ) { // send decimal
return busvoodoo_spi_action ( action + 1 , repetition , perform ) ; // just retry without leading 0
} else { // malformed action
return false ;
}
} else if ( ' x ' = = action [ 0 ] & & length > 1 ) { // send hexadecimal value
for ( uint32_t i = 1 ; i < length ; i + + ) { // check string
if ( ! ( ( action [ i ] > = ' 0 ' & & action [ i ] < = ' 9 ' ) | | ( action [ i ] > = ' a ' & & action [ i ] < = ' f ' ) | | ( action [ i ] > = ' A ' & & action [ i ] < = ' F ' ) ) ) { // check for hexadecimal character
return false ; // not an hexadecimal string
}
}
if ( ! perform ) {
return true ;
}
uint32_t value = strtol ( & action [ 1 ] , NULL , 16 ) ; // get hex value
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_spi_write ( value ) ; // write to SPI
}
} else if ( ' b ' = = action [ 0 ] & & length > 1 ) { // send binary value
for ( uint32_t i = 1 ; i < length ; i + + ) { // check string
if ( action [ i ] < ' 0 ' | | action [ i ] > ' 1 ' ) { // check for binary character
return false ; // not a binary string
}
}
if ( ! perform ) {
return true ;
}
uint32_t value = strtol ( & action [ 1 ] , NULL , 2 ) ; // get binary value
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_spi_write ( value ) ; // write to SPI
}
} else if ( action [ 0 ] > = ' 1 ' & & action [ 0 ] < = ' 9 ' ) { // send decimal value
for ( uint32_t i = 1 ; i < length ; i + + ) { // check string
if ( action [ i ] < ' 0 ' | | action [ i ] > ' 9 ' ) { // check for decimal character
return false ; // not a decimal string
}
}
if ( ! perform ) {
return true ;
}
uint32_t value = strtol ( & action [ 0 ] , NULL , 10 ) ; // get decimal value
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_spi_write ( value ) ; // write to SPI
}
} else if ( length > = 2 & & ( ' " ' = = action [ 0 ] | | ' \' ' = = action [ 0 ] ) & & ( action [ length - 1 ] = = action [ 0 ] ) ) { // send ASCII character
if ( ! perform ) {
return true ;
}
for ( uint32_t r = 0 ; r < repetition ; r + + ) {
for ( uint32_t i = 1 ; i < length - 1 ; i + + ) { // go through string
busvoodoo_spi_write ( action [ i ] ) ; // write to SPI
}
}
} else { // malformed action
return false ;
}
return true ; // all went well
}
2018-03-15 18:59:44 +01:00
// command handlers
2018-03-21 14:14:46 +01:00
/** command to perform actions
* @ param [ in ] argument actions to perform
2018-03-15 18:59:44 +01:00
*/
2018-03-21 14:14:46 +01:00
static void busvoodoo_spi_command_actions ( void * argument )
2018-03-15 18:59:44 +01:00
{
2018-03-17 19:42:01 +01:00
if ( NULL = = argument | | 0 = = strlen ( argument ) ) {
printf ( " available actions (separated by space or ,): \n " ) ;
printf ( " [/] \t select/deselect slave \n " ) ;
printf ( " 0 \t write decimal value \n " ) ;
printf ( " 0x0 \t write hexadecimal value \n " ) ;
printf ( " 0b0 \t write binary value \n " ) ;
printf ( " \" a \" /'a' \t write ASCII characters \n " ) ;
printf ( " r \t read value \n " ) ;
printf ( " u/m \t wait 1 us/ms \n " ) ;
printf ( " :n \t repeat action n times \n " ) ;
return ;
}
// copy argument since it will be modified
char * copy = calloc ( strlen ( argument ) + 1 , sizeof ( char ) ) ;
if ( ! copy ) {
while ( true ) ;
}
strncpy ( copy , argument , strlen ( argument ) + 1 ) ;
// verify and perform actions
if ( ! busvoodoo_global_actions ( copy , false , & busvoodoo_spi_action ) ) { // verify actions
printf ( " malformed action(s) \n " ) ;
} else { // action are ok
busvoodoo_global_actions ( argument , true , & busvoodoo_spi_action ) ; // perform action
}
free ( copy ) ; // release memory
2018-03-15 18:59:44 +01:00
}
static const struct menu_command_t busvoodoo_spi_commands [ ] = {
{
2018-03-17 19:42:01 +01:00
' a ' ,
" action " ,
2018-03-21 14:14:46 +01:00
" perform protocol actions " ,
2018-03-17 19:42:01 +01:00
MENU_ARGUMENT_STRING ,
" [actions] " ,
2018-03-21 14:14:46 +01:00
& busvoodoo_spi_command_actions ,
2018-03-15 18:59:44 +01:00
} ,
} ;
struct busvoodoo_mode_t busvoodoo_spi_mode = {
" spi " ,
" Serial Peripheral Interface " ,
& busvoodoo_spi_setup ,
busvoodoo_spi_commands ,
LENGTH ( busvoodoo_spi_commands ) ,
& busvoodoo_spi_exit ,
} ;