diff --git a/Configuration.h b/Configuration.h index 050be90..9e1d746 100644 --- a/Configuration.h +++ b/Configuration.h @@ -24,8 +24,8 @@ Licence: GPL #define CONFIGURATION_H #define NAME "RepRapFirmware" -#define VERSION "0.57n-dc42" -#define DATE "2014-02-18" +#define VERSION "0.57o-dc42" +#define DATE "2014-02-24" #define LAST_AUTHOR "dc42" // Other firmware that we might switch to be compatible with. diff --git a/Flash/DueFlashStorage.cpp b/Flash/DueFlashStorage.cpp new file mode 100644 index 0000000..2314e19 --- /dev/null +++ b/Flash/DueFlashStorage.cpp @@ -0,0 +1,83 @@ +#include "DueFlashStorage.h" + +void DueFlashStorage::init() { + /* Initialize flash: 6 wait states for flash writing. */ + uint32_t retCode = flash_init(FLASH_ACCESS_MODE_128, 6); + if (retCode != FLASH_RC_OK) { + _FLASH_DEBUG("Flash init failed\n"); + } +} + +byte DueFlashStorage::read(uint32_t address) { + return FLASH_START[address]; +} + +void DueFlashStorage::read(uint32_t address, void *data, uint32_t dataLength) { + memcpy(data, FLASH_START+address, dataLength); +} + +bool DueFlashStorage::write(uint32_t address, byte value) { + uint32_t byteLength = 1; + uint32_t retCode = flash_unlock((uint32_t)FLASH_START+address, (uint32_t)FLASH_START+address + byteLength - 1, 0, 0); + if (retCode != FLASH_RC_OK) { + _FLASH_DEBUG("Failed to unlock flash for write\n"); + return false; + } + + // write data + retCode = flash_write((uint32_t)FLASH_START+address, &value, byteLength, 1); + + if (retCode != FLASH_RC_OK) { + _FLASH_DEBUG("Flash write failed\n"); + return false; + } + + // Lock page + retCode = flash_lock((uint32_t)FLASH_START+address, (uint32_t)FLASH_START+address + byteLength - 1, 0, 0); + if (retCode != FLASH_RC_OK) { + _FLASH_DEBUG("Failed to lock flash page\n"); + return false; + } + return true; +} + +bool DueFlashStorage::write(uint32_t address, const void *data, uint32_t dataLength) { + if ((uint32_t)FLASH_START+address < IFLASH1_ADDR) { + _FLASH_DEBUG("Flash write address too low\n"); + return false; + } + + if ((uint32_t)FLASH_START+address >= (IFLASH1_ADDR + IFLASH1_SIZE)) { + _FLASH_DEBUG("Flash write address too high\n"); + return false; + } + + if (((uint32_t)FLASH_START+address & 3) != 0) { + _FLASH_DEBUG("Flash start address must be on four byte boundary\n"); + return false; + } + + // Unlock page + uint32_t retCode = flash_unlock((uint32_t)FLASH_START+address, (uint32_t)FLASH_START+address + dataLength - 1, 0, 0); + if (retCode != FLASH_RC_OK) { + _FLASH_DEBUG("Failed to unlock flash for write\n"); + return false; + } + + // write data + retCode = flash_write((uint32_t)FLASH_START+address, data, dataLength, 1); + + if (retCode != FLASH_RC_OK) { + _FLASH_DEBUG("Flash write failed\n"); + return false; + } + + // Lock page + retCode = flash_lock((uint32_t)FLASH_START+address, (uint32_t)FLASH_START+address + dataLength - 1, 0, 0); + if (retCode != FLASH_RC_OK) { + _FLASH_DEBUG("Failed to lock flash page\n"); + return false; + } + return true; +} + diff --git a/Flash/DueFlashStorage.h b/Flash/DueFlashStorage.h new file mode 100644 index 0000000..283a457 --- /dev/null +++ b/Flash/DueFlashStorage.h @@ -0,0 +1,52 @@ +/* +DueFlashStorage saves non-volatile data for Arduino Due. +The library is made to be similar to EEPROM library +Uses flash block 1 per default. + +Note: uploading new software will erase all flash so data written to flash +using this library will not survive a new software upload. + +Inspiration from Pansenti at https://github.com/Pansenti/DueFlash +Rewritten and modified by Sebastian Nilsson +Further modified up by David Crocker +*/ + + +#ifndef DUEFLASHSTORAGE_H +#define DUEFLASHSTORAGE_H + +#include +#include "flash_efc.h" +#include "efc.h" + +// 1Kb of data +#define DATA_LENGTH ((IFLASH1_PAGE_SIZE/sizeof(byte))*4) + +// Choose a start address close to the top of the Flash 1 memory space +#define FLASH_START ((byte *)(IFLASH1_ADDR + IFLASH1_SIZE - DATA_LENGTH)) + +// FLASH_DEBUG can be enabled to get debugging information displayed. +//#define FLASH_DEBUG + +#ifdef FLASH_DEBUG +#define _FLASH_DEBUG(x) Serial.print(x); +#else +#define _FLASH_DEBUG(x) +#endif + +// DueFlash is the main namespace for flash functions +namespace DueFlashStorage { + void init(); + + // write() writes the specified amount of data into flash. + // flashStart is the address in memory where the write should start + // data is a pointer to the data to be written + // dataLength is length of data in bytes + + byte read(uint32_t address); + void read(uint32_t address, void *data, uint32_t dataLength); + bool write(uint32_t address, byte value); + bool write(uint32_t address, const void *data, uint32_t dataLength); +}; + +#endif diff --git a/Flash/efc.cpp b/Flash/efc.cpp new file mode 100644 index 0000000..74f0c8a --- /dev/null +++ b/Flash/efc.cpp @@ -0,0 +1,340 @@ +/** + * \file + * + * \brief Enhanced Embedded Flash Controller (EEFC) driver for SAM. + * + * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE 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. + * + * \asf_license_stop + * + */ + +#include "efc.h" + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +extern "C" { +#endif +/**INDENT-ON**/ +/// @endcond + +/** + * \defgroup sam_drivers_efc_group Enhanced Embedded Flash Controller (EEFC) + * + * The Enhanced Embedded Flash Controller ensures the interface of the Flash + * block with the 32-bit internal bus. + * + * @{ + */ + +/* Address definition for read operation */ +# define READ_BUFF_ADDR0 IFLASH0_ADDR +# define READ_BUFF_ADDR1 IFLASH1_ADDR + +/* Flash Writing Protection Key */ +#define FWP_KEY 0x5Au + +#if (SAM4S || SAM4E) +#define EEFC_FCR_FCMD(value) \ + ((EEFC_FCR_FCMD_Msk & ((value) << EEFC_FCR_FCMD_Pos))) +#define EEFC_ERROR_FLAGS (EEFC_FSR_FLOCKE | EEFC_FSR_FCMDE | EEFC_FSR_FLERR) +#else +#define EEFC_ERROR_FLAGS (EEFC_FSR_FLOCKE | EEFC_FSR_FCMDE) +#endif + + + +/* + * Local function declaration. + * Because they are RAM functions, they need 'extern' declaration. + */ +extern void efc_write_fmr(Efc *p_efc, uint32_t ul_fmr); +extern uint32_t efc_perform_fcr(Efc *p_efc, uint32_t ul_fcr); + +/** + * \brief Initialize the EFC controller. + * + * \param ul_access_mode 0 for 128-bit, EEFC_FMR_FAM for 64-bit. + * \param ul_fws The number of wait states in cycle (no shift). + * + * \return 0 if successful. + */ +uint32_t efc_init(Efc *p_efc, uint32_t ul_access_mode, uint32_t ul_fws) +{ + efc_write_fmr(p_efc, ul_access_mode | EEFC_FMR_FWS(ul_fws)); + return EFC_RC_OK; +} + +/** + * \brief Enable the flash ready interrupt. + * + * \param p_efc Pointer to an EFC instance. + */ +void efc_enable_frdy_interrupt(Efc *p_efc) +{ + uint32_t ul_fmr = p_efc->EEFC_FMR; + + efc_write_fmr(p_efc, ul_fmr | EEFC_FMR_FRDY); +} + +/** + * \brief Disable the flash ready interrupt. + * + * \param p_efc Pointer to an EFC instance. + */ +void efc_disable_frdy_interrupt(Efc *p_efc) +{ + uint32_t ul_fmr = p_efc->EEFC_FMR; + + efc_write_fmr(p_efc, ul_fmr & (~EEFC_FMR_FRDY)); +} + +/** + * \brief Set flash access mode. + * + * \param p_efc Pointer to an EFC instance. + * \param ul_mode 0 for 128-bit, EEFC_FMR_FAM for 64-bit. + */ +void efc_set_flash_access_mode(Efc *p_efc, uint32_t ul_mode) +{ + uint32_t ul_fmr = p_efc->EEFC_FMR & (~EEFC_FMR_FAM); + + efc_write_fmr(p_efc, ul_fmr | ul_mode); +} + +/** + * \brief Get flash access mode. + * + * \param p_efc Pointer to an EFC instance. + * + * \return 0 for 128-bit or EEFC_FMR_FAM for 64-bit. + */ +uint32_t efc_get_flash_access_mode(Efc *p_efc) +{ + return (p_efc->EEFC_FMR & EEFC_FMR_FAM); +} + +/** + * \brief Set flash wait state. + * + * \param p_efc Pointer to an EFC instance. + * \param ul_fws The number of wait states in cycle (no shift). + */ +void efc_set_wait_state(Efc *p_efc, uint32_t ul_fws) +{ + uint32_t ul_fmr = p_efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk); + + efc_write_fmr(p_efc, ul_fmr | EEFC_FMR_FWS(ul_fws)); +} + +/** + * \brief Get flash wait state. + * + * \param p_efc Pointer to an EFC instance. + * + * \return The number of wait states in cycle (no shift). + */ +uint32_t efc_get_wait_state(Efc *p_efc) +{ + return ((p_efc->EEFC_FMR & EEFC_FMR_FWS_Msk) >> EEFC_FMR_FWS_Pos); +} + +/** + * \brief Perform the given command and wait until its completion (or an error). + * + * \note Unique ID commands are not supported, use efc_read_unique_id. + * + * \param p_efc Pointer to an EFC instance. + * \param ul_command Command to perform. + * \param ul_argument Optional command argument. + * + * \note This function will automatically choose to use IAP function. + * + * \return 0 if successful, otherwise returns an error code. + */ +uint32_t efc_perform_command(Efc *p_efc, uint32_t ul_command, + uint32_t ul_argument) +{ + /* Unique ID commands are not supported. */ + if (ul_command == EFC_FCMD_STUI || ul_command == EFC_FCMD_SPUI) { + return EFC_RC_NOT_SUPPORT; + } + + /* Use IAP function with 2 parameters in ROM. */ + static uint32_t(*iap_perform_command) (uint32_t, uint32_t); + uint32_t ul_efc_nb = (p_efc == EFC0) ? 0 : 1; + + iap_perform_command = + (uint32_t(*)(uint32_t, uint32_t)) + *((uint32_t *) CHIP_FLASH_IAP_ADDRESS); + iap_perform_command(ul_efc_nb, + EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(ul_argument) | + EEFC_FCR_FCMD(ul_command)); + return (p_efc->EEFC_FSR & EEFC_ERROR_FLAGS); +} + +/** + * \brief Get the current status of the EEFC. + * + * \note This function clears the value of some status bits (FLOCKE, FCMDE). + * + * \param p_efc Pointer to an EFC instance. + * + * \return The current status. + */ +uint32_t efc_get_status(Efc *p_efc) +{ + return p_efc->EEFC_FSR; +} + +/** + * \brief Get the result of the last executed command. + * + * \param p_efc Pointer to an EFC instance. + * + * \return The result of the last executed command. + */ +uint32_t efc_get_result(Efc *p_efc) +{ + return p_efc->EEFC_FRR; +} + +/** + * \brief Perform read sequence. Supported sequences are read Unique ID and + * read User Signature + * + * \param p_efc Pointer to an EFC instance. + * \param ul_cmd_st Start command to perform. + * \param ul_cmd_sp Stop command to perform. + * \param p_ul_buf Pointer to an data buffer. + * \param ul_size Buffer size. + * + * \return 0 if successful, otherwise returns an error code. + */ +RAMFUNC +uint32_t efc_perform_read_sequence(Efc *p_efc, + uint32_t ul_cmd_st, uint32_t ul_cmd_sp, + uint32_t *p_ul_buf, uint32_t ul_size) +{ + volatile uint32_t ul_status; + uint32_t ul_cnt; + + uint32_t *p_ul_data = + (uint32_t *) ((p_efc == EFC0) ? + READ_BUFF_ADDR0 : READ_BUFF_ADDR1); + + if (p_ul_buf == NULL) { + return EFC_RC_INVALID; + } + + p_efc->EEFC_FMR |= (0x1u << 16); + + /* Send the Start Read command */ + + p_efc->EEFC_FCR = EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(0) + | EEFC_FCR_FCMD(ul_cmd_st); + /* Wait for the FRDY bit in the Flash Programming Status Register + * (EEFC_FSR) falls. + */ + do { + ul_status = p_efc->EEFC_FSR; + } while ((ul_status & EEFC_FSR_FRDY) == EEFC_FSR_FRDY); + + /* The data is located in the first address of the Flash + * memory mapping. + */ + for (ul_cnt = 0; ul_cnt < ul_size; ul_cnt++) { + p_ul_buf[ul_cnt] = p_ul_data[ul_cnt]; + } + + /* To stop the read mode */ + p_efc->EEFC_FCR = + EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(0) | + EEFC_FCR_FCMD(ul_cmd_sp); + /* Wait for the FRDY bit in the Flash Programming Status Register (EEFC_FSR) + * rises. + */ + do { + ul_status = p_efc->EEFC_FSR; + } while ((ul_status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY); + + p_efc->EEFC_FMR &= ~(0x1u << 16); + + return EFC_RC_OK; +} + +/** + * \brief Set mode register. + * + * \param p_efc Pointer to an EFC instance. + * \param ul_fmr Value of mode register + */ +RAMFUNC +void efc_write_fmr(Efc *p_efc, uint32_t ul_fmr) +{ + p_efc->EEFC_FMR = ul_fmr; +} + +/** + * \brief Perform command. + * + * \param p_efc Pointer to an EFC instance. + * \param ul_fcr Flash command. + * + * \return The current status. + */ +RAMFUNC +uint32_t efc_perform_fcr(Efc *p_efc, uint32_t ul_fcr) +{ + volatile uint32_t ul_status; + + p_efc->EEFC_FCR = ul_fcr; + do { + ul_status = p_efc->EEFC_FSR; + } while ((ul_status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY); + + return (ul_status & EEFC_ERROR_FLAGS); +} + +//@} + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +} +#endif +/**INDENT-ON**/ +/// @endcond diff --git a/Flash/efc.h b/Flash/efc.h new file mode 100644 index 0000000..4481b98 --- /dev/null +++ b/Flash/efc.h @@ -0,0 +1,139 @@ +/** + * \file + * + * \brief Embedded Flash Controller (EFC) driver for SAM. + * + * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE 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. + * + * \asf_license_stop + * + */ + +#ifndef EFC_H_INCLUDED +#define EFC_H_INCLUDED + +#include +#include + +#define SAM3XA +#define RAMFUNC __attribute__ ((section(".ramfunc"))) + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +extern "C" { +#endif +/**INDENT-ON**/ +/// @endcond + +/*! \name EFC return codes */ +//! @{ +typedef enum efc_rc { + EFC_RC_OK = 0, //!< Operation OK + EFC_RC_YES = 0, //!< Yes + EFC_RC_NO = 1, //!< No + EFC_RC_ERROR = 1, //!< General error + EFC_RC_INVALID, //!< Invalid argument input + EFC_RC_NOT_SUPPORT = 0xFFFFFFFF //!< Operation is not supported +} efc_rc_t; +//! @} + +/*! \name EFC command */ +//! @{ +#define EFC_FCMD_GETD 0x00 //!< Get Flash Descriptor +#define EFC_FCMD_WP 0x01 //!< Write page +#define EFC_FCMD_WPL 0x02 //!< Write page and lock +#define EFC_FCMD_EWP 0x03 //!< Erase page and write page +#define EFC_FCMD_EWPL 0x04 //!< Erase page and write page then lock +#define EFC_FCMD_EA 0x05 //!< Erase all +#if (SAM3SD8) +#define EFC_FCMD_EPL 0x06 //!< Erase plane +#endif +#if (SAM4S || SAM4E) +#define EFC_FCMD_EPA 0x07 //!< Erase pages +#endif +#define EFC_FCMD_SLB 0x08 //!< Set Lock Bit +#define EFC_FCMD_CLB 0x09 //!< Clear Lock Bit +#define EFC_FCMD_GLB 0x0A //!< Get Lock Bit +#define EFC_FCMD_SGPB 0x0B //!< Set GPNVM Bit +#define EFC_FCMD_CGPB 0x0C //!< Clear GPNVM Bit +#define EFC_FCMD_GGPB 0x0D //!< Get GPNVM Bit +#define EFC_FCMD_STUI 0x0E //!< Start unique ID +#define EFC_FCMD_SPUI 0x0F //!< Stop unique ID +#if (!SAM3U && !SAM3SD8 && !SAM3S8) +#define EFC_FCMD_GCALB 0x10 //!< Get CALIB Bit +#endif +#if (SAM4S || SAM4E) +#define EFC_FCMD_ES 0x11 //!< Erase sector +#define EFC_FCMD_WUS 0x12 //!< Write user signature +#define EFC_FCMD_EUS 0x13 //!< Erase user signature +#define EFC_FCMD_STUS 0x14 //!< Start read user signature +#define EFC_FCMD_SPUS 0x15 //!< Stop read user signature +#endif +//! @} + +/*! The IAP function entry address */ +#define CHIP_FLASH_IAP_ADDRESS (IROM_ADDR + 8) + +/*! \name EFC access mode */ +//! @{ +#define EFC_ACCESS_MODE_128 0 +#define EFC_ACCESS_MODE_64 EEFC_FMR_FAM +//! @} + +uint32_t efc_init(Efc *p_efc, uint32_t ul_access_mode, uint32_t ul_fws); +void efc_enable_frdy_interrupt(Efc *p_efc); +void efc_disable_frdy_interrupt(Efc *p_efc); +void efc_set_flash_access_mode(Efc *p_efc, uint32_t ul_mode); +uint32_t efc_get_flash_access_mode(Efc *p_efc); +void efc_set_wait_state(Efc *p_efc, uint32_t ul_fws); +uint32_t efc_get_wait_state(Efc *p_efc); +uint32_t efc_perform_command(Efc *p_efc, uint32_t ul_command, + uint32_t ul_argument); +uint32_t efc_get_status(Efc *p_efc); +uint32_t efc_get_result(Efc *p_efc); +uint32_t efc_perform_read_sequence(Efc *p_efc, + uint32_t ul_cmd_st, uint32_t ul_cmd_sp, + uint32_t *p_ul_buf, uint32_t ul_size); + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +} +#endif +/**INDENT-ON**/ +/// @endcond + +#endif /* EFC_H_INCLUDED */ diff --git a/Flash/flash_efc.cpp b/Flash/flash_efc.cpp new file mode 100644 index 0000000..c7b3aa8 --- /dev/null +++ b/Flash/flash_efc.cpp @@ -0,0 +1,916 @@ +/** + * \file + * + * \brief Embedded Flash service for SAM. + * + * Copyright (c) 2011-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE 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. + * + * \asf_license_stop + * + */ + +#include +#include +#include "flash_efc.h" + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +extern "C" { +#endif +/**INDENT-ON**/ +/// @endcond + +/** + * \defgroup sam_services_flash_efc_group Embedded Flash Service + * + * The Embedded Flash service provides functions for internal flash operations. + * + * @{ + */ + +#if SAM4E +/* User signature size */ +# define FLASH_USER_SIG_SIZE (512) +#endif + +#if SAM4S +/* Internal Flash Controller 0. */ +# define EFC EFC0 +/* User signature size */ +# define FLASH_USER_SIG_SIZE (512) +/* Internal Flash 0 base address. */ +# define IFLASH_ADDR IFLASH0_ADDR +/* Internal flash page size. */ +# define IFLASH_PAGE_SIZE IFLASH0_PAGE_SIZE +/* Internal flash lock region size. */ +# define IFLASH_LOCK_REGION_SIZE IFLASH0_LOCK_REGION_SIZE +#endif + +/* Internal Flash Controller 0. */ +# define EFC EFC0 +/* The max GPNVM number. */ +# define GPNVM_NUM_MAX 3 +/* Internal Flash 0 base address. */ +# define IFLASH_ADDR IFLASH0_ADDR +/* Internal flash page size. */ +# define IFLASH_PAGE_SIZE IFLASH0_PAGE_SIZE +/* Internal flash lock region size. */ +# define IFLASH_LOCK_REGION_SIZE IFLASH0_LOCK_REGION_SIZE + +/* Flash page buffer for alignment */ +static uint32_t gs_ul_page_buffer[IFLASH_PAGE_SIZE / sizeof(uint32_t)]; + +/** + * \brief Translate the given flash address to page and offset values. + * \note pus_page and pus_offset must not be null in order to store the + * corresponding values. + * + * \param pp_efc Pointer to an EFC pointer. + * \param ul_addr Address to translate. + * \param pus_page The first page accessed. + * \param pus_offset Byte offset in the first page. + */ +static void translate_address(Efc **pp_efc, uint32_t ul_addr, + uint16_t *pus_page, uint16_t *pus_offset) +{ + Efc *p_efc; + uint16_t us_page; + uint16_t us_offset; + + if (ul_addr >= IFLASH1_ADDR) { + p_efc = EFC1; + us_page = (ul_addr - IFLASH1_ADDR) / IFLASH1_PAGE_SIZE; + us_offset = (ul_addr - IFLASH1_ADDR) % IFLASH1_PAGE_SIZE; + } else { + p_efc = EFC0; + us_page = (ul_addr - IFLASH0_ADDR) / IFLASH0_PAGE_SIZE; + us_offset = (ul_addr - IFLASH0_ADDR) % IFLASH0_PAGE_SIZE; + } + + /* Store values */ + if (pp_efc) { + *pp_efc = p_efc; + } + + if (pus_page) { + *pus_page = us_page; + } + + if (pus_offset) { + *pus_offset = us_offset; + } +} + +/** + * \brief Compute the address of a flash by the given page and offset. + * + * \param p_efc Pointer to an EFC instance. + * \param us_page Page number. + * \param us_offset Byte offset inside page. + * \param pul_addr Computed address (optional). + */ +static void compute_address(Efc *p_efc, uint16_t us_page, uint16_t us_offset, + uint32_t *pul_addr) +{ + uint32_t ul_addr; + +/* Dual bank flash */ +#ifdef EFC1 + /* Compute address */ + ul_addr = (p_efc == EFC0) ? + IFLASH0_ADDR + us_page * IFLASH_PAGE_SIZE + us_offset : + IFLASH1_ADDR + us_page * IFLASH_PAGE_SIZE + us_offset; + +/* One bank flash */ +#else + /* avoid Cppcheck Warning */ + UNUSED(p_efc); + /* Compute address */ + ul_addr = IFLASH_ADDR + us_page * IFLASH_PAGE_SIZE + us_offset; +#endif + + /* Store result */ + if (pul_addr != NULL) { + *pul_addr = ul_addr; + } +} + +/** + * \brief Compute the lock range associated with the given address range. + * + * \param ul_start Start address of lock range. + * \param ul_end End address of lock range. + * \param pul_actual_start Actual start address of lock range. + * \param pul_actual_end Actual end address of lock range. + */ +static void compute_lock_range(uint32_t ul_start, uint32_t ul_end, + uint32_t *pul_actual_start, uint32_t *pul_actual_end) +{ + uint32_t ul_actual_start, ul_actual_end; + + ul_actual_start = ul_start - (ul_start % IFLASH_LOCK_REGION_SIZE); + ul_actual_end = ul_end - (ul_end % IFLASH_LOCK_REGION_SIZE) + + IFLASH_LOCK_REGION_SIZE - 1; + + if (pul_actual_start) { + *pul_actual_start = ul_actual_start; + } + + if (pul_actual_end) { + *pul_actual_end = ul_actual_end; + } +} + +/** + * \brief Initialize the flash service. + * + * \param ul_mode FLASH_ACCESS_MODE_128 or FLASH_ACCESS_MODE_64. + * \param ul_fws The number of wait states in cycle (no shift). + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_init(uint32_t ul_mode, uint32_t ul_fws) +{ + efc_init(EFC, ul_mode, ul_fws); + +#ifdef EFC1 + efc_init(EFC1, ul_mode, ul_fws); +#endif + + return FLASH_RC_OK; +} + +/** + * \brief Set flash wait state. + * + * \param ul_address Flash bank start address. + * \param ul_fws The number of wait states in cycle (no shift). + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_set_wait_state(uint32_t ul_address, uint32_t ul_fws) +{ + Efc *p_efc; + + translate_address(&p_efc, ul_address, NULL, NULL); + efc_set_wait_state(p_efc, ul_fws); + + return FLASH_RC_OK; +} + +/** + * \brief Set flash wait state. + * + * \param ul_address Flash bank start address. + * \param ul_fws The number of wait states in cycle (no shift). + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_set_wait_state_adaptively(uint32_t ul_address) +{ + Efc *p_efc; + uint32_t clock = SystemCoreClock; + + translate_address(&p_efc, ul_address, NULL, NULL); + + /* Set FWS for embedded Flash access according to operating frequency */ + if (clock < CHIP_FREQ_FWS_0) { + efc_set_wait_state(p_efc, 0); + } else if (clock < CHIP_FREQ_FWS_1) { + efc_set_wait_state(p_efc, 1); + } else if (clock < CHIP_FREQ_FWS_2) { + efc_set_wait_state(p_efc, 2); + } else if (clock < CHIP_FREQ_FWS_3) { + efc_set_wait_state(p_efc, 3); + } else { + efc_set_wait_state(p_efc, 4); + } + return FLASH_RC_OK; +} + +/** + * \brief Get flash wait state. + * + * \param ul_address Flash bank start address. + * + * \return The number of wait states in cycle (no shift). + */ +uint32_t flash_get_wait_state(uint32_t ul_address) +{ + Efc *p_efc; + + translate_address(&p_efc, ul_address, NULL, NULL); + return efc_get_wait_state(p_efc); +} + +/** + * \brief Get flash descriptor. + * + * \param ul_address Flash bank start address. + * \param pul_flash_descriptor Pointer to a data buffer to store flash descriptor. + * \param ul_size Data buffer size in DWORD. + * + * \return The actual descriptor length. + */ +uint32_t flash_get_descriptor(uint32_t ul_address, + uint32_t *pul_flash_descriptor, uint32_t ul_size) +{ + Efc *p_efc; + uint32_t ul_tmp; + uint32_t ul_cnt; + + translate_address(&p_efc, ul_address, NULL, NULL); + + /* Command fails */ + if (FLASH_RC_OK != efc_perform_command(p_efc, EFC_FCMD_GETD, 0)) { + return 0; + } else { + /* Read until no result */ + for (ul_cnt = 0;; ul_cnt++) { + ul_tmp = efc_get_result(p_efc); + if ((ul_size > ul_cnt) && (ul_tmp != 0)) { + *pul_flash_descriptor++ = ul_tmp; + } else { + break; + } + } + } + + return ul_cnt; +} + +/** + * \brief Get flash total page count for the specified bank. + * + * \note The flash descriptor must be fetched from flash_get_descriptor + * function first. + * + * \param pul_flash_descriptor Pointer to a flash descriptor. + * + * \return The flash total page count. + */ +uint32_t flash_get_page_count(const uint32_t *pul_flash_descriptor) +{ + return (pul_flash_descriptor[1] / pul_flash_descriptor[2]); +} + +/** + * \brief Get flash page count per region (plane) for the specified bank. + * + * \note The flash descriptor must be fetched from flash_get_descriptor + * function first. + * + * \param pul_flash_descriptor Pointer to a flash descriptor. + * + * \return The flash page count per region (plane). + */ +uint32_t flash_get_page_count_per_region(const uint32_t *pul_flash_descriptor) +{ + return (pul_flash_descriptor[4] / pul_flash_descriptor[2]); +} + +/** + * \brief Get flash region (plane) count for the specified bank. + * + * \note The flash descriptor must be fetched from flash_get_descriptor + * function first. + * + * \param pul_flash_descriptor Pointer to a flash descriptor. + * + * \return The flash region (plane) count. + */ +uint32_t flash_get_region_count(const uint32_t *pul_flash_descriptor) +{ + return (pul_flash_descriptor[3]); +} + +/** + * \brief Erase the entire flash. + * + * \note Only the flash bank including ul_address will be erased. If there are + * two flash banks, we need to call this function twice with each bank start + * address. + * + * \param ul_address Flash bank start address. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_erase_all(uint32_t ul_address) +{ + Efc *p_efc; + + translate_address(&p_efc, ul_address, NULL, NULL); + + if (EFC_RC_OK != efc_perform_command(p_efc, EFC_FCMD_EA, 0)) { + return FLASH_RC_ERROR; + } + + return FLASH_RC_OK; +} + +#if SAM3SD8 +/** + * \brief Erase the flash by plane. + * + * \param ul_address Flash plane start address. + * + * \note Erase plane command needs a page number parameter which belongs to + * the plane to be erased. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_erase_plane(uint32_t ul_address) +{ + Efc *p_efc; + uint16_t us_page; + + translate_address(&p_efc, ul_address, &us_page, NULL); + + if (EFC_RC_OK != efc_perform_command(p_efc, EFC_FCMD_EPL, us_page)) { + return FLASH_RC_ERROR; + } + + return FLASH_RC_OK; +} +#endif + +#if (SAM4S || SAM4E) +/** + * \brief Erase the specified pages of flash. + * + * \param ul_address Flash bank start address. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_erase_page(uint32_t ul_address, uint8_t uc_page_num) +{ + Efc *p_efc; + uint16_t us_page; + + if (uc_page_num >= IFLASH_ERASE_PAGES_INVALID) { + return FLASH_RC_INVALID; + } + + if (ul_address & (IFLASH_PAGE_SIZE - 1)) { + return FLASH_RC_INVALID; + } + + translate_address(&p_efc, ul_address, &us_page, NULL); + + if (EFC_RC_OK != efc_perform_command(p_efc, EFC_FCMD_EPA, + (us_page | uc_page_num))) { + return FLASH_RC_ERROR; + } + + return FLASH_RC_OK; +} + +/** + * \brief Erase the flash sector. + * + * \note Erase sector command needs a page number parameter which belongs to + * the sector to be erased. + * + * \param ul_address Flash sector start address. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_erase_sector(uint32_t ul_address) +{ + Efc *p_efc; + uint16_t us_page; + + translate_address(&p_efc, ul_address, &us_page, NULL); + + if (EFC_RC_OK != efc_perform_command(p_efc, EFC_FCMD_ES, us_page)) { + return FLASH_RC_ERROR; + } + + return FLASH_RC_OK; +} +#endif + +/** + * \brief Write a data buffer on flash. + * + * \note This function works in polling mode, and thus only returns when the + * data has been effectively written. + * \note For dual bank flash, this function doesn't support cross write from + * bank 0 to bank 1. In this case, flash_write must be called twice (ie for + * each bank). + * + * \param ul_address Write address. + * \param p_buffer Data buffer. + * \param ul_size Size of data buffer in bytes. + * \param ul_erase_flag Flag to set if erase first. + * + * \return 0 if successful, otherwise returns an error code. + */ +uint32_t flash_write(uint32_t ul_address, const void *p_buffer, + uint32_t ul_size, uint32_t ul_erase_flag) +{ + Efc *p_efc; + uint32_t ul_fws_temp; + uint16_t us_page; + uint16_t us_offset; + uint32_t writeSize; + uint32_t ul_page_addr; + uint16_t us_padding; + uint32_t ul_error; + uint32_t ul_idx; + uint32_t *p_aligned_dest; + uint8_t *puc_page_buffer = (uint8_t *) gs_ul_page_buffer; + + translate_address(&p_efc, ul_address, &us_page, &us_offset); + + /* According to the errata, set the wait state value to 6. */ + ul_fws_temp = efc_get_wait_state(p_efc); + efc_set_wait_state(p_efc, 6); + + /* Write all pages */ + while (ul_size > 0) { + /* Copy data in temporary buffer to avoid alignment problems. */ + writeSize = Min((uint32_t) IFLASH_PAGE_SIZE - us_offset, + ul_size); + compute_address(p_efc, us_page, 0, &ul_page_addr); + us_padding = IFLASH_PAGE_SIZE - us_offset - writeSize; + + /* Pre-buffer data */ + memcpy(puc_page_buffer, (void *)ul_page_addr, us_offset); + + /* Buffer data */ + memcpy(puc_page_buffer + us_offset, p_buffer, writeSize); + + /* Post-buffer data */ + memcpy(puc_page_buffer + us_offset + writeSize, + (void *)(ul_page_addr + us_offset + writeSize), + us_padding); + + /* Write page. + * Writing 8-bit and 16-bit data is not allowed and may lead to + * unpredictable data corruption. + */ + p_aligned_dest = (uint32_t *) ul_page_addr; + for (ul_idx = 0; ul_idx < (IFLASH_PAGE_SIZE / sizeof(uint32_t)); + ++ul_idx) { + *p_aligned_dest++ = gs_ul_page_buffer[ul_idx]; + } + + if (ul_erase_flag) { + ul_error = efc_perform_command(p_efc, EFC_FCMD_EWP, + us_page); + } else { + ul_error = efc_perform_command(p_efc, EFC_FCMD_WP, + us_page); + } + + if (ul_error) { + return ul_error; + } + + /* Progression */ + p_buffer = (void *)((uint32_t) p_buffer + writeSize); + ul_size -= writeSize; + us_page++; + us_offset = 0; + } + + /* According to the errata, restore the wait state value. */ + efc_set_wait_state(p_efc, ul_fws_temp); + + return FLASH_RC_OK; +} + + +/** + * \brief Lock all the regions in the given address range. The actual lock + * range is reported through two output parameters. + * + * \param ul_start Start address of lock range. + * \param ul_end End address of lock range. + * \param pul_actual_start Start address of the actual lock range (optional). + * \param pul_actual_end End address of the actual lock range (optional). + * + * \return 0 if successful, otherwise returns an error code. + */ +uint32_t flash_lock(uint32_t ul_start, uint32_t ul_end, + uint32_t *pul_actual_start, uint32_t *pul_actual_end) +{ + Efc *p_efc; + uint32_t ul_actual_start, ul_actual_end; + uint16_t us_start_page, us_end_page; + uint32_t ul_error; + uint16_t us_num_pages_in_region = + IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE; + + /* Compute actual lock range and store it */ + compute_lock_range(ul_start, ul_end, &ul_actual_start, &ul_actual_end); + + if (pul_actual_start != NULL) { + *pul_actual_start = ul_actual_start; + } + + if (pul_actual_end != NULL) { + *pul_actual_end = ul_actual_end; + } + + /* Compute page numbers */ + translate_address(&p_efc, ul_actual_start, &us_start_page, 0); + translate_address(0, ul_actual_end, &us_end_page, 0); + + /* Lock all pages */ + while (us_start_page < us_end_page) { + ul_error = efc_perform_command(p_efc, EFC_FCMD_SLB, us_start_page); + + if (ul_error) { + return ul_error; + } + us_start_page += us_num_pages_in_region; + } + + return FLASH_RC_OK; +} + +/** + * \brief Unlock all the regions in the given address range. The actual unlock + * range is reported through two output parameters. + * + * \param ul_start Start address of unlock range. + * \param ul_end End address of unlock range. + * \param pul_actual_start Start address of the actual unlock range (optional). + * \param pul_actual_end End address of the actual unlock range (optional). + * + * \return 0 if successful, otherwise returns an error code. + */ +uint32_t flash_unlock(uint32_t ul_start, uint32_t ul_end, + uint32_t *pul_actual_start, uint32_t *pul_actual_end) +{ + Efc *p_efc; + uint32_t ul_actual_start, ul_actual_end; + uint16_t us_start_page, us_end_page; + uint32_t ul_error; + uint16_t us_num_pages_in_region = + IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE; + + /* Compute actual unlock range and store it */ + compute_lock_range(ul_start, ul_end, &ul_actual_start, &ul_actual_end); + if (pul_actual_start != NULL) { + *pul_actual_start = ul_actual_start; + } + if (pul_actual_end != NULL) { + *pul_actual_end = ul_actual_end; + } + + /* Compute page numbers */ + translate_address(&p_efc, ul_actual_start, &us_start_page, 0); + translate_address(0, ul_actual_end, &us_end_page, 0); + + /* Unlock all pages */ + while (us_start_page < us_end_page) { + ul_error = efc_perform_command(p_efc, EFC_FCMD_CLB, + us_start_page); + if (ul_error) { + return ul_error; + } + us_start_page += us_num_pages_in_region; + } + + return FLASH_RC_OK; +} + +/** + * \brief Get the number of locked regions inside the given address range. + * + * \param ul_start Start address of range + * \param ul_end End address of range. + * + * \return The number of locked regions inside the given address range. + */ +uint32_t flash_is_locked(uint32_t ul_start, uint32_t ul_end) +{ + Efc *p_efc; + uint16_t us_start_page, us_end_page; + uint8_t uc_start_region, uc_end_region; + uint16_t us_num_pages_in_region; + uint32_t ul_status; + uint32_t ul_error; + uint32_t ul_num_locked_regions = 0; + uint32_t ul_count = 0; + uint32_t ul_bit = 0; + + /* Compute page numbers */ + translate_address(&p_efc, ul_start, &us_start_page, 0); + translate_address(0, ul_end, &us_end_page, 0); + + /* Compute region numbers */ + us_num_pages_in_region = IFLASH_LOCK_REGION_SIZE / IFLASH_PAGE_SIZE; + uc_start_region = us_start_page / us_num_pages_in_region; + uc_end_region = us_end_page / us_num_pages_in_region; + + /* Retrieve lock status */ + ul_error = efc_perform_command(p_efc, EFC_FCMD_GLB, 0); + + /* Skip unrequested regions (if necessary) */ + ul_status = efc_get_result(p_efc); + while (!(ul_count <= uc_start_region && + uc_start_region < (ul_count + 32))) { + ul_status = efc_get_result(p_efc); + ul_count += 32; + } + + /* Check status of each involved region */ + ul_bit = uc_start_region - ul_count; + + /* Number of region to check (must be > 0) */ + ul_count = uc_end_region - uc_start_region + 1; + + while (ul_count > 0) { + if (ul_status & (1 << (ul_bit))) { + ul_num_locked_regions++; + } + + ul_count -= 1; + ul_bit += 1; + if (ul_bit == 32) { + ul_status = efc_get_result(p_efc); + ul_bit = 0; + } + } + + return ul_num_locked_regions; +} + +/** + * \brief Set the given GPNVM bit. + * + * \param ul_gpnvm GPNVM bit index. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_set_gpnvm(uint32_t ul_gpnvm) +{ + if (ul_gpnvm >= GPNVM_NUM_MAX) { + return FLASH_RC_INVALID; + } + + if (FLASH_RC_YES == flash_is_gpnvm_set(ul_gpnvm)) { + return FLASH_RC_OK; + } + + if (EFC_RC_OK == efc_perform_command(EFC, EFC_FCMD_SGPB, ul_gpnvm)) { + return FLASH_RC_OK; + } + + return FLASH_RC_ERROR; +} + +/** + * \brief Clear the given GPNVM bit. + * + * \param ul_gpnvm GPNVM bit index. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_clear_gpnvm(uint32_t ul_gpnvm) +{ + if (ul_gpnvm >= GPNVM_NUM_MAX) { + return FLASH_RC_INVALID; + } + + if (FLASH_RC_NO == flash_is_gpnvm_set(ul_gpnvm)) { + return FLASH_RC_OK; + } + + if (EFC_RC_OK == efc_perform_command(EFC, EFC_FCMD_CGPB, ul_gpnvm)) { + return FLASH_RC_OK; + } + + return FLASH_RC_ERROR; +} + +/** + * \brief Check if the given GPNVM bit is set or not. + * + * \param ul_gpnvm GPNVM bit index. + * + * \retval 1 If the given GPNVM bit is currently set. + * \retval 0 If the given GPNVM bit is currently cleared. + */ +uint32_t flash_is_gpnvm_set(uint32_t ul_gpnvm) +{ + uint32_t ul_gpnvm_bits; + + if (ul_gpnvm >= GPNVM_NUM_MAX) { + return FLASH_RC_INVALID; + } + + if (EFC_RC_OK != efc_perform_command(EFC, EFC_FCMD_GGPB, 0)) { + return FLASH_RC_ERROR; + } + + ul_gpnvm_bits = efc_get_result(EFC); + if (ul_gpnvm_bits & (1 << ul_gpnvm)) { + return FLASH_RC_YES; + } + + return FLASH_RC_NO; +} + +/** + * \brief Set security bit. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_enable_security_bit(void) +{ + return flash_set_gpnvm(0); +} + +/** + * \brief Check if the security bit is set or not. + * + * \retval 1 If the security bit is currently set. + * \retval 0 If the security bit is currently cleared. + */ +uint32_t flash_is_security_bit_enabled(void) +{ + return flash_is_gpnvm_set(0); +} + +/** + * \brief Read the flash unique ID. + * + * \param pul_data Pointer to a data buffer to store 128-bit unique ID. + * \param ul_size Data buffer size in DWORD. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_read_unique_id(uint32_t *pul_data, uint32_t ul_size) +{ + uint32_t uid_buf[4]; + uint32_t ul_idx; + + if (FLASH_RC_OK != efc_perform_read_sequence(EFC, EFC_FCMD_STUI, + EFC_FCMD_SPUI, uid_buf, 4)) { + return FLASH_RC_ERROR; + } + + if (ul_size > 4) { + /* Only 4 dword to store unique ID */ + ul_size = 4; + } + + for (ul_idx = 0; ul_idx < ul_size; ul_idx++) { + pul_data[ul_idx] = uid_buf[ul_idx]; + } + + return FLASH_RC_OK; +} + +#if (SAM4S || SAM4E) +/** + * \brief Read the flash user signature. + * + * \param p_data Pointer to a data buffer to store 512 bytes of user signature. + * \param ul_size Data buffer size. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size) +{ + if (ul_size > FLASH_USER_SIG_SIZE) { + /* Only 512 byte to store unique ID */ + ul_size = FLASH_USER_SIG_SIZE; + } + + /* Send the read user signature commands */ + if (FLASH_RC_OK != efc_perform_read_sequence(EFC, EFC_FCMD_STUS, + EFC_FCMD_SPUS, p_data, ul_size)) { + return FLASH_RC_ERROR; + } + + return FLASH_RC_OK; +} + +/** + * \brief Write the flash user signature. + * + * \param ul_address Write address. + * \param p_data Pointer to a data buffer to store 512 bytes of user signature. + * \param ul_size Data buffer size. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_write_user_signature(uint32_t ul_address, const void *p_buffer, + uint32_t ul_size) +{ + /* The user signature should be no longer than 512 bytes */ + if (ul_size > FLASH_USER_SIG_SIZE) { + return FLASH_RC_INVALID; + } + + /* Write the full page */ + flash_write(ul_address, p_buffer, ul_size, 0); + + /* Send the write signature command */ + if (FLASH_RC_OK != efc_perform_command(EFC, EFC_FCMD_WUS, 0)) { + return FLASH_RC_ERROR; + } + + return FLASH_RC_OK; +} + +/** + * \brief Erase the flash user signature. + * + * \return 0 if successful; otherwise returns an error code. + */ +uint32_t flash_erase_user_signature(void) +{ + /* Perform the erase user signature command */ + return efc_perform_command(EFC, EFC_FCMD_EUS, 0); +} +#endif + +//@} + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +} +#endif +/**INDENT-ON**/ +/// @endcond diff --git a/Flash/flash_efc.h b/Flash/flash_efc.h new file mode 100644 index 0000000..bd52033 --- /dev/null +++ b/Flash/flash_efc.h @@ -0,0 +1,151 @@ +/** + * \file + * + * \brief Embedded Flash service for SAM. + * + * Copyright (c) 2011-2013 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE 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. + * + * \asf_license_stop + * + */ + +#ifndef FLASH_H_INCLUDED +#define FLASH_H_INCLUDED + +#include +#include "efc.h" + +/* Internal Flash 0 base address. */ +#define IFLASH_ADDR IFLASH0_ADDR + /* Internal flash page size. */ +#define IFLASH_PAGE_SIZE IFLASH0_PAGE_SIZE + +/* Last page start address. */ +#define IFLASH_LAST_PAGE_ADDRESS (IFLASH1_ADDR + IFLASH1_SIZE - IFLASH1_PAGE_SIZE) + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus + extern "C" { +#endif +/**INDENT-ON**/ +/// @endcond + +/*! \name Flash driver return codes */ +//! @{ +typedef enum flash_rc { + FLASH_RC_OK = 0, //!< Operation OK + FLASH_RC_YES = 0, //!< Yes + FLASH_RC_NO = 1, //!< No + FLASH_RC_ERROR = 0x10, //!< General error + FLASH_RC_INVALID, //!< Invalid argument input + FLASH_RC_NOT_SUPPORT = 0xFFFFFFFF //!< Operation is not supported +} flash_rc_t; +//! @} + +/*! \name Flash erase page num in FARG[1:0] + \note The erase page commands should be cautiouly used as EPA4/EPA32 will not + take effect according to errata and EPA8/EPA16 must module 8/16 page addresses.*/ +//! @{ +typedef enum flash_farg_page_num { + /* 4 of pages to be erased with EPA command*/ + IFLASH_ERASE_PAGES_4=0, + /* 8 of pages to be erased with EPA command*/ + IFLASH_ERASE_PAGES_8, + /* 16 of pages to be erased with EPA command*/ + IFLASH_ERASE_PAGES_16, + /* 32 of pages to be erased with EPA command*/ + IFLASH_ERASE_PAGES_32, + /* Parameter is not support */ + IFLASH_ERASE_PAGES_INVALID, +}flash_farg_page_num_t; +//! @} + +/*! \name Flash access mode */ +//! @{ +#define FLASH_ACCESS_MODE_128 EFC_ACCESS_MODE_128 +#define FLASH_ACCESS_MODE_64 EFC_ACCESS_MODE_64 +//! @} + +uint32_t flash_init(uint32_t ul_mode, uint32_t ul_fws); +uint32_t flash_set_wait_state(uint32_t ul_address, uint32_t ul_fws); +uint32_t flash_set_wait_state_adaptively(uint32_t ul_address); +uint32_t flash_get_wait_state(uint32_t ul_address); +uint32_t flash_get_descriptor(uint32_t ul_address, + uint32_t *pul_flash_descriptor, uint32_t ul_size); +uint32_t flash_get_page_count(const uint32_t *pul_flash_descriptor); +uint32_t flash_get_page_count_per_region(const uint32_t *pul_flash_descriptor); +uint32_t flash_get_region_count(const uint32_t *pul_flash_descriptor); +uint32_t flash_erase_all(uint32_t ul_address); + +#if SAM3SD8 +uint32_t flash_erase_plane(uint32_t ul_address); +#endif + +#if (SAM4S || SAM4E) +uint32_t flash_erase_page(uint32_t ul_address, uint8_t uc_page_num); +uint32_t flash_erase_sector(uint32_t ul_address); +#endif + +uint32_t flash_write(uint32_t ul_address, const void *p_buffer, + uint32_t ul_size, uint32_t ul_erase_flag); +uint32_t flash_lock(uint32_t ul_start, uint32_t ul_end, + uint32_t *pul_actual_start, uint32_t *pul_actual_end); +uint32_t flash_unlock(uint32_t ul_start, uint32_t ul_end, + uint32_t *pul_actual_start, uint32_t *pul_actual_end); +uint32_t flash_is_locked(uint32_t ul_start, uint32_t ul_end); +uint32_t flash_set_gpnvm(uint32_t ul_gpnvm); +uint32_t flash_clear_gpnvm(uint32_t ul_gpnvm); +uint32_t flash_is_gpnvm_set(uint32_t ul_gpnvm); +uint32_t flash_enable_security_bit(void); +uint32_t flash_is_security_bit_enabled(void); +uint32_t flash_read_unique_id(uint32_t *pul_data, uint32_t ul_size); + +#if (SAM4S || SAM4E) +uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size); +uint32_t flash_write_user_signature(uint32_t ul_address, const void *p_buffer, + uint32_t ul_size); +uint32_t flash_erase_user_signature(void); +#endif + +/// @cond 0 +/**INDENT-OFF**/ +#ifdef __cplusplus +} +#endif +/**INDENT-ON**/ +/// @endcond + +#endif /* FLASH_H_INCLUDED */ diff --git a/GCodes.cpp b/GCodes.cpp index 841ca26..5008bdc 100644 --- a/GCodes.cpp +++ b/GCodes.cpp @@ -1,105 +1,106 @@ /**************************************************************************************************** -RepRapFirmware - G Codes + RepRapFirmware - G Codes -This class interprets G Codes from one or more sources, and calls the functions in Move, Heat etc -that drive the machine to do what the G Codes command. + This class interprets G Codes from one or more sources, and calls the functions in Move, Heat etc + that drive the machine to do what the G Codes command. -Most of the functions in here are designed not to wait, and they return a boolean. When you want them to do -something, you call them. If they return false, the machine can't do what you want yet. So you go away -and do something else. Then you try again. If they return true, the thing you wanted done has been done. + Most of the functions in here are designed not to wait, and they return a boolean. When you want them to do + something, you call them. If they return false, the machine can't do what you want yet. So you go away + and do something else. Then you try again. If they return true, the thing you wanted done has been done. ------------------------------------------------------------------------------------------------------ + ----------------------------------------------------------------------------------------------------- -Version 0.1 + Version 0.1 -13 February 2013 + 13 February 2013 -Adrian Bowyer -RepRap Professional Ltd -http://reprappro.com + Adrian Bowyer + RepRap Professional Ltd + http://reprappro.com -Licence: GPL + Licence: GPL -****************************************************************************************************/ + ****************************************************************************************************/ #include "RepRapFirmware.h" GCodes::GCodes(Platform* p, Webserver* w) { - active = false; - platform = p; - webserver = w; - webGCode = new GCodeBuffer(platform, "web: "); - fileGCode = new GCodeBuffer(platform, "file: "); - serialGCode = new GCodeBuffer(platform, "serial: "); - cannedCycleGCode = new GCodeBuffer(platform, "macro: "); + active = false; + platform = p; + webserver = w; + webGCode = new GCodeBuffer(platform, "web: "); + fileGCode = new GCodeBuffer(platform, "file: "); + serialGCode = new GCodeBuffer(platform, "serial: "); + cannedCycleGCode = new GCodeBuffer(platform, "macro: "); } void GCodes::Exit() { - platform->Message(HOST_MESSAGE, "GCodes class exited.\n"); - active = false; + platform->Message(HOST_MESSAGE, "GCodes class exited.\n"); + active = false; } void GCodes::Init() { - webGCode->Init(); - fileGCode->Init(); - serialGCode->Init(); - cannedCycleGCode->Init(); - webGCode->SetFinished(true); - fileGCode->SetFinished(true); - serialGCode->SetFinished(true); - cannedCycleGCode->SetFinished(true); - moveAvailable = false; - drivesRelative = true; - axesRelative = false; - checkEndStops = false; - gCodeLetters = GCODE_LETTERS; - distanceScale = 1.0; - for(int8_t i = 0; i < DRIVES - AXES; i++) - lastPos[i] = 0.0; - fileBeingPrinted = NULL; - fileToPrint = NULL; - fileBeingWritten = NULL; - configFile = NULL; - doingCannedCycleFile = false; - eofString = EOF_STRING; - eofStringCounter = 0; - eofStringLength = strlen(eofString); - homeX = false; - homeY = false; - homeZ = false; - homeAxisMoveCount = 0; - offSetSet = false; - dwellWaiting = false; - stackPointer = 0; - selectedHead = -1; - gFeedRate = platform->MaxFeedrate(Z_AXIS); // Typically the slowest - zProbesSet = false; - probeCount = 0; - cannedCycleMoveCount = 0; - cannedCycleMoveQueued = false; - active = true; - longWait = platform->Time(); - dwellTime = longWait; - axisIsHomed[X_AXIS] = axisIsHomed[Y_AXIS] = axisIsHomed[Z_AXIS] = false; + webGCode->Init(); + fileGCode->Init(); + serialGCode->Init(); + cannedCycleGCode->Init(); + webGCode->SetFinished(true); + fileGCode->SetFinished(true); + serialGCode->SetFinished(true); + cannedCycleGCode->SetFinished(true); + moveAvailable = false; + drivesRelative = true; + axesRelative = false; + checkEndStops = noEndstopCheck; + gCodeLetters = GCODE_LETTERS; + distanceScale = 1.0; + for (int8_t i = 0; i < DRIVES - AXES; i++) + lastPos[i] = 0.0; + fileBeingPrinted = NULL; + fileToPrint = NULL; + fileBeingWritten = NULL; + configFile = NULL; + doingCannedCycleFile = false; + eofString = EOF_STRING; + eofStringCounter = 0; + eofStringLength = strlen(eofString); + homeX = false; + homeY = false; + homeZ = false; + homeAxisMoveCount = 0; + offSetSet = false; + dwellWaiting = false; + stackPointer = 0; + selectedHead = -1; + gFeedRate = platform->MaxFeedrate(Z_AXIS); // Typically the slowest + zProbesSet = false; + probeCount = 0; + cannedCycleMoveCount = 0; + cannedCycleMoveQueued = false; + active = true; + longWait = platform->Time(); + dwellTime = longWait; + axisIsHomed[X_AXIS] = axisIsHomed[Y_AXIS] = axisIsHomed[Z_AXIS] = false; } void GCodes::doFilePrint(GCodeBuffer* gb) { char b; - if(fileBeingPrinted != NULL) + if (fileBeingPrinted != NULL) { - if(fileBeingPrinted->Read(b)) + if (fileBeingPrinted->Read(b)) { - if(gb->Put(b)) + if (gb->Put(b)) gb->SetFinished(ActOnGcode(gb)); - } else + } + else { - if(gb->Put('\n')) // In case there wasn't one ending the file + if (gb->Put('\n')) // In case there wasn't one ending the file gb->SetFinished(ActOnGcode(gb)); fileBeingPrinted->Close(); fileBeingPrinted = NULL; @@ -109,117 +110,118 @@ void GCodes::doFilePrint(GCodeBuffer* gb) void GCodes::Spin() { - if(!active) - return; - - // Check each of the sources of G Codes (web, serial, and file) to - // see if what they are doing has been done. If it hasn't, return without - // looking at anything else. - // - // Note the order establishes a priority: web first, then serial, and file - // last. If file weren't last, then the others would never get a look in when - // a file was being printed. + if (!active) + return; - if(!webGCode->Finished()) - { - webGCode->SetFinished(ActOnGcode(webGCode)); - platform->ClassReport("GCodes", longWait); - return; - } - - if(!serialGCode->Finished()) - { - serialGCode->SetFinished(ActOnGcode(serialGCode)); - platform->ClassReport("GCodes", longWait); - return; - } + // Check each of the sources of G Codes (web, serial, and file) to + // see if what they are doing has been done. If it hasn't, return without + // looking at anything else. + // + // Note the order establishes a priority: web first, then serial, and file + // last. If file weren't last, then the others would never get a look in when + // a file was being printed. - if(!fileGCode->Finished()) - { - fileGCode->SetFinished(ActOnGcode(fileGCode)); - platform->ClassReport("GCodes", longWait); - return; - } + if (!webGCode->Finished()) + { + webGCode->SetFinished(ActOnGcode(webGCode)); + platform->ClassReport("GCodes", longWait); + return; + } - // Now check if a G Code byte is available from each of the sources - // in the same order for the same reason. + if (!serialGCode->Finished()) + { + serialGCode->SetFinished(ActOnGcode(serialGCode)); + platform->ClassReport("GCodes", longWait); + return; + } - if(webserver->GCodeAvailable()) - { - int8_t i = 0; - do - { - char b = webserver->ReadGCode(); - if(webGCode->Put(b)) - { - // we have a complete gcode - if(webGCode->WritingFileDirectory() != NULL) - { - WriteGCodeToFile(webGCode); - } - else - { - webGCode->SetFinished(ActOnGcode(webGCode)); - } - break; // stop after receiving a complete gcode in case we haven't finished processing it - } - ++i; - } while ( i < 16 && webserver->GCodeAvailable()); - platform->ClassReport("GCodes", longWait); - return; - } - - // Now the serial interface. First check the special case of our - // uploading the reprap.htm file + if (!fileGCode->Finished()) + { + fileGCode->SetFinished(ActOnGcode(fileGCode)); + platform->ClassReport("GCodes", longWait); + return; + } - if(serialGCode->WritingFileDirectory() == platform->GetWebDir()) - { - if(platform->GetLine()->Status() & byteAvailable) - { - char b; - platform->GetLine()->Read(b); - WriteHTMLToFile(b, serialGCode); - } - } else - { - // Otherwise just deal in general with incoming bytes from the serial interface + // Now check if a G Code byte is available from each of the sources + // in the same order for the same reason. - if(platform->GetLine()->Status() & byteAvailable) - { - // Read several bytes instead of just one. This approximately doubles the speed of file uploading. - int8_t i = 0; - do - { - char b; - platform->GetLine()->Read(b); - if(serialGCode->Put(b)) // add char to buffer and test whether the gcode is complete - { - // we have a complete gcode - if(serialGCode->WritingFileDirectory() != NULL) - { - WriteGCodeToFile(serialGCode); - } - else - { - serialGCode->SetFinished(ActOnGcode(serialGCode)); - } - break; // stop after receiving a complete gcode in case we haven't finished processing it - } - ++i; - } while (i < 16 && (platform->GetLine()->Status() & byteAvailable)); - platform->ClassReport("GCodes", longWait); - return; - } - } + if (webserver->GCodeAvailable()) + { + int8_t i = 0; + do + { + char b = webserver->ReadGCode(); + if (webGCode->Put(b)) + { + // we have a complete gcode + if (webGCode->WritingFileDirectory() != NULL) + { + WriteGCodeToFile(webGCode); + } + else + { + webGCode->SetFinished(ActOnGcode(webGCode)); + } + break; // stop after receiving a complete gcode in case we haven't finished processing it + } + ++i; + } while (i < 16 && webserver->GCodeAvailable()); + platform->ClassReport("GCodes", longWait); + return; + } - doFilePrint(fileGCode); + // Now the serial interface. First check the special case of our + // uploading the reprap.htm file - platform->ClassReport("GCodes", longWait); + if (serialGCode->WritingFileDirectory() == platform->GetWebDir()) + { + if (platform->GetLine()->Status() & byteAvailable) + { + char b; + platform->GetLine()->Read(b); + WriteHTMLToFile(b, serialGCode); + } + } + else + { + // Otherwise just deal in general with incoming bytes from the serial interface + + if (platform->GetLine()->Status() & byteAvailable) + { + // Read several bytes instead of just one. This approximately doubles the speed of file uploading. + int8_t i = 0; + do + { + char b; + platform->GetLine()->Read(b); + if (serialGCode->Put(b)) // add char to buffer and test whether the gcode is complete + { + // we have a complete gcode + if (serialGCode->WritingFileDirectory() != NULL) + { + WriteGCodeToFile(serialGCode); + } + else + { + serialGCode->SetFinished(ActOnGcode(serialGCode)); + } + break; // stop after receiving a complete gcode in case we haven't finished processing it + } + ++i; + } while (i < 16 && (platform->GetLine()->Status() & byteAvailable)); + platform->ClassReport("GCodes", longWait); + return; + } + } + + doFilePrint(fileGCode); + + platform->ClassReport("GCodes", longWait); } -void GCodes::Diagnostics() +void GCodes::Diagnostics() { - platform->Message(HOST_MESSAGE, "GCodes Diagnostics:\n"); + platform->Message(HOST_MESSAGE, "GCodes Diagnostics:\n"); } // The wait till everything's done function. If you need the machine to @@ -229,23 +231,23 @@ void GCodes::Diagnostics() bool GCodes::AllMovesAreFinishedAndMoveBufferIsLoaded() { - // Last one gone? - - if(moveAvailable) - return false; - - // Wait for all the queued moves to stop so we get the actual last position and feedrate - - if(!reprap.GetMove()->AllMovesAreFinished()) - return false; - reprap.GetMove()->ResumeMoving(); - - // Load the last position; If Move can't accept more, return false - should never happen - - if(!reprap.GetMove()->GetCurrentState(moveBuffer)) - return false; - - return true; + // Last one gone? + + if (moveAvailable) + return false; + + // Wait for all the queued moves to stop so we get the actual last position and feedrate + + if (!reprap.GetMove()->AllMovesAreFinished()) + return false; + reprap.GetMove()->ResumeMoving(); + + // Load the last position; If Move can't accept more, return false - should never happen + + if (!reprap.GetMove()->GetCurrentState(moveBuffer)) + return false; + + return true; } // Save (some of) the state of the machine for recovery in the future. @@ -253,56 +255,56 @@ bool GCodes::AllMovesAreFinishedAndMoveBufferIsLoaded() bool GCodes::Push() { - if(stackPointer >= STACK) - { - platform->Message(HOST_MESSAGE, "Push(): stack overflow!\n"); - return true; - } - - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) - return false; - - drivesRelativeStack[stackPointer] = drivesRelative; - axesRelativeStack[stackPointer] = axesRelative; - feedrateStack[stackPointer] = gFeedRate; - fileStack[stackPointer] = fileBeingPrinted; - stackPointer++; - platform->PushMessageIndent(); - return true; + if (stackPointer >= STACK) + { + platform->Message(HOST_MESSAGE, "Push(): stack overflow!\n"); + return true; + } + + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) + return false; + + drivesRelativeStack[stackPointer] = drivesRelative; + axesRelativeStack[stackPointer] = axesRelative; + feedrateStack[stackPointer] = gFeedRate; + fileStack[stackPointer] = fileBeingPrinted; + stackPointer++; + platform->PushMessageIndent(); + return true; } // Recover a saved state. Call repeatedly till it returns true. bool GCodes::Pop() { - if(stackPointer <= 0) - { - platform->Message(HOST_MESSAGE, "Pop(): stack underflow!\n"); - return true; - } - - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) - return false; - - stackPointer--; - drivesRelative = drivesRelativeStack[stackPointer]; - axesRelative = axesRelativeStack[stackPointer]; - fileBeingPrinted = fileStack[stackPointer]; - platform->PopMessageIndent(); - // Remember for next time if we have just been switched - // to absolute drive moves - - for(int8_t i = AXES; i < DRIVES; i++) - lastPos[i - AXES] = moveBuffer[i]; - - // Do a null move to set the correct feedrate - - gFeedRate = feedrateStack[stackPointer]; - moveBuffer[DRIVES] = gFeedRate; - - checkEndStops = false; - moveAvailable = true; - return true; + if (stackPointer <= 0) + { + platform->Message(HOST_MESSAGE, "Pop(): stack underflow!\n"); + return true; + } + + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) + return false; + + stackPointer--; + drivesRelative = drivesRelativeStack[stackPointer]; + axesRelative = axesRelativeStack[stackPointer]; + fileBeingPrinted = fileStack[stackPointer]; + platform->PopMessageIndent(); + // Remember for next time if we have just been switched + // to absolute drive moves + + for (int8_t i = AXES; i < DRIVES; i++) + lastPos[i - AXES] = moveBuffer[i]; + + // Do a null move to set the correct feedrate + + gFeedRate = feedrateStack[stackPointer]; + moveBuffer[DRIVES] = gFeedRate; + + checkEndStops = noEndstopCheck; + moveAvailable = true; + return true; } // Move expects all axis movements to be absolute, and all @@ -311,122 +313,121 @@ bool GCodes::Pop() void GCodes::LoadMoveBufferFromGCode(GCodeBuffer *gb, bool doingG92, bool applyLimits) { - for(uint8_t i = 0; i < DRIVES; i++) + for (uint8_t i = 0; i < DRIVES; i++) { - if(i < AXES) - { - if(gb->Seen(gCodeLetters[i])) - { - float moveArg = gb->GetFValue()*distanceScale; - if (axesRelative && !doingG92) - { - moveArg += moveBuffer[i]; - } - if (applyLimits && i < 2 && axisIsHomed[i] && !doingG92) // limit X and Y moves unless doing G92 - { - if (moveArg < 0.0) - { - moveArg = 0.0; - } - else if (moveArg > platform->AxisLength(i)) - { - moveArg = platform->AxisLength(i); - } - } - moveBuffer[i] = moveArg; - if (doingG92) - { - axisIsHomed[i] = true; // doing a G92 is equivalent to homing the axis - } - } - } else - { - if(gb->Seen(gCodeLetters[i])) - { - float moveArg = gb->GetFValue()*distanceScale; - if(drivesRelative || doingG92) - moveBuffer[i] = moveArg; - else - { - float absE = moveArg; - moveBuffer[i] = absE - lastPos[i - AXES]; - lastPos[i - AXES] = absE; - } - } - } + if (i < AXES) + { + if (gb->Seen(gCodeLetters[i])) + { + float moveArg = gb->GetFValue() * distanceScale; + if (axesRelative && !doingG92) + { + moveArg += moveBuffer[i]; + } + if (applyLimits && i < 2 && axisIsHomed[i] && !doingG92) // limit X and Y moves unless doing G92 + { + if (moveArg < 0.0) + { + moveArg = 0.0; + } + else if (moveArg > platform->AxisLength(i)) + { + moveArg = platform->AxisLength(i); + } + } + moveBuffer[i] = moveArg; + if (doingG92) + { + axisIsHomed[i] = true; // doing a G92 is equivalent to homing the axis + } + } + } + else + { + if (gb->Seen(gCodeLetters[i])) + { + float moveArg = gb->GetFValue() * distanceScale; + if (drivesRelative || doingG92) + moveBuffer[i] = moveArg; + else + { + float absE = moveArg; + moveBuffer[i] = absE - lastPos[i - AXES]; + lastPos[i - AXES] = absE; + } + } + } } // Deal with feedrate - if(gb->Seen(gCodeLetters[DRIVES])) - gFeedRate = gb->GetFValue()*distanceScale*0.016666667; // G Code feedrates are in mm/minute; we need mm/sec + if (gb->Seen(gCodeLetters[DRIVES])) + gFeedRate = gb->GetFValue() * distanceScale * 0.016666667; // G Code feedrates are in mm/minute; we need mm/sec - moveBuffer[DRIVES] = gFeedRate; // We always set it, as Move may have modified the last one. + moveBuffer[DRIVES] = gFeedRate; // We always set it, as Move may have modified the last one. } - // This function is called for a G Code that makes a move. // If the Move class can't receive the move (i.e. things have to wait) // this returns false, otherwise true. bool GCodes::SetUpMove(GCodeBuffer *gb) { - // Last one gone yet? - - if(moveAvailable) - return false; - - // Load the last position; If Move can't accept more, return false - - if(!reprap.GetMove()->GetCurrentState(moveBuffer)) - return false; - - checkEndStops = false; - if(gb->Seen('S')) - { - if(gb->GetIValue() == 1) - checkEndStops = true; - } + // Last one gone yet? - LoadMoveBufferFromGCode(gb, false, !checkEndStops); + if (moveAvailable) + return false; - moveAvailable = true; - return true; + // Load the last position; If Move can't accept more, return false + + if (!reprap.GetMove()->GetCurrentState(moveBuffer)) + return false; + + checkEndStops = noEndstopCheck; + if (gb->Seen('S')) + { + if (gb->GetIValue() == 1) + checkEndStops = checkAtEndstop; + } + + LoadMoveBufferFromGCode(gb, false, checkEndStops == noEndstopCheck); + + moveAvailable = true; + return true; } // The Move class calls this function to find what to do next. -bool GCodes::ReadMove(float m[], bool& ce) +bool GCodes::ReadMove(float m[], EndstopMode& ce) { - if(!moveAvailable) - return false; - for(int8_t i = 0; i <= DRIVES; i++) // 1 more for feedrate - m[i] = moveBuffer[i]; - ce = checkEndStops; - moveAvailable = false; - checkEndStops = false; - return true; + if (!moveAvailable) + return false; + for (int8_t i = 0; i <= DRIVES; i++) // 1 more for feedrate + m[i] = moveBuffer[i]; + ce = checkEndStops; + moveAvailable = false; + checkEndStops = noEndstopCheck; + return true; } - bool GCodes::DoFileCannedCycles(const char* fileName) { // Have we started the file? - if(!doingCannedCycleFile) + if (!doingCannedCycleFile) { // No - if(!Push()) + if (!Push()) return false; fileBeingPrinted = platform->GetFileStore(platform->GetSysDir(), fileName, false); - if(fileBeingPrinted == NULL) + if (fileBeingPrinted == NULL) { platform->Message(HOST_MESSAGE, "Canned cycle GCode file not found - "); platform->Message(HOST_MESSAGE, fileName); platform->Message(HOST_MESSAGE, "\n"); - if(!Pop()) + if (!Pop()) platform->Message(HOST_MESSAGE, "Cannot pop the stack.\n"); return true; } @@ -437,11 +438,11 @@ bool GCodes::DoFileCannedCycles(const char* fileName) // Have we finished the file? - if(fileBeingPrinted == NULL) + if (fileBeingPrinted == NULL) { // Yes - if(!Pop()) + if (!Pop()) return false; doingCannedCycleFile = false; cannedCycleGCode->Init(); @@ -450,10 +451,10 @@ bool GCodes::DoFileCannedCycles(const char* fileName) // No - Do more of the file - if(!cannedCycleGCode->Finished()) + if (!cannedCycleGCode->Finished()) { cannedCycleGCode->SetFinished(ActOnGcode(cannedCycleGCode)); - return false; + return false; } doFilePrint(cannedCycleGCode); @@ -463,16 +464,16 @@ bool GCodes::DoFileCannedCycles(const char* fileName) bool GCodes::FileCannedCyclesReturn() { - if(!doingCannedCycleFile) + if (!doingCannedCycleFile) return true; - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) return false; doingCannedCycleFile = false; cannedCycleGCode->Init(); - if(fileBeingPrinted != NULL) + if (fileBeingPrinted != NULL) fileBeingPrinted->Close(); fileBeingPrinted = NULL; @@ -484,23 +485,24 @@ bool GCodes::FileCannedCyclesReturn() // be ignored. Recall that moveToDo[DRIVES] should contain the feedrate // you want (if action[DRIVES] is true). -bool GCodes::DoCannedCycleMove(bool ce) +bool GCodes::DoCannedCycleMove(EndstopMode ce) { // Is the move already running? - if(cannedCycleMoveQueued) + if (cannedCycleMoveQueued) { // Yes. - if(!Pop()) // Wait for the move to finish then restore the state + if (!Pop()) // Wait for the move to finish then restore the state return false; cannedCycleMoveQueued = false; return true; - } else + } + else { // No. - if(!Push()) // Wait for the RepRap to finish whatever it was doing, save it's state, and load moveBuffer[] with the current position. + if (!Push()) // Wait for the RepRap to finish whatever it was doing, save it's state, and load moveBuffer[] with the current position. return false; - for(int8_t drive = 0; drive <= DRIVES; drive++) + for (int8_t drive = 0; drive <= DRIVES; drive++) { - if(activeDrive[drive]) + if (activeDrive[drive]) moveBuffer[drive] = moveToDo[drive]; } checkEndStops = ce; @@ -514,7 +516,7 @@ bool GCodes::DoCannedCycleMove(bool ce) bool GCodes::SetPositions(GCodeBuffer *gb) { - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) return false; LoadMoveBufferFromGCode(gb, true, false); @@ -533,17 +535,18 @@ bool GCodes::SetPositions(GCodeBuffer *gb) bool GCodes::OffsetAxes(GCodeBuffer* gb) { - if(!offSetSet) + if (!offSetSet) { - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) - return false; - for(int8_t drive = 0; drive <= DRIVES; drive++) + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) + return false; + for (int8_t drive = 0; drive <= DRIVES; drive++) { - if(drive < AXES || drive == DRIVES) + if (drive < AXES || drive == DRIVES) { record[drive] = moveBuffer[drive]; moveToDo[drive] = moveBuffer[drive]; - } else + } + else { record[drive] = 0.0; moveToDo[drive] = 0.0; @@ -551,16 +554,16 @@ bool GCodes::OffsetAxes(GCodeBuffer* gb) activeDrive[drive] = false; } - for(int8_t axis = 0; axis < AXES; axis++) + for (int8_t axis = 0; axis < AXES; axis++) { - if(gb->Seen(gCodeLetters[axis])) + if (gb->Seen(gCodeLetters[axis])) { moveToDo[axis] += gb->GetFValue(); activeDrive[axis] = true; } } - if(gb->Seen(gCodeLetters[DRIVES])) // Has the user specified a feedrate? + if (gb->Seen(gCodeLetters[DRIVES])) // Has the user specified a feedrate? { moveToDo[DRIVES] = gb->GetFValue(); activeDrive[DRIVES] = true; @@ -569,13 +572,12 @@ bool GCodes::OffsetAxes(GCodeBuffer* gb) offSetSet = true; } - - if(DoCannedCycleMove(false)) + if (DoCannedCycleMove(noEndstopCheck)) { //LoadMoveBufferFromArray(record); - for(int drive = 0; drive <= DRIVES; drive++) + for (int drive = 0; drive <= DRIVES; drive++) moveBuffer[drive] = record[drive]; - reprap.GetMove()->SetLiveCoordinates(record); // This doesn't transform record + reprap.GetMove()->SetLiveCoordinates(record); // This doesn't transform record reprap.GetMove()->SetPositions(record); // This does offSetSet = false; return true; @@ -592,9 +594,9 @@ bool GCodes::OffsetAxes(GCodeBuffer* gb) bool GCodes::DoHome(char* reply, bool& error) //pre(reply.upb == STRING_LENGTH) { - if(homeX && homeY && homeZ) + if (homeX && homeY && homeZ) { - if(DoFileCannedCycles(HOME_ALL_G)) + if (DoFileCannedCycles(HOME_ALL_G)) { homeAxisMoveCount = 0; homeX = false; @@ -606,9 +608,9 @@ bool GCodes::DoHome(char* reply, bool& error) return false; } - if(homeX) + if (homeX) { - if(DoFileCannedCycles(HOME_X_G)) + if (DoFileCannedCycles(HOME_X_G)) { homeAxisMoveCount = 0; homeX = false; @@ -618,10 +620,9 @@ bool GCodes::DoHome(char* reply, bool& error) return false; } - - if(homeY) + if (homeY) { - if(DoFileCannedCycles(HOME_Y_G)) + if (DoFileCannedCycles(HOME_Y_G)) { homeAxisMoveCount = 0; homeY = false; @@ -631,18 +632,17 @@ bool GCodes::DoHome(char* reply, bool& error) return false; } - - if(homeZ) + if (homeZ) { - if (!(axisIsHomed[X_AXIS] && axisIsHomed[Y_AXIS])) + if (platform->MustHomeXYBeforeZ() && (!axisIsHomed[X_AXIS] || !axisIsHomed[Y_AXIS])) { - // We can only home Z if X and Y have already been homed. Possibly this should only be if we are using an IR probe. + // We can only home Z if X and Y have already been homed strncpy(reply, "Must home X and Y before homing Z", STRING_LENGTH); error = true; homeZ = false; return true; } - if(DoFileCannedCycles(HOME_Z_G)) + if (DoFileCannedCycles(HOME_Z_G)) { homeAxisMoveCount = 0; homeZ = false; @@ -654,7 +654,7 @@ bool GCodes::DoHome(char* reply, bool& error) // Should never get here - checkEndStops = false; + checkEndStops = noEndstopCheck; moveAvailable = false; homeAxisMoveCount = 0; @@ -667,23 +667,25 @@ bool GCodes::DoHome(char* reply, bool& error) bool GCodes::DoSingleZProbeAtPoint() { - float x, y, z; + reprap.GetMove()->SetIdentityTransform(); // It doesn't matter if these are called repeatedly - reprap.GetMove()->SetIdentityTransform(); // It doesn't matter if these are called repeatedly - - for(int8_t drive = 0; drive <= DRIVES; drive++) - activeDrive[drive] = false; - - switch(cannedCycleMoveCount) + for (int8_t drive = 0; drive <= DRIVES; drive++) { - case 0: // This only does anything on the first move; on all the others Z is already there + activeDrive[drive] = false; + } + + switch (cannedCycleMoveCount) + { + case 0: // This only does anything on the first move; on all the others Z is already there moveToDo[Z_AXIS] = Z_DIVE; activeDrive[Z_AXIS] = true; moveToDo[DRIVES] = platform->HomeFeedRate(Z_AXIS); activeDrive[DRIVES] = true; - reprap.GetMove()->SetZProbing(false); - if(DoCannedCycleMove(false)) + if (DoCannedCycleMove(noEndstopCheck)) + { cannedCycleMoveCount++; + reprap.GetMove()->SetZProbing(true); // we only want to call this once + } return false; case 1: @@ -694,31 +696,52 @@ bool GCodes::DoSingleZProbeAtPoint() moveToDo[DRIVES] = platform->HomeFeedRate(X_AXIS); activeDrive[DRIVES] = true; reprap.GetMove()->SetZProbing(false); - if(DoCannedCycleMove(false)) + if (DoCannedCycleMove(noEndstopCheck)) + { cannedCycleMoveCount++; + } return false; case 2: - moveToDo[Z_AXIS] = -2.0*platform->AxisLength(Z_AXIS); + moveToDo[Z_AXIS] = -2.0 * platform->AxisLength(Z_AXIS); activeDrive[Z_AXIS] = true; moveToDo[DRIVES] = platform->HomeFeedRate(Z_AXIS); activeDrive[DRIVES] = true; reprap.GetMove()->SetZProbing(true); - if(DoCannedCycleMove(true)) + if (DoCannedCycleMove(checkApproachingEndstop)) { cannedCycleMoveCount++; - axisIsHomed[Z_AXIS] = true; // we now home the Z-axis in Move.cpp it is wasn't already } return false; case 3: + { + float liveCoordinates[DRIVES + 1]; + reprap.GetMove()->LiveCoordinates(liveCoordinates); + moveToDo[Z_AXIS] = liveCoordinates[Z_AXIS] - 1.0; // move down at most another 1mm + } + activeDrive[Z_AXIS] = true; + moveToDo[DRIVES] = platform->HomeFeedRate(Z_AXIS) * 0.2; + activeDrive[DRIVES] = true; + reprap.GetMove()->SetZProbing(true); + if (DoCannedCycleMove(checkAtEndstop)) + { + cannedCycleMoveCount++; + axisIsHomed[Z_AXIS] = true; // we now home the Z-axis in Move.cpp it is wasn't already + platform->SetZProbing(false); + } + return false; + + case 4: moveToDo[Z_AXIS] = Z_DIVE; activeDrive[Z_AXIS] = true; moveToDo[DRIVES] = platform->HomeFeedRate(Z_AXIS); activeDrive[DRIVES] = true; reprap.GetMove()->SetZProbing(false); - if(DoCannedCycleMove(false)) + if (DoCannedCycleMove(noEndstopCheck)) + { cannedCycleMoveCount++; + } return false; default: @@ -728,29 +751,55 @@ bool GCodes::DoSingleZProbeAtPoint() } } - // This simply moves down till the Z probe/switch is triggered. bool GCodes::DoSingleZProbe() { - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + for (int8_t drive = 0; drive <= DRIVES; drive++) + { + activeDrive[drive] = false; + } + + switch (cannedCycleMoveCount) + { + case 0: + platform->SetZProbing(true); // we only want to call this once + ++cannedCycleMoveCount; return false; - for(int8_t drive = 0; drive <= DRIVES; drive++) - activeDrive[drive] = false; + case 1: + moveToDo[Z_AXIS] = -1.1 * platform->AxisLength(Z_AXIS); + activeDrive[Z_AXIS] = true; + moveToDo[DRIVES] = platform->HomeFeedRate(Z_AXIS); + activeDrive[DRIVES] = true; + if (DoCannedCycleMove(checkApproachingEndstop)) + { + cannedCycleMoveCount++; + } + return false; - moveToDo[Z_AXIS] = -1.1*platform->AxisLength(Z_AXIS); - activeDrive[Z_AXIS] = true; - moveToDo[DRIVES] = platform->HomeFeedRate(Z_AXIS); - activeDrive[DRIVES] = true; - if(DoCannedCycleMove(true)) - { + case 2: + { + float liveCoordinates[DRIVES + 1]; + reprap.GetMove()->LiveCoordinates(liveCoordinates); + moveToDo[Z_AXIS] = liveCoordinates[Z_AXIS] - 1.0; // move down at most another 1mm + } + activeDrive[Z_AXIS] = true; + moveToDo[DRIVES] = platform->HomeFeedRate(Z_AXIS) * 0.2; + activeDrive[DRIVES] = true; + if (DoCannedCycleMove(checkAtEndstop)) + { + cannedCycleMoveCount++; + probeCount = 0; + axisIsHomed[Z_AXIS] = true; // we have homed the Z axis + platform->SetZProbing(false); + } + return false; + + default: cannedCycleMoveCount = 0; - probeCount = 0; - axisIsHomed[Z_AXIS] = true; // we have homed the Z axis return true; } - return false; } // This sets wherever we are as the probe point P (probePointIndex) @@ -763,24 +812,24 @@ bool GCodes::DoSingleZProbe() bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer *gb) { - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) return false; - if(!gb->Seen('P')) + if (!gb->Seen('P')) return DoSingleZProbe(); int probePointIndex = gb->GetIValue(); float x, y, z; - if(gb->Seen(gCodeLetters[X_AXIS])) + if (gb->Seen(gCodeLetters[X_AXIS])) x = gb->GetFValue(); else x = moveBuffer[X_AXIS]; - if(gb->Seen(gCodeLetters[Y_AXIS])) + if (gb->Seen(gCodeLetters[Y_AXIS])) y = gb->GetFValue(); else y = moveBuffer[Y_AXIS]; - if(gb->Seen(gCodeLetters[Z_AXIS])) + if (gb->Seen(gCodeLetters[Z_AXIS])) z = gb->GetFValue(); else z = moveBuffer[Z_AXIS]; @@ -789,24 +838,25 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer *gb) reprap.GetMove()->SetXBedProbePoint(probeCount, x); reprap.GetMove()->SetYBedProbePoint(probeCount, y); - if(z > SILLY_Z_VALUE) + if (z > SILLY_Z_VALUE) { reprap.GetMove()->SetZBedProbePoint(probeCount, z); reprap.GetMove()->SetZProbing(false); // Not really needed, but let's be safe probeCount = 0; - if(gb->Seen('S')) + if (gb->Seen('S')) { zProbesSet = true; reprap.GetMove()->SetProbedBedEquation(); } return true; - } else + } + else { - if(DoSingleZProbeAtPoint()) + if (DoSingleZProbeAtPoint()) { probeCount = 0; reprap.GetMove()->SetZProbing(false); - if(gb->Seen('S')) + if (gb->Seen('S')) { zProbesSet = true; reprap.GetMove()->SetProbedBedEquation(); @@ -824,15 +874,15 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer *gb) bool GCodes::DoMultipleZProbe() { - if(reprap.GetMove()->NumberOfXYProbePoints() < 3) + if (reprap.GetMove()->NumberOfXYProbePoints() < 3) { platform->Message(HOST_MESSAGE, "Bed probing: there needs to be 3 or more points set.\n"); return true; } - if(DoSingleZProbeAtPoint()) + if (DoSingleZProbeAtPoint()) probeCount++; - if(probeCount >= reprap.GetMove()->NumberOfXYProbePoints()) + if (probeCount >= reprap.GetMove()->NumberOfXYProbePoints()) { probeCount = 0; zProbesSet = true; @@ -857,22 +907,49 @@ bool GCodes::GetProbeCoordinates(int count, float& x, float& y, float& z) bool GCodes::SetPrintZProbe(GCodeBuffer* gb, char* reply) { - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) return false; - if(gb->Seen(gCodeLetters[Z_AXIS])) + + if (gb->Seen(gCodeLetters[Z_AXIS])) { - platform->SetZProbeStopHeight(gb->GetFValue()); - if(gb->Seen('P')) + ZProbeParameters params; + platform->GetZProbeParameters(params); + params.height = gb->GetFValue(); + if (gb->Seen('P')) { - platform->SetZProbe(gb->GetIValue()); + params.adcValue = gb->GetIValue(); } - } else if (platform->GetZProbeType() == 2) - { - snprintf(reply, STRING_LENGTH, "%d (%d)", platform->ZProbe(), platform->ZProbeOnVal()); + if (gb->Seen('T')) + { + params.calibTemperature = gb->GetFValue(); + } + else if (!PrintingAFile()) + { + // Use the current bed temperature as the calibration temperature if no value was provided + params.calibTemperature = platform->GetTemperature(0); + } + if (gb->Seen('C')) + { + params.temperatureCoefficient = gb->GetFValue(); + } + platform->SetZProbeParameters(params); } else { - snprintf(reply, STRING_LENGTH, "%d", platform->ZProbe()); + int v0 = platform->ZProbe(); + int v1, v2; + switch(platform->GetZProbeSecondaryValues(v1, v2)) + { + case 1: + snprintf(reply, STRING_LENGTH, "%d (%d)", v0, v1); + break; + case 2: + snprintf(reply, STRING_LENGTH, "%d (%d, %d)", v0, v1, v2); + break; + default: + snprintf(reply, STRING_LENGTH, "%d", v0); + break; + } } return true; } @@ -885,17 +962,18 @@ bool GCodes::SetPrintZProbe(GCodeBuffer* gb, char* reply) char* GCodes::GetCurrentCoordinates() { - float liveCoordinates[DRIVES+1]; + float liveCoordinates[DRIVES + 1]; reprap.GetMove()->LiveCoordinates(liveCoordinates); - snprintf(scratchString, STRING_LENGTH, "X:%f Y:%f Z:%f E:%f", liveCoordinates[X_AXIS], liveCoordinates[Y_AXIS], liveCoordinates[Z_AXIS], liveCoordinates[AXES]); + snprintf(scratchString, STRING_LENGTH, "X:%f Y:%f Z:%f E:%f", liveCoordinates[X_AXIS], liveCoordinates[Y_AXIS], + liveCoordinates[Z_AXIS], liveCoordinates[AXES]); return scratchString; } void GCodes::OpenFileToWrite(const char* directory, const char* fileName, GCodeBuffer *gb) { fileBeingWritten = platform->GetFileStore(directory, fileName, true); - if(fileBeingWritten == NULL) + if (fileBeingWritten == NULL) { platform->Message(HOST_MESSAGE, "Can't open GCode file for writing.\n"); } @@ -911,7 +989,7 @@ void GCodes::WriteHTMLToFile(char b, GCodeBuffer *gb) char reply[1]; reply[0] = 0; - if(fileBeingWritten == NULL) + if (fileBeingWritten == NULL) { platform->Message(HOST_MESSAGE, "Attempt to write to a null file.\n"); return; @@ -919,21 +997,22 @@ void GCodes::WriteHTMLToFile(char b, GCodeBuffer *gb) fileBeingWritten->Write(b); - if(b == eofString[eofStringCounter]) + if (b == eofString[eofStringCounter]) { eofStringCounter++; - if(eofStringCounter >= eofStringLength) + if (eofStringCounter >= eofStringLength) { fileBeingWritten->Close(); fileBeingWritten = NULL; gb->SetWritingFileDirectory(NULL); char* r = reply; - if(platform->Emulating() == marlin) + if (platform->Emulating() == marlin) r = "Done saving file."; - HandleReply(false, gb == serialGCode , r, 'M', 560, false); + HandleReply(false, gb == serialGCode, r, 'M', 560, false); return; } - } else + } + else eofStringCounter = 0; } @@ -942,7 +1021,7 @@ void GCodes::WriteGCodeToFile(GCodeBuffer *gb) char reply[1]; reply[0] = 0; - if(fileBeingWritten == NULL) + if (fileBeingWritten == NULL) { platform->Message(HOST_MESSAGE, "Attempt to write to a null file.\n"); return; @@ -950,31 +1029,31 @@ void GCodes::WriteGCodeToFile(GCodeBuffer *gb) // End of file? - if(gb->Seen('M')) + if (gb->Seen('M')) { - if(gb->GetIValue() == 29) + if (gb->GetIValue() == 29) { fileBeingWritten->Close(); fileBeingWritten = NULL; gb->SetWritingFileDirectory(NULL); char* r = reply; - if(platform->Emulating() == marlin) + if (platform->Emulating() == marlin) r = "Done saving file."; - HandleReply(false, gb == serialGCode , r, 'M', 29, false); + HandleReply(false, gb == serialGCode, r, 'M', 29, false); return; } } // Resend request? - if(gb->Seen('G')) + if (gb->Seen('G')) { - if(gb->GetIValue() == 998) + if (gb->GetIValue() == 998) { - if(gb->Seen('P')) + if (gb->Seen('P')) { snprintf(scratchString, STRING_LENGTH, "%s", gb->GetIValue()); - HandleReply(false, gb == serialGCode , scratchString, 'G', 998, true); + HandleReply(false, gb == serialGCode, scratchString, 'G', 998, true); return; } } @@ -982,40 +1061,40 @@ void GCodes::WriteGCodeToFile(GCodeBuffer *gb) fileBeingWritten->Write(gb->Buffer()); fileBeingWritten->Write('\n'); - HandleReply(false, gb == serialGCode , reply, 'G', 1, false); + HandleReply(false, gb == serialGCode, reply, 'G', 1, false); } // Set up a file to print, but don't print it yet. void GCodes::QueueFileToPrint(const char* fileName) { - fileToPrint = platform->GetFileStore(platform->GetGCodeDir(), fileName, false); - if(fileToPrint == NULL) - { - webserver->HandleReply("GCode file not found", true); - platform->Message(HOST_MESSAGE, "GCode file not found\n"); - } + fileToPrint = platform->GetFileStore(platform->GetGCodeDir(), fileName, false); + if (fileToPrint == NULL) + { + webserver->HandleReply("GCode file not found", true); + platform->Message(HOST_MESSAGE, "GCode file not found\n"); + } } void GCodes::DeleteFile(const char* fileName) { - if(!platform->GetMassStorage()->Delete(platform->GetGCodeDir(), fileName)) - { - platform->Message(HOST_MESSAGE, "Unsuccessful attempt to delete: "); - platform->Message(HOST_MESSAGE, fileName); - platform->Message(HOST_MESSAGE, "\n"); - webserver->HandleReply("Failed to delete file", true); - } + if (!platform->GetMassStorage()->Delete(platform->GetGCodeDir(), fileName)) + { + platform->Message(HOST_MESSAGE, "Unsuccessful attempt to delete: "); + platform->Message(HOST_MESSAGE, fileName); + platform->Message(HOST_MESSAGE, "\n"); + webserver->HandleReply("Failed to delete file", true); + } } // Send the config file to USB in response to an M503 command. // This is not used for processing M503 requests received via the webserver. bool GCodes::SendConfigToLine() { - if(configFile == NULL) + if (configFile == NULL) { configFile = platform->GetFileStore(platform->GetSysDir(), platform->GetConfigFile(), false); - if(configFile == NULL) + if (configFile == NULL) { platform->Message(HOST_MESSAGE, "Configuration file not found\n"); return true; @@ -1024,10 +1103,10 @@ bool GCodes::SendConfigToLine() } char b; - while(configFile->Read(b)) + while (configFile->Read(b)) { platform->GetLine()->Write(b); - if(b == '\n') + if (b == '\n') return false; } @@ -1037,40 +1116,39 @@ bool GCodes::SendConfigToLine() return true; } - // Function to handle dwell delays. Return true for // dwell finished, false otherwise. bool GCodes::DoDwell(GCodeBuffer *gb) { - if(!gb->Seen('P')) - return true; // No time given - throw it away - - float dwell = 0.001*(float)gb->GetLValue(); // P values are in milliseconds; we need seconds - - // Wait for all the queued moves to stop - - if(!reprap.GetMove()->AllMovesAreFinished()) - return false; - - // Are we already in the dwell? - - if(dwellWaiting) - { - if(platform->Time() - dwellTime >= 0.0) - { - dwellWaiting = false; - reprap.GetMove()->ResumeMoving(); - return true; - } - return false; - } - - // New dwell - set it up - - dwellWaiting = true; - dwellTime = platform->Time() + dwell; - return false; + if (!gb->Seen('P')) + return true; // No time given - throw it away + + float dwell = 0.001 * (float) gb->GetLValue(); // P values are in milliseconds; we need seconds + + // Wait for all the queued moves to stop + + if (!reprap.GetMove()->AllMovesAreFinished()) + return false; + + // Are we already in the dwell? + + if (dwellWaiting) + { + if (platform->Time() - dwellTime >= 0.0) + { + dwellWaiting = false; + reprap.GetMove()->ResumeMoving(); + return true; + } + return false; + } + + // New dwell - set it up + + dwellWaiting = true; + dwellTime = platform->Time() + dwell; + return false; } // Set distance offsets and working and standby temperatures for @@ -1078,27 +1156,27 @@ bool GCodes::DoDwell(GCodeBuffer *gb) bool GCodes::SetOffsets(GCodeBuffer *gb) { - int8_t head; - if(gb->Seen('P')) - { - head = gb->GetIValue() + 1; // 0 is the Bed - if(gb->Seen('R')) - reprap.GetHeat()->SetStandbyTemperature(head, gb->GetFValue()); - - if(gb->Seen('S')) - reprap.GetHeat()->SetActiveTemperature(head, gb->GetFValue()); - // FIXME - do X, Y and Z - } - return true; + int8_t head; + if (gb->Seen('P')) + { + head = gb->GetIValue() + 1; // 0 is the Bed + if (gb->Seen('R')) + reprap.GetHeat()->SetStandbyTemperature(head, gb->GetFValue()); + + if (gb->Seen('S')) + reprap.GetHeat()->SetActiveTemperature(head, gb->GetFValue()); + // FIXME - do X, Y and Z + } + return true; } // Does what it says. bool GCodes::DisableDrives() { - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) - return false; - for(int8_t drive = 0; drive < DRIVES; drive++) + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) + return false; + for (int8_t drive = 0; drive < DRIVES; drive++) platform->Disable(drive); return true; } @@ -1107,9 +1185,9 @@ bool GCodes::DisableDrives() bool GCodes::StandbyHeaters() { - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) return false; - for(int8_t heater = 0; heater < HEATERS; heater++) + for (int8_t heater = 0; heater < HEATERS; heater++) reprap.GetHeat()->Standby(heater); selectedHead = -1; return true; @@ -1122,13 +1200,13 @@ void GCodes::SetEthernetAddress(GCodeBuffer *gb, int mCode) uint8_t sp = 0; uint8_t spp = 0; uint8_t ipp = 0; - while(ipString[sp]) + while (ipString[sp]) { - if(ipString[sp] == '.') + if (ipString[sp] == '.') { eth[ipp] = atoi(&ipString[spp]); ipp++; - if(ipp > 3) + if (ipp > 3) { platform->Message(HOST_MESSAGE, "Dud IP address: "); platform->Message(HOST_MESSAGE, gb->Buffer()); @@ -1137,13 +1215,14 @@ void GCodes::SetEthernetAddress(GCodeBuffer *gb, int mCode) } sp++; spp = sp; - }else + } + else sp++; } eth[ipp] = atoi(&ipString[spp]); - if(ipp == 3) + if (ipp == 3) { - switch(mCode) + switch (mCode) { case 552: platform->SetIPAddress(eth); @@ -1158,7 +1237,8 @@ void GCodes::SetEthernetAddress(GCodeBuffer *gb, int mCode) default: platform->Message(HOST_MESSAGE, "Setting ether parameter - dud code."); } - } else + } + else { platform->Message(HOST_MESSAGE, "Dud IP address: "); platform->Message(HOST_MESSAGE, gb->Buffer()); @@ -1168,28 +1248,28 @@ void GCodes::SetEthernetAddress(GCodeBuffer *gb, int mCode) void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOrT, int code, bool resend) { - if (gMOrT != 'M' || code != 111) // web server reply for M111 is handled before we get here + if (gMOrT != 'M' || code != 111) // web server reply for M111 is handled before we get here { webserver->HandleReply(reply, error); } Compatibility c = platform->Emulating(); - if(!fromLine) + if (!fromLine) c = me; const char* response = "ok"; - if(resend) + if (resend) response = "rs "; const char* s = 0; - switch(c) + switch (c) { case me: case reprapFirmware: - if(!reply[0]) + if (!reply[0]) return; - if(error) + if (error) platform->GetLine()->Write("Error: "); platform->GetLine()->Write(reply); platform->GetLine()->Write("\n"); @@ -1197,7 +1277,7 @@ void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOr case marlin: - if(gMOrT == 'M' && code == 20) + if (gMOrT == 'M' && code == 20) { platform->GetLine()->Write("Begin file list\n"); platform->GetLine()->Write(reply); @@ -1207,7 +1287,7 @@ void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOr return; } - if(gMOrT == 'M' && code == 28) + if (gMOrT == 'M' && code == 28) { platform->GetLine()->Write(response); platform->GetLine()->Write("\n"); @@ -1216,7 +1296,7 @@ void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOr return; } - if( (gMOrT == 'M' && code == 105) || (gMOrT == 'G' && code == 998)) + if ((gMOrT == 'M' && code == 105) || (gMOrT == 'G' && code == 998)) { platform->GetLine()->Write(response); platform->GetLine()->Write(" "); @@ -1225,7 +1305,7 @@ void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOr return; } - if(reply[0]) + if (reply[0]) { platform->GetLine()->Write(reply); platform->GetLine()->Write("\n"); @@ -1247,7 +1327,7 @@ void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOr s = "unknown"; } - if(s != 0) + if (s != 0) { snprintf(scratchString, STRING_LENGTH, "Emulation of %s is not yet supported.\n", s); platform->Message(HOST_MESSAGE, scratchString); @@ -1260,538 +1340,543 @@ void GCodes::HandleReply(bool error, bool fromLine, const char* reply, char gMOr bool GCodes::ActOnGcode(GCodeBuffer *gb) { - int code; - bool result = true; - bool error = false; - bool resend = false; - bool seen; - char reply[STRING_LENGTH]; + int code; + bool result = true; + bool error = false; + bool resend = false; + bool seen; + char reply[STRING_LENGTH]; - reply[0] = 0; + reply[0] = 0; - if(gb->Seen('G')) - { - code = gb->GetIValue(); - switch(code) - { - case 0: // There are no rapid moves... - case 1: // Ordinary move - result = SetUpMove(gb); - break; - - case 4: // Dwell - result = DoDwell(gb); - break; - - case 10: // Set offsets - result = SetOffsets(gb); - break; - - case 20: // Inches (which century are we living in, here?) - distanceScale = INCH_TO_MM; - break; - - case 21: // mm - distanceScale = 1.0; - break; - - case 28: // Home - if(NoHome()) - { - homeAxisMoveCount = 0; - homeX = gb->Seen(gCodeLetters[X_AXIS]); - homeY = gb->Seen(gCodeLetters[Y_AXIS]); - homeZ = gb->Seen(gCodeLetters[Z_AXIS]); - if(NoHome()) - { - homeX = true; - homeY = true; - homeZ = true; - } - } - result = DoHome(reply, error); - break; - - case 30: // Z probe/manually set at a position and set that as point P - result = SetSingleZProbeAtAPosition(gb); - break; - - case 31: // Return the probe value, or set probe variables - result = SetPrintZProbe(gb, reply); - break; - - case 32: // Probe Z at multiple positions and generate the bed transform - if (!(axisIsHomed[X_AXIS] && axisIsHomed[Y_AXIS])) + if (gb->Seen('G')) + { + code = gb->GetIValue(); + switch (code) { - // We can only do a Z probe if X and Y have already been homed - strncpy(reply, "Must home X and Y before bed probing", STRING_LENGTH); + case 0: // There are no rapid moves... + case 1: // Ordinary move + result = SetUpMove(gb); + break; + + case 4: // Dwell + result = DoDwell(gb); + break; + + case 10: // Set offsets + result = SetOffsets(gb); + break; + + case 20: // Inches (which century are we living in, here?) + distanceScale = INCH_TO_MM; + break; + + case 21: // mm + distanceScale = 1.0; + break; + + case 28: // Home + if (NoHome()) + { + homeAxisMoveCount = 0; + homeX = gb->Seen(gCodeLetters[X_AXIS]); + homeY = gb->Seen(gCodeLetters[Y_AXIS]); + homeZ = gb->Seen(gCodeLetters[Z_AXIS]); + if (NoHome()) + { + homeX = true; + homeY = true; + homeZ = true; + } + } + result = DoHome(reply, error); + break; + + case 30: // Z probe/manually set at a position and set that as point P + result = SetSingleZProbeAtAPosition(gb); + break; + + case 31: // Return the probe value, or set probe variables + result = SetPrintZProbe(gb, reply); + break; + + case 32: // Probe Z at multiple positions and generate the bed transform + if (!(axisIsHomed[X_AXIS] && axisIsHomed[Y_AXIS])) + { + // We can only do bed levelling if X and Y have already been homed + strncpy(reply, "Must home X and Y before bed probing", STRING_LENGTH); + error = true; + result = true; + } + else + { + result = DoMultipleZProbe(); + } + break; + + case 90: // Absolute coordinates + drivesRelative = false; + axesRelative = false; + break; + + case 91: // Relative coordinates + drivesRelative = true; // Non-axis movements (i.e. extruders) + axesRelative = true; // Axis movements (i.e. X, Y and Z) + break; + + case 92: // Set position + result = SetPositions(gb); + break; + + default: error = true; - result = true; + snprintf(reply, STRING_LENGTH, "invalid G Code: %s", gb->Buffer()); } - else + if (result) + HandleReply(error, gb == serialGCode, reply, 'G', code, resend); + return result; + } + + if (gb->Seen('M')) + { + code = gb->GetIValue(); + switch (code) { - result = DoMultipleZProbe(); - } - break; + case 0: // Stop + case 1: // Sleep + if (fileBeingPrinted != NULL) + { + fileToPrint = fileBeingPrinted; + fileBeingPrinted = NULL; + } + if (!DisableDrives()) + return false; + if (!StandbyHeaters()) + return false; // Should never happen + break; - case 90: // Absolute coordinates - drivesRelative = false; - axesRelative = false; - break; - - case 91: // Relative coordinates - drivesRelative = true; // Non-axis movements (i.e. extruders) - axesRelative = true; // Axis movements (i.e. X, Y and Z) - break; - - case 92: // Set position - result = SetPositions(gb); - break; - - default: - error = true; - snprintf(reply, STRING_LENGTH, "invalid G Code: %s", gb->Buffer()); - } - if(result) - HandleReply(error, gb == serialGCode, reply, 'G', code, resend); - return result; - } - - if(gb->Seen('M')) - { - code = gb->GetIValue(); - switch(code) - { - case 0: // Stop - case 1: // Sleep - if(fileBeingPrinted != NULL) - { - fileToPrint = fileBeingPrinted; - fileBeingPrinted = NULL; - } - if(!DisableDrives()) - return false; - if(!StandbyHeaters()) - return false; // Should never happen - break; - - case 18: // Motors off - result = DisableDrives(); - break; - - case 20: // Deprecated... - if(platform->Emulating() == me || platform->Emulating() == reprapFirmware) - snprintf(reply, STRING_LENGTH, "GCode files:\n%s", platform->GetMassStorage()->FileList(platform->GetGCodeDir(), gb == serialGCode)); - else - snprintf(reply, STRING_LENGTH, "%s", platform->GetMassStorage()->FileList(platform->GetGCodeDir(), gb == serialGCode)); - break; + case 18: // Motors off + result = DisableDrives(); + break; - case 21: // Initialise SD - ignore - break; + case 20: // Deprecated... + if (platform->Emulating() == me || platform->Emulating() == reprapFirmware) + snprintf(reply, STRING_LENGTH, "GCode files:\n%s", + platform->GetMassStorage()->FileList(platform->GetGCodeDir(), gb == serialGCode)); + else + snprintf(reply, STRING_LENGTH, "%s", + platform->GetMassStorage()->FileList(platform->GetGCodeDir(), gb == serialGCode)); + break; - case 23: // Set file to print - QueueFileToPrint(gb->GetUnprecedentedString()); - if(platform->Emulating() == marlin) - snprintf(reply, STRING_LENGTH, "%s", "File opened\nFile selected\n"); - break; - - case 24: // Print/resume-printing the selected file - if(fileBeingPrinted != NULL) - break; - fileBeingPrinted = fileToPrint; - fileToPrint = NULL; - break; - - case 25: // Pause the print - fileToPrint = fileBeingPrinted; - fileBeingPrinted = NULL; - break; + case 21: // Initialise SD - ignore + break; - case 27: // Report print status - Deprecated - if(this->PrintingAFile()) - strncpy(reply, "SD printing.", STRING_LENGTH); - else - strncpy(reply, "Not SD printing.", STRING_LENGTH); - break; + case 23: // Set file to print + QueueFileToPrint(gb->GetUnprecedentedString()); + if (platform->Emulating() == marlin) + snprintf(reply, STRING_LENGTH, "%s", "File opened\nFile selected\n"); + break; - case 28: // Write to file - { + case 24: // Print/resume-printing the selected file + if (fileBeingPrinted != NULL) + break; + fileBeingPrinted = fileToPrint; + fileToPrint = NULL; + break; + + case 25: // Pause the print + fileToPrint = fileBeingPrinted; + fileBeingPrinted = NULL; + break; + + case 27: // Report print status - Deprecated + if (this->PrintingAFile()) + strncpy(reply, "SD printing.", STRING_LENGTH); + else + strncpy(reply, "Not SD printing.", STRING_LENGTH); + break; + + case 28: // Write to file + { const char* str = gb->GetUnprecedentedString(); OpenFileToWrite(platform->GetGCodeDir(), str, gb); snprintf(reply, STRING_LENGTH, "Writing to file: %s", str); - } - break; + } + break; - case 29: // End of file being written; should be intercepted before getting here - platform->Message(HOST_MESSAGE, "GCode end-of-file being interpreted.\n"); - break; + case 29: // End of file being written; should be intercepted before getting here + platform->Message(HOST_MESSAGE, "GCode end-of-file being interpreted.\n"); + break; - case 30: // Delete file - DeleteFile(gb->GetUnprecedentedString()); - break; + case 30: // Delete file + DeleteFile(gb->GetUnprecedentedString()); + break; - case 82: - for(int8_t extruder = AXES; extruder < DRIVES; extruder++) - lastPos[extruder - AXES] = 0.0; - drivesRelative = false; - break; + case 82: + for (int8_t extruder = AXES; extruder < DRIVES; extruder++) + lastPos[extruder - AXES] = 0.0; + drivesRelative = false; + break; - case 83: - for(int8_t extruder = AXES; extruder < DRIVES; extruder++) - lastPos[extruder - AXES] = 0.0; - drivesRelative = true; + case 83: + for (int8_t extruder = AXES; extruder < DRIVES; extruder++) + lastPos[extruder - AXES] = 0.0; + drivesRelative = true; - break; + break; - case 84: // Motors off - deprecated, use M18 - result = DisableDrives(); - break; + case 84: // Motors off - deprecated, use M18 + result = DisableDrives(); + break; - case 85: // Set inactive time - break; + case 85: // Set inactive time + break; - case 92: // Set/report steps/mm for some axes - seen = false; - for(int8_t drive = 0; drive < DRIVES; drive++) - if(gb->Seen(gCodeLetters[drive])) - { - platform->SetDriveStepsPerUnit(drive, gb->GetFValue()); - seen = true; - } - reprap.GetMove()->SetStepHypotenuse(); - if(!seen) - snprintf(reply, STRING_LENGTH, "Steps/mm: X: %d, Y: %d, Z: %d, E: %d", - (int)platform->DriveStepsPerUnit(X_AXIS), (int)platform->DriveStepsPerUnit(Y_AXIS), - (int)platform->DriveStepsPerUnit(Z_AXIS), (int)platform->DriveStepsPerUnit(AXES)); // FIXME - needs to do multiple extruders - break; + case 92: // Set/report steps/mm for some axes + seen = false; + for (int8_t drive = 0; drive < DRIVES; drive++) + if (gb->Seen(gCodeLetters[drive])) + { + platform->SetDriveStepsPerUnit(drive, gb->GetFValue()); + seen = true; + } + reprap.GetMove()->SetStepHypotenuse(); + if (!seen) + snprintf(reply, STRING_LENGTH, "Steps/mm: X: %d, Y: %d, Z: %d, E: %d", + (int) platform->DriveStepsPerUnit(X_AXIS), (int) platform->DriveStepsPerUnit(Y_AXIS), + (int) platform->DriveStepsPerUnit(Z_AXIS), (int) platform->DriveStepsPerUnit(AXES)); // FIXME - needs to do multiple extruders + break; + case 98: + if (gb->Seen('P')) + result = DoFileCannedCycles(gb->GetString()); + break; - case 98: - if(gb->Seen('P')) - result = DoFileCannedCycles(gb->GetString()); - break; + case 99: + result = FileCannedCyclesReturn(); + break; - case 99: - result = FileCannedCyclesReturn(); - break; + case 104: // Deprecated + if (gb->Seen('S')) + { + reprap.GetHeat()->SetActiveTemperature(1, gb->GetFValue()); // 0 is the bed + reprap.GetHeat()->Activate(1); + } + break; - case 104: // Deprecated - if(gb->Seen('S')) - { - reprap.GetHeat()->SetActiveTemperature(1, gb->GetFValue()); // 0 is the bed - reprap.GetHeat()->Activate(1); - } - break; + case 105: // Deprecated... + strncpy(reply, "T:", STRING_LENGTH); + for (int8_t heater = HEATERS - 1; heater > 0; heater--) + { + strncat(reply, ftoa(0, reprap.GetHeat()->GetTemperature(heater), 1), STRING_LENGTH); + strncat(reply, " ", STRING_LENGTH); + } + strncat(reply, "B:", STRING_LENGTH); + strncat(reply, ftoa(0, reprap.GetHeat()->GetTemperature(0), 1), STRING_LENGTH); + break; - case 105: // Deprecated... - strncpy(reply, "T:", STRING_LENGTH); - for(int8_t heater = HEATERS - 1; heater > 0; heater--) - { - strncat(reply, ftoa(0, reprap.GetHeat()->GetTemperature(heater), 1), STRING_LENGTH); - strncat(reply, " ", STRING_LENGTH); - } - strncat(reply, "B:", STRING_LENGTH); - strncat(reply, ftoa(0, reprap.GetHeat()->GetTemperature(0), 1), STRING_LENGTH); - break; - - case 106: // Fan on or off - if(gb->Seen('S')) - platform->CoolingFan(gb->GetFValue()); - break; - - case 107: // Fan off - deprecated - platform->CoolingFan(0.0); - break; - - case 110: // Set line numbers - line numbers are dealt with in the GCodeBuffer class - break; + case 106: // Fan on or off + if (gb->Seen('S')) + platform->CoolingFan(gb->GetFValue()); + break; - case 111: // Debug level - if(gb->Seen('S')) - reprap.SetDebug(gb->GetIValue()); - break; + case 107: // Fan off - deprecated + platform->CoolingFan(0.0); + break; - case 112: // Emergency stop - acted upon in Webserver - break; + case 110: // Set line numbers - line numbers are dealt with in the GCodeBuffer class + break; - case 114: // Deprecated - { - const char* str = GetCurrentCoordinates(); - if(str != 0) + case 111: // Debug level + if (gb->Seen('S')) + reprap.SetDebug(gb->GetIValue()); + break; + + case 112: // Emergency stop - acted upon in Webserver + break; + + case 114: // Deprecated + { + const char* str = GetCurrentCoordinates(); + if (str != 0) { strncpy(reply, str, STRING_LENGTH); - } else - result = false; - } - break; + } + else + result = false; + } + break; - case 115: // Print firmware version - snprintf(reply, STRING_LENGTH, "FIRMWARE_NAME:%s FIRMWARE_VERSION:%s ELECTRONICS:%s DATE:%s", NAME, VERSION, ELECTRONICS, DATE); - break; + case 115: // Print firmware version + snprintf(reply, STRING_LENGTH, "FIRMWARE_NAME:%s FIRMWARE_VERSION:%s ELECTRONICS:%s DATE:%s", NAME, VERSION, + ELECTRONICS, DATE); + break; - case 109: // Deprecated - if(gb->Seen('S')) - { - reprap.GetHeat()->SetActiveTemperature(1, gb->GetFValue()); // 0 is the bed - reprap.GetHeat()->Activate(1); - } - /* no break */ - case 116: // Wait for everything, especially set temperatures - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) - return false; - result = reprap.GetHeat()->AllHeatersAtSetTemperatures(); - break; + case 109: // Deprecated + if (gb->Seen('S')) + { + reprap.GetHeat()->SetActiveTemperature(1, gb->GetFValue()); // 0 is the bed + reprap.GetHeat()->Activate(1); + } + /* no break */ + case 116: // Wait for everything, especially set temperatures + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) + return false; + result = reprap.GetHeat()->AllHeatersAtSetTemperatures(); + break; - case 120: - result = Push(); - break; + case 120: + result = Push(); + break; - case 121: - result = Pop(); - break; - - case 122: - reprap.Diagnostics(); - break; - - case 126: // Valve open - platform->Message(HOST_MESSAGE, "M126 - valves not yet implemented\n"); - break; - - case 127: // Valve closed - platform->Message(HOST_MESSAGE, "M127 - valves not yet implemented\n"); - break; - - case 135: // Set PID sample interval - break; + case 121: + result = Pop(); + break; - case 140: // Set bed temperature - if(gb->Seen('S')) - { - reprap.GetHeat()->SetActiveTemperature(0, gb->GetFValue()); - reprap.GetHeat()->Activate(0); - } - break; - - case 141: // Chamber temperature - platform->Message(HOST_MESSAGE, "M141 - heated chamber not yet implemented\n"); - break; + case 122: + reprap.Diagnostics(); + break; - case 201: // Set axis accelerations - for(int8_t drive = 0; drive < DRIVES; drive++) - { - float value; - if(gb->Seen(gCodeLetters[drive])) - { - value = gb->GetFValue(); - }else{ - value = -1; - } - platform->SetAcceleration(drive, value); - } - break; + case 126: // Valve open + platform->Message(HOST_MESSAGE, "M126 - valves not yet implemented\n"); + break; - case 203: // Set maximum feedrates - for(int8_t drive = 0; drive < DRIVES; drive++) - { - if(gb->Seen(gCodeLetters[drive])) - { - float value = gb->GetFValue()*distanceScale*0.016666667; // G Code feedrates are in mm/minute; we need mm/sec; - platform->SetMaxFeedrate(drive, value); - } - } - break; + case 127: // Valve closed + platform->Message(HOST_MESSAGE, "M127 - valves not yet implemented\n"); + break; - case 205: //M205 advanced settings: minimum travel speed S=while printing T=travel only, B=minimum segment time X= maximum xy jerk, Z=maximum Z jerk - break; + case 135: // Set PID sample interval + break; - case 206: // Offset axes - result = OffsetAxes(gb); - break; + case 140: // Set bed temperature + if (gb->Seen('S')) + { + reprap.GetHeat()->SetActiveTemperature(0, gb->GetFValue()); + reprap.GetHeat()->Activate(0); + } + break; - case 208: // Set maximum axis lengths - for(int8_t axis = 0; axis < AXES; axis++) - { - if(gb->Seen(gCodeLetters[axis])) - { - float value = gb->GetFValue()*distanceScale; - platform->SetAxisLength(axis, value); - } - } - break; + case 141: // Chamber temperature + platform->Message(HOST_MESSAGE, "M141 - heated chamber not yet implemented\n"); + break; - case 210: // Set homing feedrates - for(int8_t axis = 0; axis < AXES; axis++) - { - if(gb->Seen(gCodeLetters[axis])) - { - float value = gb->GetFValue()*distanceScale*0.016666667; - platform->SetHomeFeedRate(axis, value); - } - } - break; + case 201: // Set axis accelerations + for (int8_t drive = 0; drive < DRIVES; drive++) + { + float value; + if (gb->Seen(gCodeLetters[drive])) + { + value = gb->GetFValue(); + } + else + { + value = -1; + } + platform->SetAcceleration(drive, value); + } + break; - case 301: // Set hot end PID values - { - float pValue, iValue, dValue; - bool seen = false; - if (gb->Seen('P')) - { - pValue = gb->GetFValue(); - seen = true; - } - else - { - pValue = platform->PidKp(1); - } - if (gb->Seen('I')) - { - iValue = gb->GetFValue(); - seen = true; - } - else - { - iValue = platform->PidKi(1); - } - if (gb->Seen('D')) - { - dValue = gb->GetFValue(); - seen = true; - } - else - { - dValue = platform->PidKd(1); - } + case 203: // Set maximum feedrates + for (int8_t drive = 0; drive < DRIVES; drive++) + { + if (gb->Seen(gCodeLetters[drive])) + { + float value = gb->GetFValue() * distanceScale * 0.016666667; // G Code feedrates are in mm/minute; we need mm/sec; + platform->SetMaxFeedrate(drive, value); + } + } + break; - if (seen) - { - platform->SetPidValues(1, pValue, iValue, dValue); - } - else - { - snprintf(reply, STRING_LENGTH, "P:%f I:%f D: %f\n", pValue, iValue, dValue); - } - } - break; + case 205: //M205 advanced settings: minimum travel speed S=while printing T=travel only, B=minimum segment time X= maximum xy jerk, Z=maximum Z jerk + break; - case 302: // Allow cold extrudes - break; + case 206: // Offset axes + result = OffsetAxes(gb); + break; - case 304: // Set thermistor parameters - break; + case 208: // Set maximum axis lengths + for (int8_t axis = 0; axis < AXES; axis++) + { + if (gb->Seen(gCodeLetters[axis])) + { + float value = gb->GetFValue() * distanceScale; + platform->SetAxisLength(axis, value); + } + } + break; - case 503: // list variable settings - result = SendConfigToLine(); - break; + case 210: // Set homing feedrates + for (int8_t axis = 0; axis < AXES; axis++) + { + if (gb->Seen(gCodeLetters[axis])) + { + float value = gb->GetFValue() * distanceScale * 0.016666667; + platform->SetHomeFeedRate(axis, value); + } + } + break; - case 550: // Set machine name - if(gb->Seen('P')) - reprap.GetWebserver()->SetName(gb->GetString()); - break; + case 301: // Set hot end PID values + { + float pValue, iValue, dValue; + bool seen = false; + if (gb->Seen('P')) + { + pValue = gb->GetFValue(); + seen = true; + } + else + { + pValue = platform->PidKp(1); + } + if (gb->Seen('I')) + { + iValue = gb->GetFValue(); + seen = true; + } + else + { + iValue = platform->PidKi(1); + } + if (gb->Seen('D')) + { + dValue = gb->GetFValue(); + seen = true; + } + else + { + dValue = platform->PidKd(1); + } - case 551: // Set password - if(gb->Seen('P')) - reprap.GetWebserver()->SetPassword(gb->GetString()); - break; + if (seen) + { + platform->SetPidValues(1, pValue, iValue, dValue); + } + else + { + snprintf(reply, STRING_LENGTH, "P:%f I:%f D: %f\n", pValue, iValue, dValue); + } + } + break; - case 552: // Set/Get IP address - if(gb->Seen('P')) - SetEthernetAddress(gb, code); - else - { - const byte *ip = platform->IPAddress(); - snprintf(reply, STRING_LENGTH, "IP address: %d.%d.%d.%d\n ", ip[0], ip[1], ip[2], ip[3]); - } - break; + case 302: // Allow cold extrudes + break; - case 553: // Set/Get netmask - if(gb->Seen('P')) - SetEthernetAddress(gb, code); - else - { - const byte *nm = platform->NetMask(); - snprintf(reply, STRING_LENGTH, "Net mask: %d.%d.%d.%d\n ", nm[0], nm[1], nm[2], nm[3]); - } - break; + case 304: // Set thermistor parameters + break; - case 554: // Set/Get gateway - if(gb->Seen('P')) - SetEthernetAddress(gb, code); - else - { - const byte *gw = platform->GateWay(); - snprintf(reply, STRING_LENGTH, "Gateway: %d.%d.%d.%d\n ", gw[0], gw[1], gw[2], gw[3]); - } - break; + case 503: // list variable settings + result = SendConfigToLine(); + break; - case 555: // Set firmware type to emulate - if(gb->Seen('P')) - platform->SetEmulating((Compatibility)gb->GetIValue()); - break; + case 550: // Set machine name + if (gb->Seen('P')) + reprap.GetWebserver()->SetName(gb->GetString()); + break; - case 556: // Axis compensation - if(gb->Seen('S')) - { - float value = gb->GetFValue(); - for(int8_t axis = 0; axis < AXES; axis++) - if(gb->Seen(gCodeLetters[axis])) - reprap.GetMove()->SetAxisCompensation(axis, gb->GetFValue()/value); - } - break; + case 551: // Set password + if (gb->Seen('P')) + reprap.GetWebserver()->SetPassword(gb->GetString()); + break; - case 557: // Set Z probe point coordinates - if(gb->Seen('P')) - { - int iValue = gb->GetIValue(); - if(gb->Seen(gCodeLetters[X_AXIS])) - reprap.GetMove()->SetXBedProbePoint(iValue, gb->GetFValue()); - if(gb->Seen(gCodeLetters[Y_AXIS])) - reprap.GetMove()->SetYBedProbePoint(iValue, gb->GetFValue()); - } - break; + case 552: // Set/Get IP address + if (gb->Seen('P')) + SetEthernetAddress(gb, code); + else + { + const byte *ip = platform->IPAddress(); + snprintf(reply, STRING_LENGTH, "IP address: %d.%d.%d.%d\n ", ip[0], ip[1], ip[2], ip[3]); + } + break; - case 558: // Set Z probe type - if(gb->Seen('P')) - { - platform->SetZProbeType(gb->GetIValue()); - } - else - { - snprintf(reply, STRING_LENGTH, "%d", platform->GetZProbeType()); - } - break; + case 553: // Set/Get netmask + if (gb->Seen('P')) + SetEthernetAddress(gb, code); + else + { + const byte *nm = platform->NetMask(); + snprintf(reply, STRING_LENGTH, "Net mask: %d.%d.%d.%d\n ", nm[0], nm[1], nm[2], nm[3]); + } + break; - case 559: // Upload config.g - { - const char* str; - if(gb->Seen('P')) + case 554: // Set/Get gateway + if (gb->Seen('P')) + SetEthernetAddress(gb, code); + else + { + const byte *gw = platform->GateWay(); + snprintf(reply, STRING_LENGTH, "Gateway: %d.%d.%d.%d\n ", gw[0], gw[1], gw[2], gw[3]); + } + break; + + case 555: // Set firmware type to emulate + if (gb->Seen('P')) + platform->SetEmulating((Compatibility) gb->GetIValue()); + break; + + case 556: // Axis compensation + if (gb->Seen('S')) + { + float value = gb->GetFValue(); + for (int8_t axis = 0; axis < AXES; axis++) + if (gb->Seen(gCodeLetters[axis])) + reprap.GetMove()->SetAxisCompensation(axis, gb->GetFValue() / value); + } + break; + + case 557: // Set Z probe point coordinates + if (gb->Seen('P')) + { + int iValue = gb->GetIValue(); + if (gb->Seen(gCodeLetters[X_AXIS])) + reprap.GetMove()->SetXBedProbePoint(iValue, gb->GetFValue()); + if (gb->Seen(gCodeLetters[Y_AXIS])) + reprap.GetMove()->SetYBedProbePoint(iValue, gb->GetFValue()); + } + break; + + case 558: // Set Z probe type + if (gb->Seen('P')) + { + platform->SetZProbeType(gb->GetIValue()); + } + else + { + snprintf(reply, STRING_LENGTH, "%d", platform->GetZProbeType()); + } + break; + + case 559: // Upload config.g + { + const char* str; + if (gb->Seen('P')) str = gb->GetString(); else str = platform->GetConfigFile(); OpenFileToWrite(platform->GetSysDir(), str, gb); snprintf(reply, STRING_LENGTH, "Writing to file: %s", str); - } - break; + } + break; - case 560: // Upload reprap.htm - { - const char* str = INDEX_PAGE; - OpenFileToWrite(platform->GetWebDir(), str, gb); - snprintf(reply, STRING_LENGTH, "Writing to file: %s", str); - } - break; + case 560: // Upload reprap.htm + { + const char* str = INDEX_PAGE; + OpenFileToWrite(platform->GetWebDir(), str, gb); + snprintf(reply, STRING_LENGTH, "Writing to file: %s", str); + } + break; - case 561: - reprap.GetMove()->SetIdentityTransform(); - break; + case 561: + reprap.GetMove()->SetIdentityTransform(); + break; - case 562: // Reset temperature fault - use with great caution - if(gb->Seen('P')) - { - int iValue = gb->GetIValue(); - reprap.GetHeat()->ResetFault(iValue); - } - break; + case 562: // Reset temperature fault - use with great caution + if (gb->Seen('P')) + { + int iValue = gb->GetIValue(); + reprap.GetHeat()->ResetFault(iValue); + } + break; // case 876: // TEMPORARY - this will go away... // if(gb->Seen('P')) @@ -1804,111 +1889,107 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb) // } // break; - case 900: - result = DoFileCannedCycles("homex.g"); - break; + case 900: + result = DoFileCannedCycles("homex.g"); + break; - case 901: - result = DoFileCannedCycles("homey.g"); - break; + case 901: + result = DoFileCannedCycles("homey.g"); + break; + case 906: // Set Motor currents + for (uint8_t i = 0; i < DRIVES; i++) + { + if (gb->Seen(gCodeLetters[i])) + { + float value = gb->GetFValue(); // mA + platform->SetMotorCurrent(i, value); + } + } + break; + case 998: + if (gb->Seen('P')) + { + snprintf(reply, STRING_LENGTH, "%s", gb->GetIValue()); + resend = true; + } + break; - case 906: // Set Motor currents - for(uint8_t i = 0; i < DRIVES; i++) - { - if(gb->Seen(gCodeLetters[i])) - { - float value = gb->GetFValue(); // mA - platform->SetMotorCurrent(i, value); - } - } - break; + default: + error = true; + snprintf(reply, STRING_LENGTH, "invalid M Code: %s", gb->Buffer()); + } + if (result) + HandleReply(error, gb == serialGCode, reply, 'M', code, resend); + return result; + } - case 998: - if(gb->Seen('P')) - { - snprintf(reply, STRING_LENGTH, "%s", gb->GetIValue()); - resend = true; - } - break; - - default: - error = true; - snprintf(reply, STRING_LENGTH, "invalid M Code: %s", gb->Buffer()); - } - if(result) - HandleReply(error, gb == serialGCode, reply, 'M', code, resend); - return result; - } - - if(gb->Seen('T')) - { - code = gb->GetIValue(); - if(code == selectedHead) - { - if(result) - HandleReply(error, gb == serialGCode, reply, 'T', code, resend); - return result; - } + if (gb->Seen('T')) + { + code = gb->GetIValue(); + if (code == selectedHead) + { + if (result) + HandleReply(error, gb == serialGCode, reply, 'T', code, resend); + return result; + } - error = true; - for(int8_t i = AXES; i < DRIVES; i++) - { - if(selectedHead == i - AXES) - reprap.GetHeat()->Standby(selectedHead + 1); // + 1 because 0 is the Bed - } - for(int8_t i = AXES; i < DRIVES; i++) - { - if(code == i - AXES) - { - selectedHead = code; - reprap.GetHeat()->Activate(selectedHead + 1); // 0 is the Bed - error = false; - } - } + error = true; + for (int8_t i = AXES; i < DRIVES; i++) + { + if (selectedHead == i - AXES) + reprap.GetHeat()->Standby(selectedHead + 1); // + 1 because 0 is the Bed + } + for (int8_t i = AXES; i < DRIVES; i++) + { + if (code == i - AXES) + { + selectedHead = code; + reprap.GetHeat()->Activate(selectedHead + 1); // 0 is the Bed + error = false; + } + } - if(error) - snprintf(reply, STRING_LENGTH, "Invalid T Code: %s", gb->Buffer()); + if (error) + snprintf(reply, STRING_LENGTH, "Invalid T Code: %s", gb->Buffer()); - if(result) - HandleReply(error, gb == serialGCode, reply, 'T', code, resend); - return result; - } - - // An empty buffer jumps to here and gets discarded + if (result) + HandleReply(error, gb == serialGCode, reply, 'T', code, resend); + return result; + } - if(result) - HandleReply(error, gb == serialGCode, reply, 'X', code, resend); + // An empty buffer jumps to here and gets discarded - return result; + if (result) + HandleReply(error, gb == serialGCode, reply, 'X', code, resend); + + return result; } - - //************************************************************************************* // This class stores a single G Code and provides functions to allow it to be parsed GCodeBuffer::GCodeBuffer(Platform* p, const char* id) -{ - platform = p; - identity = id; - writingFileDirectory = NULL; // Has to be done here as Init() is called every line. +{ + platform = p; + identity = id; + writingFileDirectory = NULL; // Has to be done here as Init() is called every line. } void GCodeBuffer::Init() { - gcodePointer = 0; - readPointer = -1; - inComment = false; + gcodePointer = 0; + readPointer = -1; + inComment = false; } int GCodeBuffer::CheckSum() { int cs = 0; - for(int i = 0; gcodeBuffer[i] != '*' && gcodeBuffer[i] != 0; i++) - cs = cs ^ gcodeBuffer[i]; + for (int i = 0; gcodeBuffer[i] != '*' && gcodeBuffer[i] != 0; i++) + cs = cs ^ gcodeBuffer[i]; cs &= 0xff; // Defensive programming... return cs; } @@ -1918,112 +1999,113 @@ int GCodeBuffer::CheckSum() bool GCodeBuffer::Put(char c) { - bool result = false; - gcodeBuffer[gcodePointer] = c; - - if(c == ';') - inComment = true; - - if(c == '\n' || !c) - { - gcodeBuffer[gcodePointer] = 0; - Init(); - if(reprap.Debug() && gcodeBuffer[0] && !writingFileDirectory) // Don't bother with blank/comment lines - { - platform->Message(HOST_MESSAGE, identity); - platform->Message(HOST_MESSAGE, gcodeBuffer); - platform->Message(HOST_MESSAGE, "\n"); - } + bool result = false; + gcodeBuffer[gcodePointer] = c; - // Deal with line numbers and checksums + if (c == ';') + inComment = true; - if(Seen('*')) - { - int csSent = GetIValue(); - int csHere = CheckSum(); - Seen('N'); - if(csSent != csHere) - { - snprintf(gcodeBuffer, GCODE_LENGTH, "M998 P%d", GetIValue()); - Init(); - result = true; - return result; - } + if (c == '\n' || !c) + { + gcodeBuffer[gcodePointer] = 0; + Init(); + if (reprap.Debug() && gcodeBuffer[0] && !writingFileDirectory) // Don't bother with blank/comment lines + { + platform->Message(HOST_MESSAGE, identity); + platform->Message(HOST_MESSAGE, gcodeBuffer); + platform->Message(HOST_MESSAGE, "\n"); + } - // Strip out the line number and checksum + // Deal with line numbers and checksums - while(gcodeBuffer[gcodePointer] != ' ' && gcodeBuffer[gcodePointer]) - gcodePointer++; + if (Seen('*')) + { + int csSent = GetIValue(); + int csHere = CheckSum(); + Seen('N'); + if (csSent != csHere) + { + snprintf(gcodeBuffer, GCODE_LENGTH, "M998 P%d", GetIValue()); + Init(); + result = true; + return result; + } - // Anything there? + // Strip out the line number and checksum - if(!gcodeBuffer[gcodePointer]) - { - // No... - gcodeBuffer[0] = 0; - Init(); - result = true; - return result; - } + while (gcodeBuffer[gcodePointer] != ' ' && gcodeBuffer[gcodePointer]) + gcodePointer++; - // Yes... + // Anything there? - gcodePointer++; - int gp2 = 0; - while(gcodeBuffer[gcodePointer] != '*' && gcodeBuffer[gcodePointer]) - { - gcodeBuffer[gp2] = gcodeBuffer[gcodePointer++]; - gp2++; - } - gcodeBuffer[gp2] = 0; - Init(); - } + if (!gcodeBuffer[gcodePointer]) + { + // No... + gcodeBuffer[0] = 0; + Init(); + result = true; + return result; + } - result = true; - } else - { - if(!inComment || writingFileDirectory) - gcodePointer++; - } - - if(gcodePointer >= GCODE_LENGTH) - { - platform->Message(HOST_MESSAGE, "G Code buffer length overflow.\n"); - gcodePointer = 0; - gcodeBuffer[0] = 0; - } - - return result; -} + // Yes... + + gcodePointer++; + int gp2 = 0; + while (gcodeBuffer[gcodePointer] != '*' && gcodeBuffer[gcodePointer]) + { + gcodeBuffer[gp2] = gcodeBuffer[gcodePointer++]; + gp2++; + } + gcodeBuffer[gp2] = 0; + Init(); + } + + result = true; + } + else + { + if (!inComment || writingFileDirectory) + gcodePointer++; + } + + if (gcodePointer >= GCODE_LENGTH) + { + platform->Message(HOST_MESSAGE, "G Code buffer length overflow.\n"); + gcodePointer = 0; + gcodeBuffer[0] = 0; + } + + return result; +} // Is 'c' in the G Code string? // Leave the pointer there for a subsequent read. bool GCodeBuffer::Seen(char c) { - readPointer = 0; - while(gcodeBuffer[readPointer]) - { - if(gcodeBuffer[readPointer] == c) - return true; - readPointer++; - } - readPointer = -1; - return false; + readPointer = 0; + while (gcodeBuffer[readPointer]) + { + if (gcodeBuffer[readPointer] == c) + return true; + readPointer++; + } + readPointer = -1; + return false; } // Get a float after a G Code letter found by a call to Seen() float GCodeBuffer::GetFValue() { - if(readPointer < 0) - { - platform->Message(HOST_MESSAGE, "GCodes: Attempt to read a GCode float before a search.\n"); - return 0.0; - } - float result = (float)strtod(&gcodeBuffer[readPointer + 1], 0); - readPointer = -1; - return result; + if (readPointer < 0) + { + platform->Message(HOST_MESSAGE, "GCodes: Attempt to read a GCode float before a search.\n"); + return 0.0; + } + float result = (float) strtod(&gcodeBuffer[readPointer + 1], 0); + readPointer = -1; + return result; } // Get a string after a G Code letter found by a call to Seen(). @@ -2032,12 +2114,12 @@ float GCodeBuffer::GetFValue() const char* GCodeBuffer::GetString() { - if(readPointer < 0) + if (readPointer < 0) { platform->Message(HOST_MESSAGE, "GCodes: Attempt to read a GCode string before a search.\n"); return ""; } - const char* result = &gcodeBuffer[readPointer+1]; + const char* result = &gcodeBuffer[readPointer + 1]; readPointer = -1; return result; } @@ -2055,36 +2137,32 @@ const char* GCodeBuffer::GetString() const char* GCodeBuffer::GetUnprecedentedString() { - readPointer = 0; - while(gcodeBuffer[readPointer] && gcodeBuffer[readPointer] != ' ') - readPointer++; + readPointer = 0; + while (gcodeBuffer[readPointer] && gcodeBuffer[readPointer] != ' ') + readPointer++; - if(!gcodeBuffer[readPointer]) - { - platform->Message(HOST_MESSAGE, "GCodes: String expected but not seen.\n"); - return gcodeBuffer; // Good idea? - } + if (!gcodeBuffer[readPointer]) + { + platform->Message(HOST_MESSAGE, "GCodes: String expected but not seen.\n"); + return gcodeBuffer; // Good idea? + } - char* result = &gcodeBuffer[readPointer+1]; - readPointer = -1; - return result; + char* result = &gcodeBuffer[readPointer + 1]; + readPointer = -1; + return result; } - // Get an long after a G Code letter long GCodeBuffer::GetLValue() { - if(readPointer < 0) - { - platform->Message(HOST_MESSAGE, "GCodes: Attempt to read a GCode int before a search.\n"); - return 0; - } - long result = strtol(&gcodeBuffer[readPointer + 1], 0, 0); - readPointer = -1; - return result; + if (readPointer < 0) + { + platform->Message(HOST_MESSAGE, "GCodes: Attempt to read a GCode int before a search.\n"); + return 0; + } + long result = strtol(&gcodeBuffer[readPointer + 1], 0, 0); + readPointer = -1; + return result; } - - - diff --git a/GCodes.h b/GCodes.h index 30507ee..3419b9f 100644 --- a/GCodes.h +++ b/GCodes.h @@ -27,6 +27,11 @@ Licence: GPL #define GCODE_LETTERS { 'X', 'Y', 'Z', 'E', 'F' } // The drives and feedrate in a GCode +// Enumeration to define the mode in which we check endstops + +enum EndstopMode { noEndstopCheck, checkApproachingEndstop, checkAtEndstop}; + + // Small class to hold an individual GCode and provide functions to allow it to be parsed class GCodeBuffer @@ -72,7 +77,7 @@ class GCodes void Init(); void Exit(); bool RunConfigurationGCodes(); - bool ReadMove(float* m, bool& ce); + bool ReadMove(float* m, EndstopMode& ce); void QueueFileToPrint(const char* fileName); void DeleteFile(const char* fileName); bool GetProbeCoordinates(int count, float& x, float& y, float& z); @@ -86,7 +91,7 @@ class GCodes void doFilePrint(GCodeBuffer* gb); bool AllMovesAreFinishedAndMoveBufferIsLoaded(); - bool DoCannedCycleMove(bool ce); + bool DoCannedCycleMove(EndstopMode ce); bool DoFileCannedCycles(const char* fileName); bool FileCannedCyclesReturn(); bool ActOnGcode(GCodeBuffer* gb); @@ -126,7 +131,7 @@ class GCodes GCodeBuffer* cannedCycleGCode; bool moveAvailable; float moveBuffer[DRIVES+1]; // Last is feed rate - bool checkEndStops; + EndstopMode checkEndStops; bool drivesRelative; // All except X, Y and Z bool axesRelative; // X, Y and Z bool drivesRelativeStack[STACK]; diff --git a/Move.cpp b/Move.cpp index af456a1..26ad6d1 100644 --- a/Move.cpp +++ b/Move.cpp @@ -94,7 +94,7 @@ void Move::Init() liveCoordinates[i] = 0.0; } - lastMove->Init(ep, platform->HomeFeedRate(Z_AXIS), platform->InstantDv(Z_AXIS), false, zMove); // Typically Z is the slowest Axis + lastMove->Init(ep, platform->HomeFeedRate(Z_AXIS), platform->InstantDv(Z_AXIS), noEndstopCheck, zMove); // Typically Z is the slowest Axis lastMove->Release(); liveCoordinates[DRIVES] = platform->HomeFeedRate(Z_AXIS); @@ -167,13 +167,11 @@ void Move::Spin() // If there's a G Code move available, add it to the look-ahead // ring for processing. - bool checkEndStopsOnNextMove; + EndstopMode checkEndStopsOnNextMove; if(gCodes->ReadMove(nextMove, checkEndStopsOnNextMove)) { Transform(nextMove); - currentFeedrate = nextMove[DRIVES]; // Might be G1 with just an F field - for(int8_t drive = 0; drive < DRIVES; drive++) nextMachineEndPoints[drive] = LookAhead::EndPointToMachine(drive, nextMove[drive]); @@ -183,12 +181,12 @@ void Move::Spin() if(movementType == noMove) { + currentFeedrate = nextMove[DRIVES]; // Might be G1 with just an F field platform->ClassReport("Move", longWait); return; } // Real move - record its feedrate with it, not here. - currentFeedrate = -1.0; // Promote minimum feedrates @@ -294,7 +292,7 @@ bool Move::GetCurrentState(float m[]) // for the bed's plane, which means that a move is MAINLY and XY move, or MAINLY a Z move. It // is the main type of move that is returned. -int8_t Move::GetMovementType(long p0[], long p1[]) +int8_t Move::GetMovementType(const long p0[], const long p1[]) const { int8_t result = noMove; long dxy = 0; @@ -568,7 +566,7 @@ void Move::Interrupt() } -bool Move::LookAheadRingAdd(long ep[], float feedRate, float vv, bool ce, int8_t mt) +bool Move::LookAheadRingAdd(const long ep[], float feedRate, float vv, EndstopMode ce, int8_t mt) { if(LookAheadRingFull()) return false; @@ -898,9 +896,9 @@ MovementProfile DDA::Init(LookAhead* lookAhead, float& u, float& v) distance = 0.0; // X+Y+Z float eDistance = 0.0; float d; - long* targetPosition = myLookAheadEntry->MachineEndPoints(); + const long* targetPosition = myLookAheadEntry->MachineEndPoints(); v = myLookAheadEntry->V(); - long* positionNow = myLookAheadEntry->Previous()->MachineEndPoints(); + const long* positionNow = myLookAheadEntry->Previous()->MachineEndPoints(); u = myLookAheadEntry->Previous()->V(); checkEndStops = myLookAheadEntry->CheckEndStops(); @@ -1004,10 +1002,17 @@ MovementProfile DDA::Init(LookAhead* lookAhead, float& u, float& v) // If velocity requested is (almost) zero, set it to instantDv - if(v < instantDv) // Set change here? + if(v < instantDv) { - v = instantDv; - result = change; + v = instantDv; + result = change; + } + + // u here may be zero if we recently hit an endstop, in which case we need to set to to instantDv + if (u < instantDv) + { + u = instantDv; + result = change; } if(myLookAheadEntry->FeedRate() < instantDv) @@ -1019,11 +1024,11 @@ MovementProfile DDA::Init(LookAhead* lookAhead, float& u, float& v) velocity = u; - // Sanity check +// Sanity check if(velocity <= 0.0) { - velocity = 1.0; + velocity = 1.0; // if(reprap.Debug()) // platform->Message(HOST_MESSAGE, "DDA.Init(): Zero or negative initial velocity!\n"); } @@ -1076,18 +1081,27 @@ void DDA::Step() // Hit anything? - if(checkEndStops) + if(checkEndStops != noEndstopCheck) { - EndStopHit esh = platform->Stopped(drive); - if(esh == lowHit) + switch(platform->Stopped(drive)) { + case lowHit: move->HitLowStop(drive, myLookAheadEntry, this); active = false; - } - if(esh == highHit) - { + break; + case highHit: move->HitHighStop(drive, myLookAheadEntry, this); active = false; + break; + case lowNear: + if (checkEndStops == checkApproachingEndstop) + { + move->NearLowStop(drive, myLookAheadEntry, this); + active = false; + } + break; + default: + break; } } } @@ -1095,7 +1109,7 @@ void DDA::Step() // May have hit a stop, so test active here - if(active) + if(active) { if(axesMoving) timeStep = move->stepDistances[axesMoving]/velocity; @@ -1140,7 +1154,7 @@ LookAhead::LookAhead(Move* m, Platform* p, LookAhead* n) next = n; } -void LookAhead::Init(long ep[], float f, float vv, bool ce, int8_t mt) +void LookAhead::Init(const long ep[], float f, float vv, EndstopMode ce, int8_t mt) { v = vv; movementType = mt; diff --git a/Move.h b/Move.h index f4126a6..2b7c3e3 100644 --- a/Move.h +++ b/Move.h @@ -70,22 +70,22 @@ public: protected: LookAhead(Move* m, Platform* p, LookAhead* n); - void Init(long ep[], float feedRate, float vv, bool ce, int8_t mt); + void Init(const long ep[], float feedRate, float vv, EndstopMode ce, int8_t mt); LookAhead* Next(); LookAhead* Previous(); - long* MachineEndPoints(); + const long* MachineEndPoints() const; float MachineToEndPoint(int8_t drive); static float MachineToEndPoint(int8_t drive, long coord); static long EndPointToMachine(int8_t drive, float coord); - int8_t GetMovementType(); - float FeedRate(); - float V(); + int8_t GetMovementType() const; + float FeedRate() const; + float V() const; void SetV(float vv); void SetFeedRate(float f); - int8_t Processed(); + int8_t Processed() const; void SetProcessed(MovementState ms); void SetDriveCoordinateAndZeroEndSpeed(float a, int8_t drive); - bool CheckEndStops(); + EndstopMode CheckEndStops() const; void Release(); private: @@ -97,7 +97,7 @@ private: long endPoint[DRIVES+1]; // Should never use the +1, but safety first int8_t movementType; float Cosine(); - bool checkEndStops; + EndstopMode checkEndStops; float cosine; float v; // The feedrate we can actually do float feedRate; // The requested feedrate @@ -118,9 +118,9 @@ protected: MovementProfile Init(LookAhead* lookAhead, float& u, float& v); void Start(bool noTest); void Step(); - bool Active(); + bool Active() const; DDA* Next(); - float InstantDv(); + float InstantDv() const; private: MovementProfile AccelerationCalculation(float& u, float& v, MovementProfile result); @@ -135,7 +135,7 @@ private: bool directions[DRIVES]; long totalSteps; long stepCount; - bool checkEndStops; + EndstopMode checkEndStops; float timeStep; float velocity; long stopAStep; @@ -147,8 +147,6 @@ private: }; - - class Move { public: @@ -165,23 +163,24 @@ class Move void ResumeMoving(); void DoLookAhead(); void HitLowStop(int8_t drive, LookAhead* la, DDA* hitDDA); + void NearLowStop(int8_t drive, LookAhead* la, DDA* hitDDA); void HitHighStop(int8_t drive, LookAhead* la, DDA* hitDDA); void SetPositions(float move[]); void SetLiveCoordinates(float coords[]); void SetXBedProbePoint(int index, float x); void SetYBedProbePoint(int index, float y); void SetZBedProbePoint(int index, float z); - float xBedProbePoint(int index); - float yBedProbePoint(int index); - float zBedProbePoint(int index); - int NumberOfProbePoints(); - int NumberOfXYProbePoints(); - bool AllProbeCoordinatesSet(int index); - bool XYProbeCoordinatesSet(int index); + float xBedProbePoint(int index) const; + float yBedProbePoint(int index) const; + float zBedProbePoint(int index) const; + int NumberOfProbePoints() const; + int NumberOfXYProbePoints() const; + bool AllProbeCoordinatesSet(int index) const; + bool XYProbeCoordinatesSet(int index) const; void SetZProbing(bool probing); void SetProbedBedEquation(); - float SecondDegreeTransformZ(float x, float y); - float GetLastProbedZ(); + float SecondDegreeTransformZ(float x, float y) const; + float GetLastProbedZ() const; void SetAxisCompensation(int8_t axis, float tangent); void SetIdentityTransform(); void Transform(float move[]); @@ -197,16 +196,16 @@ class Move bool DDARingAdd(LookAhead* lookAhead); DDA* DDARingGet(); - bool DDARingEmpty(); - bool NoLiveMovement(); - bool DDARingFull(); + bool DDARingEmpty() const; + bool NoLiveMovement() const; + bool DDARingFull() const; bool GetDDARingLock(); void ReleaseDDARingLock(); - bool LookAheadRingEmpty(); - bool LookAheadRingFull(); - bool LookAheadRingAdd(long ep[], float feedRate, float vv, bool ce, int8_t movementType); + bool LookAheadRingEmpty() const; + bool LookAheadRingFull() const; + bool LookAheadRingAdd(const long ep[], float feedRate, float vv, EndstopMode ce, int8_t movementType); LookAhead* LookAheadRingGet(); - int8_t GetMovementType(long sp[], long ep[]); + int8_t GetMovementType(const long sp[], const long ep[]) const; float liveCoordinates[DRIVES + 1]; @@ -263,7 +262,7 @@ inline void LookAhead::SetV(float vv) v = vv; } -inline float LookAhead::V() +inline float LookAhead::V() const { return v; } @@ -276,7 +275,7 @@ inline float LookAhead::MachineToEndPoint(int8_t drive) } -inline float LookAhead::FeedRate() +inline float LookAhead::FeedRate() const { return feedRate; } @@ -286,7 +285,7 @@ inline void LookAhead::SetFeedRate(float f) feedRate = f; } -inline int8_t LookAhead::Processed() +inline int8_t LookAhead::Processed() const { return processed; } @@ -304,7 +303,7 @@ inline void LookAhead::Release() processed = released; } -inline bool LookAhead::CheckEndStops() +inline EndstopMode LookAhead::CheckEndStops() const { return checkEndStops; } @@ -316,19 +315,19 @@ inline void LookAhead::SetDriveCoordinateAndZeroEndSpeed(float a, int8_t drive) v = 0.0; } -inline long* LookAhead::MachineEndPoints() +inline const long* LookAhead::MachineEndPoints() const { return endPoint; } -inline int8_t LookAhead::GetMovementType() +inline int8_t LookAhead::GetMovementType() const { return movementType; } //****************************************************************************************************** -inline bool DDA::Active() +inline bool DDA::Active() const { return active; } @@ -338,7 +337,7 @@ inline DDA* DDA::Next() return next; } -inline float DDA::InstantDv() +inline float DDA::InstantDv() const { return instantDv; } @@ -346,12 +345,12 @@ inline float DDA::InstantDv() //*************************************************************************************** -inline bool Move::DDARingEmpty() +inline bool Move::DDARingEmpty() const { return ddaRingGetPointer == ddaRingAddPointer; } -inline bool Move::NoLiveMovement() +inline bool Move::NoLiveMovement() const { if(dda != NULL) return false; @@ -360,19 +359,19 @@ inline bool Move::NoLiveMovement() // Leave a gap of 2 as the last Get result may still be being processed -inline bool Move::DDARingFull() +inline bool Move::DDARingFull() const { return ddaRingAddPointer->Next()->Next() == ddaRingGetPointer; } -inline bool Move::LookAheadRingEmpty() +inline bool Move::LookAheadRingEmpty() const { return lookAheadRingCount == 0; } // Leave a gap of 2 as the last Get result may still be being processed -inline bool Move::LookAheadRingFull() +inline bool Move::LookAheadRingFull() const { if(!(lookAheadRingAddPointer->Processed() & released)) return true; @@ -459,17 +458,17 @@ inline void Move::SetZBedProbePoint(int index, float z) probePointSet[index] |= zSet; } -inline float Move::xBedProbePoint(int index) +inline float Move::xBedProbePoint(int index) const { return xBedProbePoints[index]; } -inline float Move::yBedProbePoint(int index) +inline float Move::yBedProbePoint(int index) const { return yBedProbePoints[index]; } -inline float Move::zBedProbePoint(int index) +inline float Move::zBedProbePoint(int index) const { return zBedProbePoints[index]; } @@ -479,22 +478,22 @@ inline void Move::SetZProbing(bool probing) zProbing = probing; } -inline float Move::GetLastProbedZ() +inline float Move::GetLastProbedZ() const { return lastZHit; } -inline bool Move::AllProbeCoordinatesSet(int index) +inline bool Move::AllProbeCoordinatesSet(int index) const { return probePointSet[index] == (xSet | ySet | zSet); } -inline bool Move::XYProbeCoordinatesSet(int index) +inline bool Move::XYProbeCoordinatesSet(int index) const { return (probePointSet[index] & xSet) && (probePointSet[index] & ySet); } -inline int Move::NumberOfProbePoints() +inline int Move::NumberOfProbePoints() const { if(AllProbeCoordinatesSet(0) && AllProbeCoordinatesSet(1) && AllProbeCoordinatesSet(2)) { @@ -506,7 +505,7 @@ inline int Move::NumberOfProbePoints() return 0; } -inline int Move::NumberOfXYProbePoints() +inline int Move::NumberOfXYProbePoints() const { if(XYProbeCoordinatesSet(0) && XYProbeCoordinatesSet(1) && XYProbeCoordinatesSet(2)) { @@ -530,7 +529,7 @@ inline int Move::NumberOfXYProbePoints() * * The values of x and y are transformed to put them in the interval [0, 1]. */ -inline float Move::SecondDegreeTransformZ(float x, float y) +inline float Move::SecondDegreeTransformZ(float x, float y) const { x = (x - xBedProbePoints[0])*xRectangle; y = (y - yBedProbePoints[0])*yRectangle; @@ -564,8 +563,12 @@ inline void Move::HitLowStop(int8_t drive, LookAhead* la, DDA* hitDDA) } else { // Executing G30, so set the current Z height to the value at which the end stop is triggered - lastZHit = platform->ZProbeStopHeight(); - hitPoint = lastZHit; + // Transform it first so that the height is correct in user coordinates + float xyzPoint[3]; + LiveCoordinates(xyzPoint); + xyzPoint[Z_AXIS] = lastZHit = platform->ZProbeStopHeight(); + Transform(xyzPoint); + hitPoint = xyzPoint[Z_AXIS]; } } la->SetDriveCoordinateAndZeroEndSpeed(hitPoint, drive); @@ -576,6 +579,11 @@ inline void Move::HitHighStop(int8_t drive, LookAhead* la, DDA* hitDDA) la->SetDriveCoordinateAndZeroEndSpeed(platform->AxisLength(drive), drive); } +inline void Move::NearLowStop(int8_t drive, LookAhead* la, DDA* hitDDA) +{ + la->SetDriveCoordinateAndZeroEndSpeed(1.0, drive); // say we are at 1mm +} + inline float Move::ComputeCurrentCoordinate(int8_t drive, LookAhead* la, DDA* runningDDA) { float previous = la->Previous()->MachineToEndPoint(drive); diff --git a/Platform.cpp b/Platform.cpp index 7fb1956..54c162d 100644 --- a/Platform.cpp +++ b/Platform.cpp @@ -20,6 +20,7 @@ Licence: GPL ****************************************************************************************************/ #include "RepRapFirmware.h" +#include "DueFlashStorage.h" #define WINDOWED_SEND_PACKETS (2) @@ -74,16 +75,29 @@ Platform::Platform() void Platform::Init() { - byte i; + DueFlashStorage::init(); + DueFlashStorage::read(nvAddress, &nvData, sizeof(nvData)); + if (nvData.magic != FlashData::magicValue) + { + // Nonvolatile data has not been initialized since the firmware was last written, so set up default values + nvData.compatibility = me; + nvData.ipAddress = IP_ADDRESS; + nvData.netMask = NET_MASK; + nvData.gateWay = GATE_WAY; - compatibility = me; + nvData.zProbeType = 0; // Default is to use the switch + nvData.irZProbeParameters.Init(false); + nvData.ultrasonicZProbeParameters.Init(true); + + nvData.magic = FlashData::magicValue; + } line->Init(); messageIndent = 0; massStorage->Init(); - for(i=0; i < MAX_FILES; i++) + for(size_t i=0; i < MAX_FILES; i++) files[i]->Init(); fileStructureInitialised = true; @@ -93,10 +107,6 @@ void Platform::Init() sysDir = SYS_DIR; configFile = CONFIG_FILE; - ipAddress = IP_ADDRESS; - netMask = NET_MASK; - gateWay = GATE_WAY; - // DRIVES stepPins = STEP_PINS; @@ -117,9 +127,6 @@ void Platform::Init() zProbePin = Z_PROBE_PIN; zProbeModulationPin = Z_PROBE_MOD_PIN; - zProbeType = 0; // Default is to use the switch - zProbeADValue = Z_PROBE_AD_VALUE; - zProbeStopHeight = Z_PROBE_STOP_HEIGHT; InitZProbe(); // AXES @@ -153,7 +160,7 @@ void Platform::Init() gcodeDir = GCODE_DIR; tempDir = TEMP_DIR; - for(i = 0; i < DRIVES; i++) + for(size_t i = 0; i < DRIVES; i++) { if(stepPins[i] >= 0) @@ -181,7 +188,7 @@ void Platform::Init() driveEnabled[i] = false; } - for(i = 0; i < AXES; i++) + for(size_t i = 0; i < AXES; i++) { if(lowStopPins[i] >= 0) { @@ -199,7 +206,7 @@ void Platform::Init() pinMode(heatOnPins[0], OUTPUT); thermistorInfRs[0] = ( thermistorInfRs[0]*exp(-thermistorBetas[0]/(25.0 - ABS_ZERO)) ); - for(i = 1; i < HEATERS; i++) + for(size_t i = 1; i < HEATERS; i++) { if(heatOnPins[i] >= 0) pinModeNonDue(heatOnPins[i], OUTPUT); @@ -227,18 +234,261 @@ void Platform::InitZProbe() zProbeCount = 0; zProbeOnSum = 0; zProbeOffSum = 0; + zProbeMinSum = 0; + zProbeWorking = false; for (uint8_t i = 0; i < NumZProbeReadingsAveraged; ++i) { zProbeReadings[i] = 0; } - if (zProbeType != 0) + if (nvData.zProbeType == 1 || nvData.zProbeType == 2) { pinMode(zProbeModulationPin, OUTPUT); digitalWrite(zProbeModulationPin, HIGH); // enable the IR LED + SetZProbing(false); } } +int Platform::GetRawZHeight() const +{ + return (nvData.zProbeType != 0) ? analogRead(zProbePin) : 0; +} + +int Platform::ZProbe() const +{ + switch(nvData.zProbeType) + { + case 1: // simple IR sensor + return (int)((zProbeOnSum + zProbeOffSum)/NumZProbeReadingsAveraged); + case 2: // modulated IR sensor + return (int)((zProbeOnSum - zProbeOffSum)/(NumZProbeReadingsAveraged/2)); + case 3: // ultrasonic sensor + return (int)((zProbeOnSum + zProbeOffSum - zProbeMinSum)/(NumZProbeReadingsAveraged/2)); + default: + return 0; + } +} + +int Platform::GetZProbeSecondaryValues(int& v1, int& v2) const +{ + switch(nvData.zProbeType) + { + case 2: // modulated IR sensor + v1 = zProbeOnSum/(NumZProbeReadingsAveraged/2); // pass back the reading with IR turned on + return 1; + case 3: // ultrasonic + v1 = (zProbeOnSum + zProbeOffSum)/NumZProbeReadingsAveraged; // pass back the raw reading + v2 = zProbeMinSum/NumZProbeReadingsAveraged; // pass back the minimum found + return 2; + default: + return 0; + } +} + +int Platform::GetZProbeType() const +{ + return nvData.zProbeType; +} + +void Platform::PollZHeight() +{ + uint16_t currentReading = GetRawZHeight(); + if (nvData.zProbeType == 2) + { + // Reverse the modulation, ready for next time + digitalWrite(zProbeModulationPin, (zProbeCount & 1) ? HIGH : LOW); + } + if (zProbeCount & 1) + { + zProbeOffSum = zProbeOffSum - zProbeReadings[zProbeCount] + currentReading; + } + else + { + zProbeOnSum = zProbeOnSum - zProbeReadings[zProbeCount] + currentReading; + } + zProbeReadings[zProbeCount] = currentReading; + ++zProbeCount; + if (zProbeCount == NumZProbeReadingsAveraged) + { + zProbeCount = 0; + if (!zProbeWorking) + { + zProbeWorking = true; + if (nvData.zProbeType == 3) + { + zProbeMinSum = zProbeOnSum + zProbeOffSum; + } + } + } + if (nvData.zProbeType == 3 && zProbeWorking) + { + // Update the minimum value seen from the ultrasonic sensor + long temp = zProbeOnSum + zProbeOffSum; + if (temp < zProbeMinSum) + { + zProbeMinSum = temp; + } + } +} + +float Platform::ZProbeStopHeight() const +{ + switch(nvData.zProbeType) + { + case 1: + case 2: + return nvData.irZProbeParameters.GetStopHeight(GetTemperature(0)); + case 3: + return nvData.ultrasonicZProbeParameters.GetStopHeight(GetTemperature(0)); + default: + return 0; + } +} + +void Platform::SetZProbeType(int pt) +{ + int newZProbeType = (pt >= 0 && pt <= 3) ? pt : 0; + if (newZProbeType != nvData.zProbeType) + { + nvData.zProbeType = newZProbeType; + WriteNvData(); + } + InitZProbe(); +} + +bool Platform::GetZProbeParameters(struct ZProbeParameters& params) const +{ + switch (nvData.zProbeType) + { + case 1: + case 2: + params = nvData.irZProbeParameters; + return true; + case 3: + params = nvData.ultrasonicZProbeParameters; + return true; + default: + return false; + } +} + +bool Platform::SetZProbeParameters(const struct ZProbeParameters& params) +{ + switch (nvData.zProbeType) + { + case 1: + case 2: + if (nvData.irZProbeParameters != params) + { + nvData.irZProbeParameters = params; + WriteNvData(); + } + return true; + case 3: + if (nvData.ultrasonicZProbeParameters != params) + { + nvData.ultrasonicZProbeParameters = params; + WriteNvData(); + } + return true; + default: + return false; + } +} + +// Return true if we must hoe X and U before we home Z (i.e. we are using an IR probe) +bool Platform::MustHomeXYBeforeZ() const +{ + return nvData.zProbeType == 1 || nvData.zProbeType == 2; +} + +void Platform::WriteNvData() +{ + DueFlashStorage::write(nvAddress, &nvData, sizeof(nvData)); +} + +void Platform::SetZProbing(bool starting) +{ + if (starting && nvData.zProbeType == 3) + { + zProbeMinSum = zProbeOnSum + zProbeOffSum; //look for a new minimum + // Tell the ultrasonic sensor we are starting or have completed a z-probe + //digitalWrite(zProbeModulationPin, (starting) ? LOW : HIGH); + } +} + +float Platform::Time() +{ + unsigned long now = micros(); + if(now < lastTimeCall) // Has timer overflowed? + addToTime += ((float)ULONG_MAX)*TIME_FROM_REPRAP; + lastTimeCall = now; + return addToTime + TIME_FROM_REPRAP*(float)now; +} + +void Platform::Exit() +{ + Message(HOST_MESSAGE, "Platform class exited.\n"); + active = false; +} + +Compatibility Platform::Emulating() const +{ + if(nvData.compatibility == reprapFirmware) + return me; + return nvData.compatibility; +} + +void Platform::SetEmulating(Compatibility c) +{ + if(c != me && c != reprapFirmware && c != marlin) + { + Message(HOST_MESSAGE, "Attempt to emulate unsupported firmware.\n"); + return; + } + if(c == reprapFirmware) + { + c = me; + } + if (nvData.compatibility != c) + { + nvData.compatibility = c; + WriteNvData(); + } +} + +void Platform::UpdateNetworkAddress(byte dst[4], const byte src[4]) +{ + bool changed = false; + for(uint8_t i = 0; i < 4; i++) + { + if(dst[i] != src[i]) + { + dst[i] = src[i]; + changed = true; + } + } + if (changed) + { + WriteNvData(); + } +} + +void Platform::SetIPAddress(byte ip[]) +{ + UpdateNetworkAddress(nvData.ipAddress, ip); +} + +void Platform::SetGateWay(byte gw[]) +{ + UpdateNetworkAddress(nvData.gateWay, gw); +} + +void Platform::SetNetMask(byte nm[]) +{ + UpdateNetworkAddress(nvData.netMask, nm); +} + void Platform::StartNetwork() { network->Init(); @@ -360,7 +610,7 @@ void Platform::ClassReport(char* className, float &lastTime) // Result is in degrees celsius -float Platform::GetTemperature(int8_t heater) +float Platform::GetTemperature(int8_t heater) const { // If the ADC reading is N then for an ideal ADC, the input voltage is at least N/(AD_RANGE + 1) and less than (N + 1)/(AD_RANGE + 1), times the analog reference. // So we add 0.5 to to the reading to get a better estimate of the input. But first, recognise the special case of thermistor disconnected. @@ -394,12 +644,18 @@ void Platform::SetHeater(int8_t heater, const float& power) EndStopHit Platform::Stopped(int8_t drive) { - if(zProbeType > 0) + if(nvData.zProbeType > 0) { // Z probe is used for both X and Z. if(drive != Y_AXIS) { - if(ZProbe() > zProbeADValue) + int zProbeVal = ZProbe(); + int zProbeADValue = (nvData.zProbeType == 3) + ? nvData.ultrasonicZProbeParameters.adcValue + : nvData.irZProbeParameters.adcValue; + if(zProbeVal >= zProbeADValue) return lowHit; + else if (zProbeVal * 10 >= zProbeADValue * 9) // if we are at/above 90% of the target value + return lowNear; else return noStop; } diff --git a/Platform.h b/Platform.h index 59778a3..f40a7d6 100644 --- a/Platform.h +++ b/Platform.h @@ -203,7 +203,8 @@ enum EndStopHit { noStop = 0, // no enstop hit lowHit = 1, // low switch hit, or Z-probe in use and above threshold - highHit = 2 // high stop hit + highHit = 2, // high stop hit + lowNear = 3 // approaching Z-probe threshold }; /***************************************************************************************************/ @@ -410,6 +411,44 @@ private: /***************************************************************************************************************/ +// Struct for holding Z probe parameters + +struct ZProbeParameters +{ + int adcValue; // the target ADC value + float height; // the nozzle height at which the target ADC value is returned + float calibTemperature; // the temperature at which we did the calibration + float temperatureCoefficient; // the variation of height with bed temperature + + void Init(bool isUltrasonic) + { + adcValue = Z_PROBE_AD_VALUE; + height = Z_PROBE_STOP_HEIGHT; + calibTemperature = 20.0; + temperatureCoefficient = (isUltrasonic) + ? 0.007575 // speed of sound correction assuming half wavelength between sensor and bed + : 0.0; // no default temperature correction for IR sensor + } + + float GetStopHeight(float temperature) const + { + return ((temperature - calibTemperature) * temperatureCoefficient) + height; + } + + bool operator==(const ZProbeParameters& other) const + { + return adcValue == other.adcValue + && height == other.height + && calibTemperature == other.calibTemperature + && temperatureCoefficient == other.temperatureCoefficient; + } + + bool operator!=(const ZProbeParameters& other) const + { + return !operator==(other); + } +}; + // The main class that defines the RepRap machine for the benefit of the other classes class Platform @@ -495,17 +534,21 @@ class Platform void SetAxisLength(int8_t axis, float value); bool HighStopButNotLow(int8_t axis) const; + // Z probe + float ZProbeStopHeight() const; - void SetZProbeStopHeight(float z); int ZProbe() const; - int ZProbeOnVal() const; - void SetZProbe(int iZ); + int GetZProbeSecondaryValues(int& v1, int& v2) const; void SetZProbeType(int iZ); int GetZProbeType() const; + void SetZProbing(bool starting); + bool GetZProbeParameters(struct ZProbeParameters& params) const; + bool SetZProbeParameters(const struct ZProbeParameters& params); + bool MustHomeXYBeforeZ() const; // Heat and temperature - float GetTemperature(int8_t heater); // Result is in degrees celsius + float GetTemperature(int8_t heater) const; // Result is in degrees Celsius void SetHeater(int8_t heater, const float& power); // power is a fraction in [0,1] float PidKp(int8_t heater) const; float PidKi(int8_t heater) const; @@ -526,6 +569,27 @@ class Platform private: + // This is the structure used to hold out non-volatile data. + // The SAM3X doesn't have EEPROM so we save the data to flash. This unfortunately means that it gets cleared + // every time we reprogram the firmware. So there is no need to cater for writing one version of this + // struct and reading back another. + + struct FlashData + { + static const uint16_t magicValue = 0x59B2; // value we use to recognise that he flash data has been written + uint16_t magic; + ZProbeParameters irZProbeParameters; // Z probe values for the IR sensor + ZProbeParameters ultrasonicZProbeParameters; // Z probe values for the IR sensor + int zProbeType; // the type of Z probe we are using + byte ipAddress[4]; + byte netMask[4]; + byte gateWay[4]; + Compatibility compatibility; + }; + + static const uint32_t nvAddress = 0; // address in flash where we store the nonvolatile data + FlashData nvData; + float lastTime; float longWait; float addToTime; @@ -533,8 +597,6 @@ class Platform bool active; - Compatibility compatibility; - void InitialiseInterrupts(); int GetRawZHeight() const; @@ -555,29 +617,25 @@ class Platform int8_t potWipes[DRIVES]; float senseResistor; float maxStepperDigipotVoltage; -// float zProbeGradient; -// float zProbeConstant; int8_t zProbePin; int8_t zProbeModulationPin; - int8_t zProbeType; uint8_t zProbeCount; long zProbeOnSum; // sum of readings taken when IR led is on long zProbeOffSum; // sum of readings taken when IR led is on + long zProbeMinSum; // minimum zprobe value seen, used with ultrasonic probe uint16_t zProbeReadings[NumZProbeReadingsAveraged]; - int zProbeADValue; - float zProbeStopHeight; + bool zProbeWorking; // AXES void InitZProbe(); void PollZHeight(); + void UpdateNetworkAddress(byte dst[4], const byte src[4]); + void WriteNvData(); float axisLengths[AXES]; float homeFeedrates[AXES]; float headOffsets[AXES]; // FIXME - needs a 2D array -// bool zProbeStarting; -// float zProbeHigh; -// float zProbeLow; // HEATERS - Bed is assumed to be the first @@ -612,61 +670,19 @@ class Platform MassStorage* massStorage; FileStore* files[MAX_FILES]; bool fileStructureInitialised; - //bool* inUse; char* webDir; char* gcodeDir; char* sysDir; char* tempDir; char* configFile; - //byte* buf[MAX_FILES]; - //int bPointer[MAX_FILES]; - //char fileList[FILE_LIST_LENGTH]; - //char scratchString[STRING_LENGTH]; // Network connection Network* network; - byte ipAddress[4]; - byte netMask[4]; - byte gateWay[4]; }; // Seconds -inline float Platform::Time() -{ - unsigned long now = micros(); - if(now < lastTimeCall) // Has timer overflowed? - addToTime += ((float)ULONG_MAX)*TIME_FROM_REPRAP; - lastTimeCall = now; - return addToTime + TIME_FROM_REPRAP*(float)now; -} - -inline void Platform::Exit() -{ - Message(HOST_MESSAGE, "Platform class exited.\n"); - active = false; -} - -inline Compatibility Platform::Emulating() const -{ - if(compatibility == reprapFirmware) - return me; - return compatibility; -} - -inline void Platform::SetEmulating(Compatibility c) -{ - if(c != me && c != reprapFirmware && c != marlin) - { - Message(HOST_MESSAGE, "Attempt to emulate unsupported firmware.\n"); - return; - } - if(c == reprapFirmware) - c = me; - compatibility = c; -} - // Where the htm etc files are inline const char* Platform::GetWebDir() const @@ -824,76 +840,6 @@ inline void Platform::SetMaxFeedrate(int8_t drive, float value) maxFeedrates[drive] = value; } -inline int Platform::GetRawZHeight() const -{ - return (zProbeType != 0) ? analogRead(zProbePin) : 0; -} - -inline int Platform::ZProbe() const -{ - return (zProbeType == 1) - ? (zProbeOnSum + zProbeOffSum)/NumZProbeReadingsAveraged // non-modulated mode - : (zProbeType == 2) - ? (zProbeOnSum - zProbeOffSum)/(NumZProbeReadingsAveraged/2) // modulated mode - : 0; // z-probe disabled -} - -inline int Platform::ZProbeOnVal() const -{ - return (zProbeType == 1) - ? (zProbeOnSum + zProbeOffSum)/NumZProbeReadingsAveraged - : (zProbeType == 2) - ? zProbeOnSum/(NumZProbeReadingsAveraged/2) - : 0; -} - -inline float Platform::ZProbeStopHeight() const -{ - return zProbeStopHeight; -} - -inline void Platform::SetZProbeStopHeight(float z) -{ - zProbeStopHeight = z; -} - -inline void Platform::SetZProbe(int iZ) -{ - zProbeADValue = iZ; -} - -inline void Platform::SetZProbeType(int pt) -{ - zProbeType = (pt >= 0 && pt <= 2) ? pt : 0; - InitZProbe(); -} - -inline int Platform::GetZProbeType() const -{ - return zProbeType; -} - -inline void Platform::PollZHeight() -{ - uint16_t currentReading = GetRawZHeight(); - if (zProbeType == 2) - { - // Reverse the modulation, ready for next time - digitalWrite(zProbeModulationPin, (zProbeCount & 1) ? HIGH : LOW); - } - if (zProbeCount & 1) - { - zProbeOffSum = zProbeOffSum - zProbeReadings[zProbeCount] + currentReading; - } - else - { - zProbeOnSum = zProbeOnSum - zProbeReadings[zProbeCount] + currentReading; - } - zProbeReadings[zProbeCount] = currentReading; - zProbeCount = (zProbeCount + 1) % NumZProbeReadingsAveraged; -} - - //******************************************************************************************************** // Drive the RepRap machine - Heat and temperature @@ -990,37 +936,19 @@ inline Network* Platform::GetNetwork() return network; } -inline void Platform::SetIPAddress(byte ip[]) -{ - for(uint8_t i = 0; i < 4; i++) - ipAddress[i] = ip[i]; -} - inline const byte* Platform::IPAddress() const { - return ipAddress; -} - -inline void Platform::SetNetMask(byte nm[]) -{ - for(uint8_t i = 0; i < 4; i++) - netMask[i] = nm[i]; + return nvData.ipAddress; } inline const byte* Platform::NetMask() const { - return netMask; -} - -inline void Platform::SetGateWay(byte gw[]) -{ - for(uint8_t i = 0; i < 4; i++) - gateWay[i] = gw[i]; + return nvData.netMask; } inline const byte* Platform::GateWay() const { - return gateWay; + return nvData.gateWay; } inline Line* Platform::GetLine() const diff --git a/Release/RepRapFirmware-057o-dc52.bin b/Release/RepRapFirmware-057o-dc52.bin new file mode 100644 index 0000000..0a2f811 Binary files /dev/null and b/Release/RepRapFirmware-057o-dc52.bin differ diff --git a/Webserver.cpp b/Webserver.cpp index 87b9cd3..5e73c51 100644 --- a/Webserver.cpp +++ b/Webserver.cpp @@ -440,14 +440,20 @@ void Webserver::GetJsonResponse(const char* request) // Send the Z probe value char scratch[SHORT_STRING_LENGTH+1]; scratch[SHORT_STRING_LENGTH] = 0; - if (platform->GetZProbeType() == 2) - { - snprintf(scratch, SHORT_STRING_LENGTH, ",\"probe\":\"%d (%d)\"", (int)platform->ZProbe(), platform->ZProbeOnVal()); - } - else - { - snprintf(scratch, SHORT_STRING_LENGTH, ",\"probe\":\"%d\"", (int)platform->ZProbe()); - } + int v0 = platform->ZProbe(); + int v1, v2; + switch(platform->GetZProbeSecondaryValues(v1, v2)) + { + case 1: + snprintf(scratch, SHORT_STRING_LENGTH, ",\"probe\":\"%d (%d)\"", v0, v1); + break; + case 2: + snprintf(scratch, SHORT_STRING_LENGTH, ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2); + break; + default: + snprintf(scratch, SHORT_STRING_LENGTH, ",\"probe\":\"%d\"", v0); + break; + } strncat(jsonResponse, scratch, STRING_LENGTH); // Send the amount of buffer space available for gcodes