Bluetooth Low Energy and Stepper Motor enabled device, compatible with the BlueST Protocol.
Dependencies: X_NUCLEO_IHM01A1
Bluetooth Low Energy and Stepper Motor enabled device, compatible with the BlueST Protocol.
Diff: source/main.cpp
- Revision:
- 0:f87cdc08dcd1
- Child:
- 1:84935552ca4d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/main.cpp Tue Apr 17 14:59:28 2018 +0000 @@ -0,0 +1,338 @@ +/** + ******************************************************************************* + * @file main.cpp + * @author Davide Aliprandi, STMicroelectronics + * @version V1.0.0 + * @date October 31st, 2017 + * @brief mbed test application for the STMicroelectronics X-NUCLEO-IHM01A1 + * Motor Control Expansion Board and the X-NUCLEO-IDB05A1 Bluetooth + * Low energy Expansion Board. + ******************************************************************************* + * @attention + * + * <h2><center>© COPYRIGHT(c) 2018 STMicroelectronics</center></h2> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************* + */ + + +/* Includes ------------------------------------------------------------------*/ + +/* mbed specific header files. */ +#include <events/mbed_events.h> +#include <mbed.h> +#include "ble/BLE.h" +#include "CustomService.h" + +/* Helper header files. */ +#include "DevSPI.h" + +/* Component specific header files. */ +#include "L6474.h" + + +/* Definitions ---------------------------------------------------------------*/ + +#define BLE_ADVERTISING_INTERVAL_ms 1000 +#define DELAY_1 100 + + +/* Variables -----------------------------------------------------------------*/ + +/* Blinky LED to indicate system aliveness. */ +//DigitalOut alivenessLED(LED1, 0); //Conflicts with SPI CLK on D13 + +/* Bluetooth. */ +const static char DEVICE_NAME[] = "SHUTTER_DEVICE"; +const static uint8_t MANUFACTURER_SPECIFIC_DATA[]= {0x01,0x80,0x00,0x00,0x20,0x00}; +static EventQueue event_queue(/* event count */ 10 * EVENTS_EVENT_SIZE); +CustomService *custom_service; + +/* Initialization parameters. */ +L6474_init_t init = { + 160, /* Acceleration rate in pps^2. Range: (0..+inf). */ + 160, /* Deceleration rate in pps^2. Range: (0..+inf). */ + 1600, /* Maximum speed in pps. Range: (30..10000]. */ + 800, /* Minimum speed in pps. Range: [30..10000). */ + 250, /* Torque regulation current in mA. Range: 31.25mA to 4000mA. */ + L6474_OCD_TH_750mA, /* Overcurrent threshold (OCD_TH register). */ + L6474_CONFIG_OC_SD_ENABLE, /* Overcurrent shutwdown (OC_SD field of CONFIG register). */ + L6474_CONFIG_EN_TQREG_TVAL_USED, /* Torque regulation method (EN_TQREG field of CONFIG register). */ + L6474_STEP_SEL_1_8, /* Step selection (STEP_SEL field of STEP_MODE register). */ + L6474_SYNC_SEL_1_2, /* Sync selection (SYNC_SEL field of STEP_MODE register). */ + L6474_FAST_STEP_12us, /* Fall time value (T_FAST field of T_FAST register). Range: 2us to 32us. */ + L6474_TOFF_FAST_8us, /* Maximum fast decay time (T_OFF field of T_FAST register). Range: 2us to 32us. */ + 3, /* Minimum ON time in us (TON_MIN register). Range: 0.5us to 64us. */ + 21, /* Minimum OFF time in us (TOFF_MIN register). Range: 0.5us to 64us. */ + L6474_CONFIG_TOFF_044us, /* Target Swicthing Period (field TOFF of CONFIG register). */ + L6474_CONFIG_SR_320V_us, /* Slew rate (POW_SR field of CONFIG register). */ + L6474_CONFIG_INT_16MHZ, /* Clock setting (OSC_CLK_SEL field of CONFIG register). */ + L6474_ALARM_EN_OVERCURRENT | + L6474_ALARM_EN_THERMAL_SHUTDOWN | + L6474_ALARM_EN_THERMAL_WARNING | + L6474_ALARM_EN_UNDERVOLTAGE | + L6474_ALARM_EN_SW_TURN_ON | + L6474_ALARM_EN_WRONG_NPERF_CMD /* Alarm (ALARM_EN register). */ +}; + +/* Motor Control Component. */ +L6474 *motor; + +/* Button event. */ +InterruptIn event(USER_BUTTON); + + +/* Bluetooth related functions -----------------------------------------------*/ + +void on_disconnection_callback(const Gap::DisconnectionCallbackParams_t *params) +{ + (void) params; + BLE::Instance().gap().startAdvertising(); +} + +void aliveness_callback(void) +{ + //alivenessLED = !alivenessLED; /* Do blinky to indicate system aliveness. */ +} + +/** + * This callback allows the custom service to read updates from the + * characteristic. + * + * @param[in] params + * Information about the characterisitc being updated. + */ +void on_data_read_callback(const GattReadCallbackParams *params) { + static uint16_t time_stamp = 0; + + if (params->handle == custom_service->getValueHandle()) { + /* Getting motor's state. */ + uint8_t state = (uint8_t) (motor->get_device_state() == INACTIVE ? CustomService::MOTOR_INACTIVE : CustomService::MOTOR_RUNNING); + + /* Sending data via bluetooth. */ + if (BLE::Instance().getGapState().connected) { + event_queue.call(Callback<void(uint16_t, uint8_t)>(custom_service, &CustomService::send_state), time_stamp++, state); + } + } +} + +/** + * This callback allows the custom service to write updates to the + * characteristic. + * + * @param[in] params + * Information about the characterisitc being updated. + */ +void on_data_written_callback(const GattWriteCallbackParams *params) { + uint32_t steps; + + if (params->handle == custom_service->getValueHandle()) { + //printf("--> COMMAND: %d\r\n", ((uint8_t *) (params->data))[0]); + switch ((CustomService::motor_command_t) ((uint8_t *) (params->data))[0]) + { + //Stops running with HiZ + case CustomService::MOTOR_STOP_RUNNING_WITHOUT_TORQUE: + motor->hard_hiz(); + motor->wait_while_active(); + break; + //Stops running with torque applied + case CustomService::MOTOR_STOP_RUNNING_WITH_TORQUE: + motor->hard_stop(); + motor->enable(); + motor->wait_while_active(); + break; + //Runs forward indefinitely + case CustomService::MOTOR_RUN_FORWARD: + motor->run(StepperMotor::FWD); + break; + //Runs backward indefinitely + case CustomService::MOTOR_RUN_BACKWARD: + motor->run(StepperMotor::BWD); + break; + //Moves steps forward + case CustomService::MOTOR_MOVE_STEPS_FORWARD: + steps = ((((uint8_t *) (params->data))[1]) | + (((uint8_t *) (params->data))[2] << 8 ) | + (((uint8_t *) (params->data))[3] << 16) | + (((uint8_t *) (params->data))[4] << 24)); + motor->move(StepperMotor::FWD, steps); + motor->wait_while_active(); + //printf("--> %d %d %d\r\n", params->len, ((uint8_t *) (params->data))[0], steps); + break; + //Moves steps backward + case CustomService::MOTOR_MOVE_STEPS_BACKWARD: + steps = ((((uint8_t *) (params->data))[1]) | + (((uint8_t *) (params->data))[2] << 8 ) | + (((uint8_t *) (params->data))[3] << 16) | + (((uint8_t *) (params->data))[4] << 24)); + motor->move(StepperMotor::BWD, steps); + motor->wait_while_active(); + //printf("--> %d %d %d\r\n", params->len, ((uint8_t *) (params->data))[0], steps); + break; + //Other + default: + break; + } + } +} + +/** + * This function is called when the ble initialization process has failled. + */ +void on_ble_init_error_callback(BLE &ble, ble_error_t error) +{ + /* Initialization error handling should go here */ +} + +/** + * Callback triggered when the ble initialization process has finished. + */ +void ble_init_complete(BLE::InitializationCompleteCallbackContext *params) +{ + BLE& ble = params->ble; + ble_error_t error = params->error; + + if (error != BLE_ERROR_NONE) { + /* In case of error, forward the error handling to on_ble_init_error_callback */ + on_ble_init_error_callback(ble, error); + return; + } + + /* Ensure that it is the default instance of BLE */ + if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { + return; + } + + ble.gap().onDisconnection(on_disconnection_callback); + ble.gattServer().onDataWritten(on_data_written_callback); + ble.gattServer().onDataRead(on_data_read_callback); + + custom_service = new CustomService(ble); + + /* Setup advertising data. */ + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t *) CUSTOM_STEPPER_MOTOR_SERVICE_UUID, sizeof(CUSTOM_STEPPER_MOTOR_SERVICE_UUID)); + ble.gap().accumulateScanResponse(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, MANUFACTURER_SPECIFIC_DATA, sizeof(MANUFACTURER_SPECIFIC_DATA)); + ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, (const uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME)); + ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + ble.gap().setAdvertisingInterval(BLE_ADVERTISING_INTERVAL_ms); + ble.gap().startAdvertising(); +} + +void schedule_ble_events_processing(BLE::OnEventsToProcessCallbackContext* context) { + BLE &ble = BLE::Instance(); + event_queue.call(Callback<void()>(&ble, &BLE::processEvents)); +} + + +/* Custom service related functions ------------------------------------------*/ + +/** + * @brief This is an example of user handler for the flag interrupt. + * @param None + * @retval None + * @note If needed, implement it, and then attach and enable it: + * + motor->attach_flag_irq(&flag_irq_handler); + * + motor->enable_flag_irq(); + * To disable it: + * + motor->disble_flag_irq(); + */ +void flag_irq_handler(void) +{ + /* Set ISR flag. */ + motor->isr_flag = TRUE; + + /* Get the value of the status register. */ + unsigned int status = motor->get_status(); + + /* Check NOTPERF_CMD flag: if set, the command received by SPI can't be performed. */ + /* This often occures when a command is sent to the L6474 while it is not in HiZ state. */ + if ((status & L6474_STATUS_NOTPERF_CMD) == L6474_STATUS_NOTPERF_CMD) { + printf(" WARNING: \"FLAG\" interrupt triggered. Non-performable command detected when updating L6474's registers while not in HiZ state.\r\n"); + } + + /* Reset ISR flag. */ + motor->isr_flag = FALSE; +} + + +/* Motor related functions ------------------------------------------*/ + +void motor_run_callback(void) +{ + motor->run(StepperMotor::FWD); +} + +void motor_stop_callback(void) +{ + motor->hard_hiz(); + motor->wait_while_active(); +} + + +/* Main function -------------------------------------------------------------*/ + +int main() +{ + /*----- Initialization. -----*/ + + /* Printing to the console. */ + printf("Motor Node Application Example\r\n\n"); + + /* Aliveness callback. */ + //event_queue.call_every(500, aliveness_callback); + + /* Bluetooth. */ + BLE &ble = BLE::Instance(); + ble.onEventsToProcess(schedule_ble_events_processing); + ble.init(ble_init_complete); + + /* Initializing SPI bus. */ + DevSPI dev_spi(D11, D12, D13); + + /* Initializing Motor Control Component. */ + /* D7 conflicts with BNRG_RST (when motor changes direction 0-1 BLE resets). */ + /* Used Morpho PB_2 and made HW wiring. */ + motor = new L6474(D2, D8, PB_2, D9, D10, dev_spi); + if (motor->init(&init) != COMPONENT_OK) { + exit(EXIT_FAILURE); + } + + /* Attaching and enabling interrupt handlers. */ + motor->attach_flag_irq(&flag_irq_handler); + motor->enable_flag_irq(); + event.fall(motor_run_callback); + event.rise(motor_stop_callback); + + /* Stopping with HiZ. */ + motor->hard_hiz(); + motor->wait_while_active(); + + /* Start. */ + event_queue.dispatch_forever(); + + return 0; +}