diff options
Diffstat (limited to 'src/drivers')
-rw-r--r-- | src/drivers/ao_hmc5883.c | 18 | ||||
-rw-r--r-- | src/drivers/ao_hmc5883.h | 2 | ||||
-rw-r--r-- | src/drivers/ao_mpu6000.c | 39 | ||||
-rw-r--r-- | src/drivers/ao_ms5607.c | 14 | ||||
-rw-r--r-- | src/drivers/ao_rn4678.c | 606 | ||||
-rw-r--r-- | src/drivers/ao_rn4678.h | 101 |
6 files changed, 748 insertions, 32 deletions
diff --git a/src/drivers/ao_hmc5883.c b/src/drivers/ao_hmc5883.c index f668fb66..c33aa536 100644 --- a/src/drivers/ao_hmc5883.c +++ b/src/drivers/ao_hmc5883.c @@ -126,13 +126,15 @@ struct ao_hmc5883_sample ao_hmc5883_current; static void ao_hmc5883(void) { + struct ao_hmc5883_sample sample; ao_hmc5883_setup(); for (;;) { - ao_hmc5883_sample(&ao_hmc5883_current); - ao_arch_critical( - AO_DATA_PRESENT(AO_DATA_HMC5883); - AO_DATA_WAIT(); - ); + ao_hmc5883_sample(&sample); + ao_arch_block_interrupts(); + ao_hmc5883_current = sample; + AO_DATA_PRESENT(AO_DATA_HMC5883); + AO_DATA_WAIT(); + ao_arch_release_interrupts(); } } @@ -141,10 +143,8 @@ static struct ao_task ao_hmc5883_task; static void ao_hmc5883_show(void) { - struct ao_data sample; - ao_data_get(&sample); - printf ("X: %d Y: %d Z: %d missed irq: %lu\n", - sample.hmc5883.x, sample.hmc5883.y, sample.hmc5883.z, ao_hmc5883_missed_irq); + printf ("X: %d Z: %d Y: %d missed irq: %lu\n", + ao_hmc5883_current.x, ao_hmc5883_current.z, ao_hmc5883_current.y, ao_hmc5883_missed_irq); } static const struct ao_cmds ao_hmc5883_cmds[] = { diff --git a/src/drivers/ao_hmc5883.h b/src/drivers/ao_hmc5883.h index 78637b02..b90733df 100644 --- a/src/drivers/ao_hmc5883.h +++ b/src/drivers/ao_hmc5883.h @@ -77,7 +77,7 @@ #define HMC5883_ID_C 12 struct ao_hmc5883_sample { - int16_t x, y, z; + int16_t x, z, y; }; extern struct ao_hmc5883_sample ao_hmc5883_current; diff --git a/src/drivers/ao_mpu6000.c b/src/drivers/ao_mpu6000.c index 650407ad..81d3c16c 100644 --- a/src/drivers/ao_mpu6000.c +++ b/src/drivers/ao_mpu6000.c @@ -192,7 +192,7 @@ _ao_mpu6000_setup(void) _ao_mpu6000_wait_alive(); /* Reset the whole chip */ - + _ao_mpu6000_reg_write(MPU6000_PWR_MGMT_1, (1 << MPU6000_PWR_MGMT_1_DEVICE_RESET)); @@ -292,7 +292,7 @@ _ao_mpu6000_setup(void) ao_delay(AO_MS_TO_TICKS(200)); _ao_mpu6000_sample(&normal_mode); - + errors += ao_mpu6000_accel_check(normal_mode.accel_x, test_mode.accel_x); errors += ao_mpu6000_accel_check(normal_mode.accel_y, test_mode.accel_y); errors += ao_mpu6000_accel_check(normal_mode.accel_z, test_mode.accel_z); @@ -315,7 +315,7 @@ _ao_mpu6000_setup(void) /* Set sample rate divider to sample at 200Hz (v = gyro/rate - 1) */ _ao_mpu6000_reg_write(MPU6000_SMPRT_DIV, 1000 / 200 - 1); - + ao_delay(AO_MS_TO_TICKS(100)); ao_mpu6000_configured = 1; } @@ -325,6 +325,7 @@ struct ao_mpu6000_sample ao_mpu6000_current; static void ao_mpu6000(void) { + struct ao_mpu6000_sample sample; /* ao_mpu6000_init already grabbed the SPI bus and mutex */ _ao_mpu6000_setup(); #if AO_MPU6000_SPI @@ -335,14 +336,15 @@ ao_mpu6000(void) #if AO_MPU6000_SPI ao_mpu6000_spi_get(); #endif - _ao_mpu6000_sample(&ao_mpu6000_current); + _ao_mpu6000_sample(&sample); #if AO_MPU6000_SPI ao_mpu6000_spi_put(); -#endif - ao_arch_critical( - AO_DATA_PRESENT(AO_DATA_MPU6000); - AO_DATA_WAIT(); - ); +#endif + ao_arch_block_interrupts(); + ao_mpu6000_current = sample; + AO_DATA_PRESENT(AO_DATA_MPU6000); + AO_DATA_WAIT(); + ao_arch_release_interrupts(); } } @@ -351,16 +353,13 @@ static struct ao_task ao_mpu6000_task; static void ao_mpu6000_show(void) { - struct ao_data sample; - - ao_data_get(&sample); printf ("Accel: %7d %7d %7d Gyro: %7d %7d %7d\n", - sample.mpu6000.accel_x, - sample.mpu6000.accel_y, - sample.mpu6000.accel_z, - sample.mpu6000.gyro_x, - sample.mpu6000.gyro_y, - sample.mpu6000.gyro_z); + ao_mpu6000_current.accel_x, + ao_mpu6000_current.accel_y, + ao_mpu6000_current.accel_z, + ao_mpu6000_current.gyro_x, + ao_mpu6000_current.gyro_y, + ao_mpu6000_current.gyro_z); } static const struct ao_cmds ao_mpu6000_cmds[] = { @@ -374,7 +373,7 @@ ao_mpu6000_init(void) ao_mpu6000_configured = 0; ao_add_task(&ao_mpu6000_task, ao_mpu6000, "mpu6000"); - + #if AO_MPU6000_SPI ao_spi_init_cs(AO_MPU6000_SPI_CS_PORT, (1 << AO_MPU6000_SPI_CS_PIN)); @@ -386,7 +385,7 @@ ao_mpu6000_init(void) ao_cur_task = &ao_mpu6000_task; ao_spi_get(AO_MPU6000_SPI_BUS, AO_SPI_SPEED_1MHz); ao_cur_task = NULL; -#endif +#endif ao_cmd_register(&ao_mpu6000_cmds[0]); } diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c index 261df67f..914e0c1b 100644 --- a/src/drivers/ao_ms5607.c +++ b/src/drivers/ao_ms5607.c @@ -191,17 +191,23 @@ ao_ms5607_sample(__xdata struct ao_ms5607_sample *sample) #include "ao_ms5607_convert.c" #endif -#if HAS_TASK +#ifndef HAS_MS5607_TASK +#define HAS_MS5607_TASK HAS_TASK +#endif + __xdata struct ao_ms5607_sample ao_ms5607_current; +#if HAS_MS5607_TASK static void ao_ms5607(void) { + struct ao_ms5607_sample sample; ao_ms5607_setup(); for (;;) { - ao_ms5607_sample(&ao_ms5607_current); + ao_ms5607_sample(&sample); ao_arch_block_interrupts(); + ao_ms5607_current = sample; AO_DATA_PRESENT(AO_DATA_MS5607); AO_DATA_WAIT(); ao_arch_release_interrupts(); @@ -209,7 +215,9 @@ ao_ms5607(void) } __xdata struct ao_task ao_ms5607_task; +#endif +#if HAS_TASK void ao_ms5607_info(void) { @@ -248,6 +256,8 @@ ao_ms5607_init(void) #if HAS_TASK ao_cmd_register(&ao_ms5607_cmds[0]); +#endif +#if HAS_MS5607_TASK ao_add_task(&ao_ms5607_task, ao_ms5607, "ms5607"); #endif diff --git a/src/drivers/ao_rn4678.c b/src/drivers/ao_rn4678.c new file mode 100644 index 00000000..98dc35b5 --- /dev/null +++ b/src/drivers/ao_rn4678.c @@ -0,0 +1,606 @@ +/* + * Copyright © 2017 Keith Packard <keithp@keithp.com> + * + * 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 2 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. + */ + +#include <ao.h> +#include <ao_rn4678.h> +#include <ao_exti.h> +#include <stdarg.h> + +static uint8_t ao_rn_connected; + +#define AO_RN_DEBUG 0 + +#if AO_RN_DEBUG +static void ao_rn_dbg(char *format, ...) { + va_list a; + uint32_t irq = ao_arch_irqsave(); + ao_arch_release_interrupts(); + va_start(a, format); + vprintf(format, a); + va_end(a); + flush(); + ao_arch_irqrestore(irq); +} + +static char ao_rn_dir; + +static void +ao_rn_log_char(char c, char dir) +{ + if (dir != ao_rn_dir) { + putchar(dir); putchar('\n'); + ao_rn_dir = dir; + } + switch (c) { + case '\r': + putchar('\\'); putchar('r'); + break; + case '\n': + putchar('\\'); putchar('n'); + break; + default: + putchar(c); + } + flush(); +} + +static void +ao_rn_log_out_char(char c) +{ + ao_rn_log_char(c, '}'); +} + +static void +ao_rn_log_in_char(char c) +{ + ao_rn_log_char(c, '{'); +} + +static inline void +ao_rn_putchar(char c) +{ + ao_rn_log_out_char(c); + ao_serial_rn_putchar(c); +} + +static inline int +_ao_rn_pollchar(void) +{ + int c = _ao_serial_rn_pollchar(); + + if (c != AO_READ_AGAIN) { + ao_arch_release_interrupts(); + ao_rn_log_in_char((char) c); + ao_arch_block_interrupts(); + } + return c; +} +#else +#define ao_rn_dbg(fmt, ...) +#define ao_rn_putchar(c) ao_serial_rn_putchar(c) +#define _ao_rn_pollchar() _ao_serial_rn_pollchar() +#endif + +/* For stdio, this skips all status messages *sigh* */ + +#define STATUS_CHAR '%' + +static const char *status_strings[] = { + "RFCOMM_CLOSE", + "RFCOMM_OPEN", + "CONNECT", + "LCONNECT", + "DISCONN", + "BONDED", +}; + +#define NUM_STATUS_STRING (sizeof status_strings/sizeof status_strings[0]) + +static char ao_rn_buffer[64]; +static int ao_rn_buf_cnt, ao_rn_buf_ptr; +static int ao_rn_draining; +static AO_TICK_TYPE ao_rn_buf_time; + +/* Well, this is annoying. The status strings from the RN4678 can't be + * disabled due to a firmware bug. So, this code finds those in the + * input and strips them out. + */ +int +_ao_wrap_rn_pollchar(void) +{ + int c = AO_READ_AGAIN; + unsigned i; + int done = 0; + + while (!done && !ao_rn_draining) { + c = _ao_serial_rn_pollchar(); + + if (c == AO_READ_AGAIN) { + if (ao_rn_buf_cnt && (ao_time() - ao_rn_buf_time) > AO_MS_TO_TICKS(1000)) { + ao_rn_draining = 1; + continue; + } + return AO_READ_AGAIN; + } + + if (ao_rn_buf_cnt) { + /* buffering chars */ + + if (c == STATUS_CHAR) { + /* End of status string, drop it and carry on */ + ao_rn_buffer[ao_rn_buf_cnt] = '\0'; +// ao_rn_dbg("discard %s\n", ao_rn_buffer); + ao_rn_buf_cnt = 0; + } else if (ao_rn_buf_cnt == sizeof(ao_rn_buffer)) { + /* If we filled the buffer, just give up */ + ao_rn_draining = 1; + } else { + ao_rn_buffer[ao_rn_buf_cnt++] = c; + for (i = 0; i < NUM_STATUS_STRING; i++) { + int cmp = strlen(status_strings[i]); + if (cmp >= ao_rn_buf_cnt) + cmp = ao_rn_buf_cnt-1; + if (memcmp(ao_rn_buffer+1, status_strings[i], cmp) == 0) + break; + } + if (i == NUM_STATUS_STRING) + ao_rn_draining = 1; + } + } else if (c == STATUS_CHAR) { + ao_rn_buffer[0] = c; + ao_rn_buf_cnt = 1; + ao_rn_buf_ptr = 0; + ao_rn_buf_time = ao_time(); + } else + done = 1; + } + if (ao_rn_draining) { + c = ao_rn_buffer[ao_rn_buf_ptr++] & 0xff; + if (ao_rn_buf_ptr == ao_rn_buf_cnt) { + ao_rn_buf_ptr = ao_rn_buf_cnt = 0; + ao_rn_draining = 0; + } + } + return c; +} + +static void +ao_rn_puts(char *s) +{ + char c; + + while ((c = *s++)) + ao_rn_putchar(c); +} + +static void +ao_rn_drain(void) +{ + int timeout = 0; + +// ao_rn_dbg("drain...\n"); + ao_serial_rn_drain(); + while (!timeout) { + ao_arch_block_interrupts(); + while (_ao_rn_pollchar() == AO_READ_AGAIN) { + if (_ao_serial_rn_sleep_for(AO_MS_TO_TICKS(10))) { + timeout = 1; + break; + } + } + ao_arch_release_interrupts(); + } +// ao_rn_dbg("drain done\n"); +} + +static void +ao_rn_send_cmd(char *cmd, char *param) +{ +// ao_rn_dbg("send_cmd %s%s\n", cmd, param ? param : ""); + ao_rn_drain(); + ao_rn_puts(cmd); + if (param) + ao_rn_puts(param); + ao_rn_putchar('\r'); +} + +static int +ao_rn_wait_char(AO_TICK_TYPE giveup_time) +{ + int c; + + ao_arch_block_interrupts(); + while ((c = _ao_rn_pollchar()) == AO_READ_AGAIN) { + AO_TICK_SIGNED delay = (AO_TICK_SIGNED) (giveup_time - ao_time()); + if (delay < 0) { + ao_arch_release_interrupts(); + return AO_READ_AGAIN; + } + _ao_serial_rn_sleep_for(delay); + } + ao_arch_release_interrupts(); + return c; +} + +static int +ao_rn_wait_for(int timeout, char *match) +{ + char reply[AO_RN_MAX_REPLY_LEN + 1]; + int match_len = strlen(match); + AO_TICK_TYPE giveup_time = ao_time() + timeout; + int c; + +// ao_rn_dbg("wait for %d, \"%s\"\n", timeout, match); + memset(reply, ' ', sizeof(reply)); + while (memcmp(reply, match, match_len) != 0) { + c = ao_rn_wait_char(giveup_time); + if (c == AO_READ_AGAIN) { +// ao_rn_dbg("\twait for timeout\n"); + return AO_RN_TIMEOUT; + } + reply[match_len] = (char) c; + memmove(reply, reply+1, match_len); + reply[match_len] = '\0'; +// ao_rn_dbg("\tmatch now \"%s\"\n", reply); + } +// ao_rn_dbg("\twait for ok\n"); + return AO_RN_OK; +} + +static int +ao_rn_wait_line(AO_TICK_TYPE giveup_time, char *line, int len) +{ + char *l = line; + +// ao_rn_dbg("wait line\n"); + for (;;) { + int c = ao_rn_wait_char(giveup_time); + + /* timeout */ + if (c == AO_READ_AGAIN) { +// ao_rn_dbg("\twait line timeout\n"); + return AO_RN_TIMEOUT; + } + + /* done */ + if (c == '\r') { + *l = '\0'; +// ao_rn_dbg("\twait line \"%s\"\n", line); + return AO_RN_OK; + } + + if (c == '\n') + continue; + + /* buffer overrun */ + if (len <= 1) + return AO_RN_ERROR; + + *l++ = (char) c; + len--; + } +} + +static int +ao_rn_wait_status(void) +{ + char message[AO_RN_MAX_REPLY_LEN]; + AO_TICK_TYPE giveup_time = ao_time() + AO_RN_CMD_TIMEOUT; + int status; + +// ao_rn_dbg("wait status\n"); + status = ao_rn_wait_line(giveup_time, message, sizeof (message)); + if (status == AO_RN_OK) + if (strncmp(message, "AOK", 3) != 0) + status = AO_RN_ERROR; + return status; +} + +static int +ao_rn_set_name(void) +{ + char sn[8]; + char *s = sn + 8; + int n; + +// ao_rn_dbg("set name...\n"); + *--s = '\0'; + n = ao_serial_number; + do { + *--s = '0' + n % 10; + } while (n /= 10); + ao_rn_send_cmd(AO_RN_SET_NAME_CMD "TeleBT-", s); + return ao_rn_wait_status(); +} + +static int +ao_rn_get_name(char *name, int len) +{ +// ao_rn_dbg("get name...\n"); + ao_rn_send_cmd(AO_RN_GET_NAME_CMD, NULL); + return ao_rn_wait_line(ao_time() + AO_RN_CMD_TIMEOUT, name, len); +} + +static void +ao_rn_check_link(void) +{ + ao_rn_connected = 1 - ao_gpio_get(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN, foo); +} + +static void +ao_rn_isr(void) +{ + ao_rn_check_link(); + ao_wakeup(&ao_rn_connected); +} + +static void +ao_bt_panic(int where) +{ + int i; + for (;;) { + for (i = 0; i < 50; i++) { + ao_led_toggle(AO_BT_LED); + ao_delay(AO_MS_TO_TICKS(10)); + } + ao_led_off(AO_BT_LED); + ao_delay(AO_MS_TO_TICKS(500)); + for (i = 0; i < where; i++) { + ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(200)); + ao_delay(AO_MS_TO_TICKS(200)); + } + } +} + +static uint8_t ao_rn_stdio; + +/* + * Set the stdio echo for the bluetooth link + */ +void +ao_rn_echo(uint8_t echo) +{ + ao_stdios[ao_rn_stdio].echo = echo; +} + +static void +ao_rn(void) +{ + int status = AO_RN_ERROR; + char name[17]; + int i; + + ao_rn_dbg("ao_rn top\n"); + + /* Select CMD mode after the device gets out of reset */ + ao_gpio_set(AO_RN_CMD_PORT, AO_RN_CMD_PIN, foo, AO_RN_CMD_CMD); + + for (i = 0; i < 3; i++) { + ao_rn_dbg("reset device\n"); + + ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 0); + ao_delay(AO_MS_TO_TICKS(100)); + + /* Reboot the RN4678 and wait for it to start talking */ + ao_rn_drain(); + ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 1); + status = ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, AO_RN_REBOOT_MSG); + if (status != AO_RN_OK) { + ao_rn_dbg("reboot failed\n"); + continue; + } + + /* After it reboots, it can take a moment before it responds + * to commands + */ + status = ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, "CMD> "); + + if (status == AO_RN_TIMEOUT) { + ao_rn_puts("$$$"); + (void) ao_rn_wait_for(AO_RN_REBOOT_TIMEOUT, "CMD> "); + } + + ao_rn_send_cmd(AO_RN_VERSION_CMD, NULL); + (void) ao_rn_wait_status(); + + /* Check to see if the name is already set and assume + * that the device is ready to go + */ + status = ao_rn_get_name(name, sizeof (name)); + if (status != AO_RN_OK) { + ao_rn_dbg("get name failed\n"); + status = ao_rn_get_name(name, sizeof (name)); + if (status != AO_RN_OK) + continue; + } + + if (strncmp(name, "TeleBT-", 7) == 0) { + ao_rn_dbg("name is set\n"); + status = AO_RN_OK; + break; + } + + /* Make the command pin control command/data mode */ + ao_rn_send_cmd(AO_RN_SET_COMMAND_PIN, NULL); + if (ao_rn_wait_status() != AO_RN_OK) { + ao_rn_dbg("set command pin failed\n"); + continue; + } + + ao_rn_send_cmd(AO_RN_SET_STATUS_STRING, AO_RN_STATUS_STRING_ENABLE); + if (ao_rn_wait_status() != AO_RN_OK) { + ao_rn_dbg("set status string\n"); + continue; + } + + /* Select 'fast' mode to ignore command sequence (more or less) */ + ao_rn_send_cmd(AO_RN_SET_FAST_MODE, NULL); + if (ao_rn_wait_status() != AO_RN_OK) { + ao_rn_dbg("set fast mode failed\n"); + continue; + } + + /* Finally, set the name. Doing this last makes it possible to check + * if the whole sequence has been done + */ + if (ao_rn_set_name() != AO_RN_OK) { + ao_rn_dbg("set name failed\n"); + continue; + } + + /* After we've configured the device, go back around and reboot it + * as that's how we get the new configuration to take effect + */ + } + ao_rn_dbg("ao_rn status %d\n", status); + + if (status != AO_RN_OK) + ao_bt_panic(4); + + ao_gpio_set(AO_RN_CMD_PORT, AO_RN_CMD_PIN, foo, AO_RN_CMD_DATA); + + /* Wait for the hardware to finish sending messages, then clear the queue */ + ao_delay(AO_MS_TO_TICKS(200)); + ao_rn_drain(); + + ao_exti_enable(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN); + +#if AO_RN_DEBUG + + /* + * Separate debug code when things aren't working. Just dump + * inbound bluetooth characters to stdout + */ + for (;;) { + int c; + + ao_arch_block_interrupts(); + while ((c = _ao_rn_pollchar()) == AO_READ_AGAIN) + ao_sleep(&ao_serial_rn_rx_fifo); + ao_arch_release_interrupts(); + } +#else + ao_rn_stdio = ao_add_stdio(_ao_wrap_rn_pollchar, + ao_serial_rn_putchar, + NULL); + + ao_rn_echo(0); + ao_rn_check_link(); + /* + * Now just hang around and flash the blue LED when we've got + * a connection + */ + for (;;) { + ao_arch_block_interrupts(); + while (!ao_rn_connected) + ao_sleep(&ao_rn_connected); + ao_arch_release_interrupts(); + while (ao_rn_connected) { + ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(20)); + if (ao_rn_buf_cnt != 0) + ao_wakeup(&ao_stdin_ready); + ao_delay(AO_SEC_TO_TICKS(3)); + } + } +#endif +} + +static struct ao_task ao_rn_task; + +static void +ao_rn_factory(void) +{ + int i; + int v = 0; + + /* + * Factory reset. Flip pin P3_1 5 times within the first five + * seconds of power-on + */ + + /* Select our target output pin */ + ao_enable_output(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, foo, v); + + /* Turn off the BT device using the SW_BTN pin */ + printf("Power down BT\n"); flush(); + ao_gpio_set(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, foo, 0); + ao_delay(AO_MS_TO_TICKS(1000)); + + /* And turn it back on */ + printf("Power up BT\n"); flush(); + ao_gpio_set(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, foo, 1); + + /* Right after power on, poke P3_1 five times to force a + * factory reset + */ + for (i = 0; i < 20; i++) { + v = 1-v; + ao_delay(AO_MS_TO_TICKS(50)); + ao_gpio_set(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, foo, v); + ao_led_toggle(AO_BT_LED); + } + + /* And let P3_1 float again */ + ao_enable_input(AO_RN_P3_1_PORT, AO_RN_P3_1_PIN, AO_EXTI_MODE_PULL_NONE); + + printf("Reboot BT\n"); flush(); + ao_delay(AO_MS_TO_TICKS(100)); + ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 0); + ao_delay(AO_MS_TO_TICKS(100)); + ao_gpio_set(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 1); +} + +#if AO_RN_DEBUG +static void +ao_rn_send(void) +{ + int c; + + while ((c = getchar()) != '~') + ao_rn_putchar(c); +} +#endif + +static const struct ao_cmds rn_cmds[] = { + { ao_rn_factory, "F\0Factory reset rn4678" }, +#if AO_RN_DEBUG + { ao_rn_send, "B\0Send data to rn4678. End with ~" }, +#endif + { 0 }, +}; + +void +ao_rn4678_init(void) +{ + (void) ao_rn_set_name; + + ao_serial_rn_set_speed(AO_SERIAL_SPEED_115200); + + /* Reset line */ + ao_enable_output(AO_RN_RST_N_PORT, AO_RN_RST_N_PIN, foo, 0); + + /* SW_BTN */ + ao_enable_output(AO_RN_SW_BTN_PORT, AO_RN_SW_BTN_PIN, foo, 1); + + /* P3_7 command/data selector */ + ao_enable_output(AO_RN_CMD_PORT, AO_RN_CMD_PIN, foo, AO_RN_CMD_CMD); + + ao_enable_input(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN, AO_EXTI_MODE_PULL_NONE); + ao_exti_setup(AO_RN_CONNECTED_PORT, AO_RN_CONNECTED_PIN, + AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_LOW, + ao_rn_isr); + + ao_cmd_register(rn_cmds); + ao_add_task(&ao_rn_task, ao_rn, "bluetooth"); +} diff --git a/src/drivers/ao_rn4678.h b/src/drivers/ao_rn4678.h new file mode 100644 index 00000000..a4dcea38 --- /dev/null +++ b/src/drivers/ao_rn4678.h @@ -0,0 +1,101 @@ +/* + * Copyright © 2017 Keith Packard <keithp@keithp.com> + * + * 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 2 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. + */ + +#ifndef _AO_RN4678_H_ +#define _AO_RN4678_H_ + +/* From the rn4678 pictail board + + 1 SW_BTN 0-off/1-on 2 P2_0 1-app/0-test (boot time) + 3 P2_4 1-app/0-WF 4 EAN 0-app/1-WF (reboots) + 5 6 RTS 0 + 7 8 P3_2 1 (NC) + 9 TXD 10 P3_3 1 (NC) + 11 RXD 12 P3_4 1 (NC) + 13 14 + 15 16 P3_7 1 (NC) + 17 P0_5 1 (NC) 18 RST_N 1 run/0 reset + 19 WAKE_UP 1 run/0 btn 20 + 21 P0_4 0 (NC) 22 + 23 P1_5 1 (NC) 24 P3_1 1 (NC) + 25 CTS 0 26 3.3V + 27 28 GND + + + Interesting pins: + + Not connected to microcontroller: + + SW_BTN 0-off 1-on + P2_4 0 write-flash 1-app + WAKE_UP 0-stop 1-run + P2_0 0 test 1-run + EAN 1-WF (reboots) 0-run + RST_N 0 reset 1 run + + Connected to microcontroller: + + CTS mostly 0 + RTS mostly 1 + + TXD + RXD + + Other connections -- LDO33_O to VDD_IO + +*/ + +#define AO_RN_REBOOT_MSG "REBOOT" + +#define AO_RN_CMD_TIMEOUT AO_MS_TO_TICKS(200) + +#define AO_RN_REBOOT_TIMEOUT AO_MS_TO_TICKS(2000) + +#define AO_RN_MAX_REPLY_LEN 10 + +#define AO_RN_SET_NAME_CMD "SN," +#define AO_RN_GET_NAME_CMD "GN" + +#define AO_RN_SET_STATUS_STRING "so," +#define AO_RN_STATUS_STRING_DISABLE " " +#define AO_RN_STATUS_STRING_ENABLE "%,%" + +#define AO_RN_REBOOT_CMD "R,1" + +#define AO_RN_VERSION_CMD "V" + +#define AO_RN_TIMEOUT -1 +#define AO_RN_ERROR 0 +#define AO_RN_OK 1 + +#define AO_RN_SET_COMMAND_PIN "SX,07,0B" + +#define AO_RN_SET_AUTH_JUST_WORKS "SA,2" +#define AO_RN_SET_FAST_MODE "SQ,9000" + +/* This pin is configured to control cmd/data mode */ +#define AO_RN_CMD_PORT AO_RN_P3_7_PORT +#define AO_RN_CMD_PIN AO_RN_P3_7_PIN + +#define AO_RN_CMD_CMD 0 +#define AO_RN_CMD_DATA 1 + +/* This pin indicates BT connection status */ +#define AO_RN_CONNECTED_PORT AO_RN_P1_5_PORT +#define AO_RN_CONNECTED_PIN AO_RN_P1_5_PIN + +void +ao_rn4678_init(void); + +#endif /* _AO_RN_H_ */ |