CuVoodoo STM32F1 firmware template
busvoodoo_onewire.c
Go to the documentation of this file.
1 /* This program is free software: you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License as published by
3  * the Free Software Foundation, either version 3 of the License, or
4  * (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program. If not, see <http://www.gnu.org/licenses/>.
13  *
14  */
21 /* standard libraries */
22 #include <stdint.h> // standard integer types
23 #include <stdlib.h> // standard utilities
24 #include <string.h> // string utilities
25 
26 /* STM32 (including CM3) libraries */
27 #include <libopencm3/stm32/rcc.h> // real-time control clock library
28 #include <libopencm3/stm32/gpio.h> // general purpose input output library
29 #include <libopencm3/stm32/timer.h> // timer library
30 
31 /* own libraries */
32 #include "global.h" // board definitions
33 #include "print.h" // printing utilities
34 #include "menu.h" // menu definitions
35 #include "onewire_master.h" // 1-wire methods
36 #include "busvoodoo_global.h" // BusVoodoo definitions
37 #include "busvoodoo_oled.h" // OLED utilities
38 #include "busvoodoo_onewire.h" // own definitions
39 
42  BUSVOODOO_ONEWIRE_SETTING_NONE,
43  BUSVOODOO_ONEWIRE_SETTING_PULLUP,
44  BUSVOODOO_ONEWIRE_SETTING_POWER,
45  BUSVOODOO_ONEWIRE_SETTING_DONE,
46 } busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE;
50 static bool busvoodoo_onewire_power = false;
51 
57 static bool busvoodoo_onewire_setup(char** prefix, const char* line)
58 {
59  bool complete = false; // is the setup complete
60  if (NULL==line) { // first call
61  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE; // re-start configuration
62  }
63  switch (busvoodoo_onewire_setting) {
64  case BUSVOODOO_ONEWIRE_SETTING_NONE:
65  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_PULLUP; // go to first setting
66  printf("1) use embedded pull-up resistor (2kO)\n");
67  printf("2) use external pull-up resistor\n");
68  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "pull-up mode (1,2) [%c]", busvoodoo_onewire_embedded_pullup ? '1' : '2'); // show pull-up setting
69  *prefix = busvoodoo_global_string; // display next setting
70  break;
71  case BUSVOODOO_ONEWIRE_SETTING_PULLUP:
72  if (NULL==line || 0==strlen(line)) { // use default setting
73  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_POWER; // go to next setting
74  } else if (1==strlen(line)) { // setting provided
75  uint8_t pullup = atoi(line); // parse setting
76  if (1==pullup || 2==pullup) { // check setting
77  busvoodoo_onewire_embedded_pullup = (1==pullup); // remember setting
78  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_POWER; // go to next setting
79  }
80  }
81  if (BUSVOODOO_ONEWIRE_SETTING_POWER==busvoodoo_onewire_setting) {
82  printf("1) don't drive 1-wire data line (target uses external or parasitic power)\n");
83  printf("2) power 1-wire data line at 3.3V when not communicating (not multi-master compatible)\n");
84  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "power source (1,2) [%c]", busvoodoo_onewire_power ? '2' : '1'); // show power setting
85  *prefix = busvoodoo_global_string; // display next setting
86  }
87  break;
88  case BUSVOODOO_ONEWIRE_SETTING_POWER:
89  if (NULL==line || 0==strlen(line)) { // use default setting
90  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_DONE; // go to next setting
91  } else if (1==strlen(line)) { // setting provided
92  uint8_t power = atoi(line); // parse setting
93  if (1==power || 2==power) { // check setting
94  busvoodoo_onewire_power = (1==power); // remember setting
95  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_DONE; // go to next setting
96  }
97  }
98  if (BUSVOODOO_ONEWIRE_SETTING_DONE==busvoodoo_onewire_setting) { // we have all settings, configure SPI
99  onewire_master_setup(); // setup 1-wire
101  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)
102  }
105  busvoodoo_embedded_pullup(true); // set embedded pull-ups
106  }
107  printf("use LV to set pull-up voltage\n");
108  }
109  busvoodoo_led_blue_off(); // disable blue LED because there is no activity
110  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE; // restart settings next time
111  *prefix = "1-Wire"; // display mode
112  busvoodoo_oled_text_left(*prefix); // set mode title on OLED display
113  const char* pinout_io[10] = {"GND", "5V", "3V3", "LV", NULL, "1WR", NULL, NULL, NULL, NULL}; // 1-wire mode pinout
114  for (uint8_t i=0; i<LENGTH(pinout_io) && i<LENGTH(busvoodoo_global_pinout_io); i++) {
115  busvoodoo_global_pinout_io[i] = pinout_io[i]; // set pin names
116  }
117  if (busvoodoo_full) {
118  const char* pinout_rscan[5] = {"HV", NULL, NULL, NULL, NULL}; // HiZ mode RS/CAN pinout
119  for (uint8_t i=0; i<LENGTH(pinout_rscan) && i<LENGTH(busvoodoo_global_pinout_rscan); i++) {
120  busvoodoo_global_pinout_rscan[i] = pinout_rscan[i]; // set pin names
121  }
122  }
123  busvoodoo_oled_text_pinout(pinout_io, true); // set pinout on display
124  busvoodoo_oled_update(); // update display to show text and pinout
125  complete = true; // configuration is complete
126  }
127  break;
128  default: // unknown case
129  busvoodoo_onewire_setting = BUSVOODOO_ONEWIRE_SETTING_NONE; // restart settings next time
130  break;
131  }
132  return complete;
133 }
134 
138 static void busvoodoo_onewire_write(uint8_t value)
139 {
140  printf("write: 0x%02x", value);
141  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show we are writing
142  if (!onewire_master_write_byte(value)) { // send data bytes
143  printf(" (error)");
144  }
145  printf("\n");
146 }
147 
150 static void busvoodoo_onewire_read(void)
151 {
152  uint8_t data; // buffer to read data
153  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show we are reading
154  bool error = onewire_master_read_byte(&data); // read byte
155  printf("read: 0x%02x%s\n", data, error ? "" : " (error)");
156 }
157 
160 static void busvoodoo_onewire_exit(void)
161 {
162  onewire_master_release(); // release peripheral
163  busvoodoo_embedded_pullup(false); // disable embedded pull-ups
164 }
165 
172 static bool busvoodoo_onewire_action(const char* action, uint32_t repetition, bool perform)
173 {
174  uint32_t length = strlen(action); // remember length since it will be used a number of times
175  if (NULL==action || 0==length) { // there is nothing to do
176  return true;
177  }
178 
179  if (1==length && 'r'==action[0]) { // read data
180  if (!perform) {
181  return true;
182  }
183  for (uint32_t i=0; i<repetition; i++) {
184  busvoodoo_onewire_read(); // read from 1-wire
185  }
186  } else if (1==length && '['==action[0]) { // start transaction with slave presence detection
187  if (!perform) {
188  return true;
189  }
191  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
192  }
193  if (!gpio_get(GPIO(ONEWIRE_MASTER_PORT), GPIO(ONEWIRE_MASTER_PIN))) {
194  printf("WARNING: data line does not seem to be pulled up\n");
195  }
196  printf("start transaction: ");
197  bool presence = onewire_master_reset(); // send reset pulse and detect slave presence
198  printf("slave presence %sdetected\n", presence ? "" : "not ");
199  } else if (1==length && ']'==action[0]) { // stop transaction
200  if (!perform) {
201  return true;
202  }
203  printf("end transaction%s\n", busvoodoo_onewire_power ? " and provide power on data line" : "");
205  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)
206  }
207  } else if (1==length && 'u'==action[0]) { // sleep us
208  if (!perform) {
209  return true;
210  }
211  printf("wait for %u us\n", repetition);
212  sleep_us(repetition); // sleep
213  } else if (1==length && 'm'==action[0]) { // sleep ms
214  if (!perform) {
215  return true;
216  }
217  printf("wait for %u ms\n", repetition);
218  sleep_ms(repetition); // sleep
219  } else if ('0'==action[0]) { // send digit
220  if (1==length) { // just send 0
221  if (!perform) {
222  return true;
223  }
224  for (uint32_t i=0; i<repetition; i++) {
225  busvoodoo_onewire_write(0); // write to SPI
226  }
227  } else if ('x'==action[1] || 'b'==action[1]) { // send hex/binary
228  return busvoodoo_onewire_action(action+1, repetition, perform); // just retry without leading 0
229  } else if (action[1]>='0' && action[1]<='9') { // send decimal
230  return busvoodoo_onewire_action(action+1, repetition, perform); // just retry without leading 0
231  } else { // malformed action
232  return false;
233  }
234  } else if ('x'==action[0] && length>1) { // send hexadecimal value
235  for (uint32_t i=1; i<length; i++) { // check string
236  if (!((action[i]>='0' && action[i]<='9') || (action[i]>='a' && action[i]<='f') || (action[i]>='A' && action[i]<='F'))) { // check for hexadecimal character
237  return false; // not an hexadecimal string
238  }
239  }
240  if (!perform) {
241  return true;
242  }
243  uint32_t value = strtol(&action[1], NULL, 16); // get hex value
244  for (uint32_t i=0; i<repetition; i++) {
245  busvoodoo_onewire_write(value); // write to SPI
246  }
247  } else if ('b'==action[0] && length>1) { // send binary value
248  for (uint32_t i=1; i<length; i++) { // check string
249  if (action[i]<'0' || action[i]>'1') { // check for binary character
250  return false; // not a binary string
251  }
252  }
253  if (!perform) {
254  return true;
255  }
256  uint32_t value = strtol(&action[1], NULL, 2); // get binary value
257  for (uint32_t i=0; i<repetition; i++) {
258  busvoodoo_onewire_write(value); // write to SPI
259  }
260  } else if (action[0]>='1' && action[0]<='9') { // send decimal value
261  for (uint32_t i=1; i<length; i++) { // check string
262  if (action[i]<'0' || action[i]>'9') { // check for decimal character
263  return false; // not a decimal string
264  }
265  }
266  if (!perform) {
267  return true;
268  }
269  uint32_t value = strtol(&action[0], NULL, 10); // get decimal value
270  for (uint32_t i=0; i<repetition; i++) {
271  busvoodoo_onewire_write(value); // write to SPI
272  }
273  } else if (length>=2 && ('"'==action[0] || '\''==action[0]) && (action[length-1]==action[0])) { // send ASCII character
274  if (!perform) {
275  return true;
276  }
277  for (uint32_t r=0; r<repetition; r++) {
278  for (uint32_t i=1; i<length-1; i++) { // go through string
279  busvoodoo_onewire_write(action[i]); // write to SPI
280  }
281  }
282  } else { // malformed action
283  return false;
284  }
285 
286  return true; // all went well
287 }
288 
289 // command handlers
290 
294 static void busvoodoo_onewire_command_actions(void* argument)
295 {
296  if (NULL==argument || 0==strlen(argument)) {
297  printf("available actions (separated by space or ,):\n");
298  printf("[\tstart transaction: send reset pulse and detect slave presence\n");
299  printf("]\tend transaction%s\n", busvoodoo_onewire_power ? " and provide power on data line" : "");
300  printf("0\twrite decimal byte\n");
301  printf("0x0\twrite hexadecimal byte\n");
302  printf("0b0\twrite binary byte\n");
303  printf("\"a\"/'a'\twrite ASCII characters\n");
304  printf("r\tread byte\n");
305  printf("u/m\twait 1 us/ms\n");
306  printf(":n\trepeat action n times\n");
307  return;
308  }
309  // copy argument since it will be modified
310  char* copy = calloc(strlen(argument)+1, sizeof(char));
311  if (!copy) {
312  while (true);
313  }
314  strncpy(copy, argument, strlen(argument)+1);
315  // verify and perform actions
316  if (!busvoodoo_global_actions(copy, false, &busvoodoo_onewire_action)) { // verify actions
317  printf("malformed action(s)\n");
318  } else { // action are OK
319  busvoodoo_global_actions(argument, true, &busvoodoo_onewire_action); // perform action
320  }
321  free(copy); // release memory
322 }
323 
327 static void busvoodoo_onewire_rom_search(void* argument)
328 {
329  bool alarm = false; // if only ROMs with alarms should be searched
330  if (argument && 0==strcmp(argument, "alarm")) {
331  alarm = true;
332  }
334  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
335  }
336  if (!gpio_get(GPIO(ONEWIRE_MASTER_PORT), GPIO(ONEWIRE_MASTER_PIN))) {
337  printf("WARNING: the data line needs to be pulled up\n");
338  return;
339  }
340  bool presence = onewire_master_reset(); // send reset pulse and detect slave presence
341  printf("slave presence %sdetected\n", presence ? "" : "not ");
342  if (presence) { // only search if a slave presence has been detected
343  uint64_t code = 0; // code found
344  uint64_t codes = 0; // number of codes found
345  printf("searching ROM codes%s:\n", alarm ? " with alarm" : "");
346  bool next; // if another ROM code is detected
347  do { // search until all has been found
348  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show we are scanning
349  next = onewire_master_rom_search(&code, alarm); // search for the code
350  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
351  printf("not able to search for ROM codes when the data line is not pulled up\n");
352  break;
353  }
354  if (next) {
355  presence = onewire_master_reset(); // send reset pulse and detect slave presence for the next slave
356  }
357  printf("0x%016X\n", code);
358  codes++; // remember we found a code
359  } while (presence && next && !user_input_available);
360  printf("%U ROM code(s)%s found\n", codes, alarm ? " with alarm" : "");
361  if (user_input_available) { // user interrupted flow
362  user_input_get(); // discard user input
363  }
364  }
366  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
367  }
368 }
369 
372  {
373  .shortcut = 'a',
374  .name = "action",
375  .command_description = "perform protocol actions",
376  .argument = MENU_ARGUMENT_STRING,
377  .argument_description = "[actions]",
378  .command_handler = &busvoodoo_onewire_command_actions,
379  },
380  {
381  .shortcut = 's',
382  .name = "search",
383  .command_description = "perform ROM search",
384  .argument = MENU_ARGUMENT_STRING,
385  .argument_description = "[alarm]",
386  .command_handler = &busvoodoo_onewire_rom_search,
387  },
388 };
389 
391  .name = "1-wire",
392  .description = "1-Wire",
393  .full_only = false,
394  .setup = &busvoodoo_onewire_setup,
395  .commands = busvoodoo_onewire_commands,
396  .commands_nb = LENGTH(busvoodoo_onewire_commands),
397  .exit = &busvoodoo_onewire_exit,
398 };
command menu entry
Definition: menu.h:31
#define BUSVOODOO_LED_PULSE
recommended duration in ms for pulsing LEDs to show activity
static bool busvoodoo_onewire_action(const char *action, uint32_t repetition, bool perform)
perform 1-wire action
const struct busvoodoo_mode_t busvoodoo_onewire_mode
1-wire mode interface definition
BusVoodoo global definitions and methods (API)
static bool busvoodoo_onewire_embedded_pullup
if embedded pull-up resistors are used
#define ONEWIRE_MASTER_PORT
GPIO port.
void busvoodoo_oled_text_pinout(const char *pins[10], bool io_connector)
draw pin names on bottom (blue) part in display buffer
const char * name
name of the mode (i.e.
void busvoodoo_led_blue_pulse(uint32_t ms)
pulse blue LED for short duration
bool onewire_master_rom_search(uint64_t *code, bool alarm)
send SEARCH ROM command
#define ONEWIRE_MASTER_PIN
GPIO pin.
static void busvoodoo_onewire_rom_search(void *argument)
command to perform ROM search
void busvoodoo_oled_update(void)
update OLED display RAM with current display buffer
const char * busvoodoo_global_pinout_rscan[5]
RS/CAN connector pinout.
void onewire_master_setup(void)
setup 1-wire peripheral
global definitions and methods (API)
#define GPIO(x)
get GPIO based on GPIO identifier
Definition: global.h:103
void sleep_us(uint32_t duration)
go to sleep for some microseconds
Definition: global.c:102
bool busvoodoo_full
is the BusVoodoo board fully populated (with HV voltage regulator, RS-232, RS-485, CAN transceiver on the back side)
library for 1-wire protocol as master
bool onewire_master_write_byte(uint8_t data)
write data byte
void busvoodoo_led_blue_off(void)
switch off blue LED
busvoodoo_onewire_setting_t
mode setup stage
static void busvoodoo_onewire_exit(void)
exit 1-wire mode
static enum busvoodoo_onewire_setting_t busvoodoo_onewire_setting
current mode setup stage
char shortcut
short command code (0 if not available)
Definition: menu.h:32
const char * busvoodoo_global_pinout_io[10]
I/O connector pinout.
static void busvoodoo_onewire_command_actions(void *argument)
command to perform actions
char user_input_get(void)
get user input
Definition: global.c:145
char busvoodoo_global_string[64]
shared string buffer, i.e.
bool busvoodoo_global_actions(char *actions, bool perform, bool(*action_handler)(const char *action, uint32_t repetition, bool perform))
parse and perform actions
void sleep_ms(uint32_t duration)
go to sleep for some milliseconds
Definition: global.c:117
static bool busvoodoo_onewire_power
time (in ms) between slot to provide power
bool onewire_master_read_byte(uint8_t *data)
read data byte
void busvoodoo_oled_text_left(char *text)
draw mode text on top (yellow) left side in display buffer
BusVoodoo 1-wire mode (API)
float busvoodoo_embedded_pullup(bool on)
enable embedded pull-up resistors
static void busvoodoo_onewire_write(uint8_t value)
write to 1-wire
static void busvoodoo_onewire_read(void)
read from 1-wire
#define LENGTH(x)
get the length of an array
Definition: global.h:26
static bool busvoodoo_onewire_setup(char **prefix, const char *line)
setup 1-wire mode
void onewire_master_release(void)
release 1-wire peripheral
library to show BusVoodoo mode information on SSD1306 OLED display: name, activity, pinout (API)
volatile bool user_input_available
flag set when user input is available
Definition: global.c:36
static const struct menu_command_t busvoodoo_onewire_commands[]
1-wire menu commands
BusVoodoo mode interface.
bool onewire_master_reset(void)
send reset pulse