2018-05-09 21:27:44 +02: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 1-wire mode (code)
* @ file busvoodoo_onewire . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ date 2018
* @ note peripherals used : timer @ ref onewire_master_timer
*/
/* 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/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/timer.h> // timer library
/* own libraries */
# include "global.h" // board definitions
# include "print.h" // printing utilities
# include "menu.h" // menu definitions
# include "onewire_master.h" // 1-wire methods
# include "busvoodoo_global.h" // BusVoodoo definitions
# include "busvoodoo_oled.h" // OLED utilities
# include "busvoodoo_onewire.h" // own definitions
/** mode setup stage */
static enum busvoodoo_onewire_setting_t {
BUSVOODOO_ONEWIRE_SETTING_NONE ,
BUSVOODOO_ONEWIRE_SETTING_PULLUP ,
BUSVOODOO_ONEWIRE_SETTING_POWER ,
BUSVOODOO_ONEWIRE_SETTING_DONE ,
} busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE ; /**< current mode setup stage */
/** if embedded pull-up resistors are used */
static bool busvoodoo_onewire_embedded_pullup = true ;
/** time (in ms) between slot to provide power */
static bool busvoodoo_onewire_power = false ;
/** setup 1-wire mode
* @ param [ out ] prefix terminal prompt prefix
* @ param [ in ] line terminal prompt line to configure mode
* @ return if setup is complete
*/
static bool busvoodoo_onewire_setup ( char * * prefix , const char * line )
{
bool complete = false ; // is the setup complete
if ( NULL = = line ) { // first call
busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE ; // re-start configuration
}
switch ( busvoodoo_onewire_setting ) {
case BUSVOODOO_ONEWIRE_SETTING_NONE :
busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_PULLUP ; // go to first setting
printf ( " 1) use embedded pull-up resistor (2kO) \n " ) ;
printf ( " 2) use external pull-up resistor \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " pull-up mode (1,2) [%c] " , busvoodoo_onewire_embedded_pullup ? ' 1 ' : ' 2 ' ) ; // show pull-up setting
* prefix = busvoodoo_global_string ; // display next setting
break ;
case BUSVOODOO_ONEWIRE_SETTING_PULLUP :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_POWER ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t pullup = atoi ( line ) ; // parse setting
if ( 1 = = pullup | | 2 = = pullup ) { // check setting
busvoodoo_onewire_embedded_pullup = ( 1 = = pullup ) ; // remember setting
busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_POWER ; // go to next setting
}
}
if ( BUSVOODOO_ONEWIRE_SETTING_POWER = = busvoodoo_onewire_setting ) {
printf ( " 1) don't drive 1-wire data line (target uses external or parasitic power) \n " ) ;
printf ( " 2) power 1-wire data line at 3.3V when not communicating (not multi-master compatible) \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " power source (1,2) [%c] " , busvoodoo_onewire_power ? ' 2 ' : ' 1 ' ) ; // show power setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_ONEWIRE_SETTING_POWER :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_DONE ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t power = atoi ( line ) ; // parse setting
if ( 1 = = power | | 2 = = power ) { // check setting
busvoodoo_onewire_power = ( 1 = = power ) ; // remember setting
busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_DONE ; // go to next setting
}
}
if ( BUSVOODOO_ONEWIRE_SETTING_DONE = = busvoodoo_onewire_setting ) { // we have all settings, configure SPI
onewire_master_setup ( ) ; // setup 1-wire
if ( busvoodoo_onewire_power ) {
gpio_set_mode ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( ONEWIRE_MASTER_PIN ) ) ; // provide power (external pull-up resistor is still require for communication)
}
if ( busvoodoo_onewire_embedded_pullup ) {
if ( ! busvoodoo_onewire_power ) {
busvoodoo_embedded_pullup ( true ) ; // set embedded pull-ups
}
printf ( " use LV to set pull-up voltage \n " ) ;
}
busvoodoo_led_blue_off ( ) ; // disable blue LED because there is no activity
busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE ; // restart settings next time
* prefix = " 1-Wire " ; // display mode
busvoodoo_oled_text_left ( * prefix ) ; // set mode title on OLED display
const char * pinout_io [ 10 ] = { " GND " , " 5V " , " 3V3 " , " LV " , NULL , " 1WR " , NULL , NULL , NULL , NULL } ; // 1-wire mode pinout
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
}
if ( busvoodoo_full ) {
const char * pinout_rscan [ 5 ] = { " HV " , NULL , NULL , NULL , NULL } ; // HiZ mode RS/CAN pinout
for ( uint8_t i = 0 ; i < LENGTH ( pinout_rscan ) & & i < LENGTH ( busvoodoo_global_pinout_rscan ) ; i + + ) {
busvoodoo_global_pinout_rscan [ i ] = pinout_rscan [ 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_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE ; // restart settings next time
break ;
}
return complete ;
}
/** write to 1-wire
* @ param [ in ] value value to write
*/
static void busvoodoo_onewire_write ( uint8_t value )
{
printf ( " write: 0x%02x " , value ) ;
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // pulse blue LED to show we are writing
if ( ! onewire_master_write_byte ( value ) ) { // send data bytes
printf ( " (error) " ) ;
}
printf ( " \n " ) ;
}
/** read from 1-wire
*/
static void busvoodoo_onewire_read ( void )
{
uint8_t data ; // buffer to read data
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // pulse blue LED to show we are reading
bool error = onewire_master_read_byte ( & data ) ; // read byte
printf ( " read: 0x%02x%s \n " , data , error ? " " : " (error) " ) ;
}
/** exit 1-wire mode
*/
static void busvoodoo_onewire_exit ( void )
{
onewire_master_release ( ) ; // release peripheral
busvoodoo_embedded_pullup ( false ) ; // disable embedded pull-ups
}
/** perform 1-wire 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_onewire_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_onewire_read ( ) ; // read from 1-wire
}
} else if ( 1 = = length & & ' [ ' = = action [ 0 ] ) { // start transaction with slave presence detection
if ( ! perform ) {
return true ;
}
if ( busvoodoo_onewire_power ) {
gpio_set_mode ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_OPENDRAIN , GPIO ( ONEWIRE_MASTER_PIN ) ) ; // remove power from data line
}
if ( ! gpio_get ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO ( ONEWIRE_MASTER_PIN ) ) ) {
printf ( " WARNING: data line does not seem to be pulled up \n " ) ;
}
printf ( " start transaction: " ) ;
bool presence = onewire_master_reset ( ) ; // send reset pulse and detect slave presence
printf ( " slave presence %sdetected \n " , presence ? " " : " not " ) ;
} else if ( 1 = = length & & ' ] ' = = action [ 0 ] ) { // stop transaction
if ( ! perform ) {
return true ;
}
printf ( " end transaction%s \n " , busvoodoo_onewire_power ? " and provide power on data line " : " " ) ;
if ( busvoodoo_onewire_power ) {
gpio_set_mode ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( ONEWIRE_MASTER_PIN ) ) ; // provide power (external pull-up resistor is still require for communication)
}
} 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_onewire_write ( 0 ) ; // write to SPI
}
} else if ( ' x ' = = action [ 1 ] | | ' b ' = = action [ 1 ] ) { // send hex/binary
return busvoodoo_onewire_action ( action + 1 , repetition , perform ) ; // just retry without leading 0
} else if ( action [ 1 ] > = ' 0 ' & & action [ 1 ] < = ' 9 ' ) { // send decimal
return busvoodoo_onewire_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_onewire_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_onewire_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_onewire_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_onewire_write ( action [ i ] ) ; // write to SPI
}
}
} else { // malformed action
return false ;
}
return true ; // all went well
}
// command handlers
/** command to perform actions
* @ param [ in ] argument actions to perform
*/
static void busvoodoo_onewire_command_actions ( void * argument )
{
if ( NULL = = argument | | 0 = = strlen ( argument ) ) {
printf ( " available actions (separated by space or ,): \n " ) ;
printf ( " [ \t start transaction: send reset pulse and detect slave presence \n " ) ;
printf ( " ] \t end transaction%s \n " , busvoodoo_onewire_power ? " and provide power on data line " : " " ) ;
printf ( " 0 \t write decimal byte \n " ) ;
printf ( " 0x0 \t write hexadecimal byte \n " ) ;
printf ( " 0b0 \t write binary byte \n " ) ;
printf ( " \" a \" /'a' \t write ASCII characters \n " ) ;
printf ( " r \t read byte \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_onewire_action ) ) { // verify actions
printf ( " malformed action(s) \n " ) ;
} else { // action are OK
busvoodoo_global_actions ( argument , true , & busvoodoo_onewire_action ) ; // perform action
}
free ( copy ) ; // release memory
}
/** command to perform ROM search
* @ param [ in ] argument if only ROMs with alarms should be searched
*/
static void busvoodoo_onewire_rom_search ( void * argument )
{
bool alarm = false ; // if only ROMs with alarms should be searched
if ( argument & & 0 = = strcmp ( argument , " alarm " ) ) {
alarm = true ;
}
if ( busvoodoo_onewire_power ) {
gpio_set_mode ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_OPENDRAIN , GPIO ( ONEWIRE_MASTER_PIN ) ) ; // remove power from data line
}
if ( ! gpio_get ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO ( ONEWIRE_MASTER_PIN ) ) ) {
2018-06-16 11:06:24 +02:00
printf ( " WARNING: the data line needs to be pulled up \n " ) ;
return ;
2018-05-09 21:27:44 +02:00
}
bool presence = onewire_master_reset ( ) ; // send reset pulse and detect slave presence
printf ( " slave presence %sdetected \n " , presence ? " " : " not " ) ;
if ( presence ) { // only search if a slave presence has been detected
uint64_t code = 0 ; // code found
uint64_t codes = 0 ; // number of codes found
printf ( " searching ROM codes%s: \n " , alarm ? " with alarm " : " " ) ;
bool next ; // if another ROM code is detected
do { // search until all has been found
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // pulse blue LED to show we are scanning
next = onewire_master_rom_search ( & code , alarm ) ; // search for the code
2018-06-16 11:06:24 +02:00
if ( 0 = = code & & ! gpio_get ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO ( ONEWIRE_MASTER_PIN ) ) ) { // searching for ROM codes does not work when the line is not pulled up
printf ( " not able to search for ROM codes when the data line is not pulled up \n " ) ;
break ;
}
2018-05-09 21:27:44 +02:00
if ( next ) {
presence = onewire_master_reset ( ) ; // send reset pulse and detect slave presence for the next slave
}
printf ( " 0x%016X \n " , code ) ;
codes + + ; // remember we found a code
2018-06-16 11:06:24 +02:00
} while ( presence & & next & & ! user_input_available ) ;
2018-05-09 21:27:44 +02:00
printf ( " %U ROM code(s)%s found \n " , codes , alarm ? " with alarm " : " " ) ;
2018-06-16 11:06:24 +02:00
if ( user_input_available ) { // user interrupted flow
user_input_get ( ) ; // discard user input
}
2018-05-09 21:27:44 +02:00
}
if ( busvoodoo_onewire_power ) {
gpio_set_mode ( GPIO ( ONEWIRE_MASTER_PORT ) , GPIO_MODE_OUTPUT_50_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO ( ONEWIRE_MASTER_PIN ) ) ; // provide power on data line
}
}
/** 1-wire menu commands */
static const struct menu_command_t busvoodoo_onewire_commands [ ] = {
{
. shortcut = ' a ' ,
. name = " action " ,
. command_description = " perform protocol actions " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [actions] " ,
. command_handler = & busvoodoo_onewire_command_actions ,
} ,
{
. shortcut = ' s ' ,
. name = " search " ,
. command_description = " perform ROM search " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [alarm] " ,
. command_handler = & busvoodoo_onewire_rom_search ,
} ,
} ;
const struct busvoodoo_mode_t busvoodoo_onewire_mode = {
. name = " 1-wire " ,
. description = " 1-Wire " ,
. full_only = false ,
. setup = & busvoodoo_onewire_setup ,
. commands = busvoodoo_onewire_commands ,
. commands_nb = LENGTH ( busvoodoo_onewire_commands ) ,
. exit = & busvoodoo_onewire_exit ,
} ;