text: /* Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
   *
   * The information contained herein is property of Nordic Semiconductor ASA.
   * Terms and conditions of usage are described in detail in NORDIC
   * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
   *
   * Licensees are granted free, non-transferable use of the information. NO
   * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
   * the file.
   *
   */
  
  /**
   * @file
   * @brief File with example code presenting usage of drivers for TWIS slave and TWI in master mode
   *
   * @sa twi_master_with_twis_slave_example
   */
  
  /**
   * @defgroup twi_master_with_twis_slave_example Example code presenting usage of drivers for TWIS slave and TWI in master mode
   *
   * This code presents usage of the two drivers:
   * - @ref nrf_twi_drv (in synchronous mode)
   * - @ref nrf_twis_drv (in asynchronous mode)
   *
   * On the slave an EEPROM memory is simulated.
   * For simulated EEPROM AT24C01C device produced by ATMEL was selected.
   * This device has 128 bytes of memory and it is simulated using internal RAM.
   * This RAM area is accessed only by simulated EEPROM so the rest of application can access it
   * only using TWI commands via hardware configured pins.
   *
   * Selected memory chip uses 7 bits constant address. Word to access is selected during
   * write operation: first byte send is used as current address pointer.
   *
   * Maximum 8 byte page can be written in single access.
   * The whole memory can be read in single access.
   *
   * Differences between real chip and simulated one:
   * 1. Simulated chip has practically 0ms write time.
   *    This example does not poll the memory for readiness.
   * 2. During sequential read, when memory end is reached, zeroes are send.
   *    There is no support for roll-over.
   * 3. It is possible to write maximum of 8 bytes in single sequential write.
   *    But in simulated EEPROM the whole address pointer is incremented.
   *    In real memory chip only 3 lowest bits changes during writing.
   *    In real device writing would roll-over in memory page.
   *
   * On the master side we communicate with that memory and allow write and read.
   * We can use simple commands via UART to check the memory.
   *
   * Pins to short:
   * - @ref TWI_SCL_M - @ref EEPROM_SIM_SCL_S
   * - @ref TWI_SDA_M - @ref EEPROM_SIM_SDA_S
   *
   * Supported commands are always listed in the welcome message.
   * @{
   */
  #include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <ctype.h>
  #include <string.h>
  #include "config.h"
  #include "eeprom_simulator.h"
  #include "app_uart.h"
  #include "nrf_drv_twi.h"
  #include "nrf_gpio.h"
  #include "app_error.h"
  #include "nrf.h"
  #include "bsp.h"
  #include "app_util_platform.h"
  
  
  /**
   * @brief Repeated part of help string
   *
   * This string contains list of supported command together with description.
   * It is used in welcome message and in help message if unsupported commmand is detected.
   */
  static const char m_cmd_help_str[] = 
          "   p - Print the EEPROM contents in a form: address, 8 bytes of code, ASCII form.\n\r"
          "   w - Write string starting from address 0.\n\r"
          "   c - Clear the memory (write 0xff)\n\r"
          "   x - Get transmission error byte.\n\r";
  
  
  /**
   * @brief TWI master instance
   *
   * Instance of TWI master driver that would be used for communication with simulated
   * eeprom memory.
   */
  static const nrf_drv_twi_t m_twi_master = NRF_DRV_TWI_INSTANCE(MASTER_TWI_INST);
  
  
  /**
   * @brief Write data to simulated EEPROM
   *
   * Function uses TWI interface to write data into EEPROM memory.
   *
   * @param     addr  Start address to write
   * @param[in] pdata Pointer to data to send
   * @param     size  Byte count of data to send
   * @attention       Maximum number of bytes that may be written is @ref EEPROM_SIM_SEQ_WRITE_MAX.
   *                  In sequential write all data have to be in the same page
   *                  (higher address bits do not change).
   *
   * @return NRF_SUCCESS or reason of error.
   *
   * @attention If you wish to communicate with real EEPROM memory chip check its readiness
   * after writing data.
   */
  static ret_code_t eeprom_write(size_t addr, uint8_t const * pdata, size_t size)
  {
      ret_code_t ret;
      /* Memory device supports only limited number of bytes written in sequence */
      if(size > (EEPROM_SIM_SEQ_WRITE_MAX))
      {
          return NRF_ERROR_INVALID_LENGTH;
      }
      /* All written data has to be in the same page */
      if((addr/(EEPROM_SIM_SEQ_WRITE_MAX)) != ((addr+size-1)/(EEPROM_SIM_SEQ_WRITE_MAX)))
      {
          return NRF_ERROR_INVALID_ADDR;
      }
      do
      {
          uint8_t buffer[1 + EEPROM_SIM_SEQ_WRITE_MAX]; /* Addr + data */
          buffer[0] = (uint8_t)addr;
          memcpy(buffer+1, pdata, size);
          ret = nrf_drv_twi_tx(&m_twi_master, EEPROM_SIM_ADDR, buffer, size+1, false);
      }while(0);
      return ret;
  }
  
  
  /**
   * @brief Read data from simulated EEPROM
   *
   * Function uses TWI interface to read data from EEPROM memory.
   *
   * @param     addr  Start address to read
   * @param[in] pdata Pointer to the buffer to fill with data
   * @param     size  Byte count of data to read
   *
   * @return NRF_SUCCESS or reason of error.
   */
  static ret_code_t eeprom_read(size_t addr, uint8_t * pdata, size_t size)
  {
      ret_code_t ret;
      if(size > (EEPROM_SIM_SIZE))
      {
          return NRF_ERROR_INVALID_LENGTH;
      }
      do
      {
         uint8_t addr8 = (uint8_t)addr;
         ret = nrf_drv_twi_tx(&m_twi_master, EEPROM_SIM_ADDR, &addr8, 1, true);
         if(NRF_SUCCESS != ret)
         {
             break;
         }
         ret = nrf_drv_twi_rx(&m_twi_master, EEPROM_SIM_ADDR, pdata, size);
      }while(0);
      return ret;
  }
  
  
  /**
   * @brief Put printable character to stdout
   *
   * Function puts given character to stdout.
   * If the character is not printable it is changed to dot before processing.
   *
   * @param c Character to print
   */
  static void safe_putc(char c)
  {
      if(!isprint((int)c))
      {
          c = '.';
      }
      UNUSED_VARIABLE(putc(c, stdout));
  }
  
  
  /**
   * @brief Print hexadecimal value to stdout
   *
   * Function prints given value as 2 digit hexadecimal.
   * Printed value always finishes with space.
   * This function is used in EEPROM content pretty-printing.
   *
   * @sa do_print_data
   *
   * @param data Value to print
   */
  static void print_hex(uint8_t data)
  {
      printf("%.2x ", (unsigned int)data);
  }
  
  
  /**
   * @brief Print address
   *
   * Function used to print address of a row in EEPROM content pretty-printing.
   * Function finishes printed value with a colon and a space.
   *
   * @sa do_print_data
   *
   * @param addr Address to print
   */
  static void print_addr(size_t addr)
  {
      printf("%.2x: ", (unsigned int)addr);
  }
  
  
  /**
   * @brief Pretty-print EEPROM content
   *
   * Respond on memory print command.
   */
  static void do_print_data(void)
  {
      size_t addr;
      uint8_t buff[IN_LINE_PRINT_CNT];
      for(addr=0; addr<(EEPROM_SIM_SIZE); addr+=(IN_LINE_PRINT_CNT))
      {
          unsigned int n;
          ret_code_t err_code;
          err_code = eeprom_read(addr, buff, IN_LINE_PRINT_CNT);
          if(NRF_SUCCESS != err_code)
          {
              printf("Communication error\n\r");
              return;
          }
  
          print_addr(addr);
          for(n=0; n<IN_LINE_PRINT_CNT; ++n)
          {
              print_hex(buff[n]);
          }
  
          safe_putc(' '); safe_putc(' ');
  
          for(n=0; n<IN_LINE_PRINT_CNT; ++n)
          {
              safe_putc((char)buff[n]);
          }
          printf("\n\r");
      }
      UNUSED_VARIABLE(fflush(stdout));
  }
  
  
  /**
   * @brief Safely get string from stdin
   *
   * Function reads character by character into given buffer.
   * Maximum @em nmax number of characters are read.
   *
   * Function ignores all nonprintable characters.
   * String may be finished by CR or NL.
   *
   * @attention
   * Remember that after characters read zero would be added to mark the string end.
   * Given buffer should be no smaller than @em nmax + 1
   *
   * @param[out] str  Buffer for the string
   * @param      nmax Maximum number of characters to be readed
   */
  static void safe_gets(char * str, size_t nmax)
  {
      int c;
      while(1)
      {
          c = getchar();
          if(isprint(c))
          {
              *str++ = (char)c;
              UNUSED_VARIABLE(putc(c, stdout));
              UNUSED_VARIABLE(fflush(stdout));
              if(0 == --nmax)
                  break;
          }
          else if('\n' == c || '\r' == c)
          {
              break;
          }
      }
      *str = '\0';
      UNUSED_VARIABLE(putc('\n', stdout));
      UNUSED_VARIABLE(fflush(stdout));
  }
  
  /**
   * @brief Check the string length check no more than given number of characters
   *
   * Function iterates through the string searching for the zero character.
   * Even when it is not found it stops iterating after maximum of @em nmax characters.
   *
   * @param[in] str  String to check
   * @param     nmax Maximum number of characters to check
   *
   * @return String length or @em nmax if no the zero character is found.
   */
  static size_t safe_strlen(char const * str, size_t nmax)
  {
      size_t n=0;
      while('\0' != *str++)
      {
          ++n;
          if(0 == --nmax)
              break;
      }
      return n;
  }
  
  /**
   * @brief Function that performs the command of writing string to EEPROM
   *
   * Function gets user input and writes given string to EEPROM starting from address 0.
   * It is accessing EEPROM using maximum allowed number of bytes in sequence.
   */
  static void do_string_write(void)
  {
      char str[(EEPROM_SIM_SIZE)+1];
      size_t addr = 0;
  
      printf("Waiting for string to write:\n\r");
      safe_gets(str, sizeof(str)-1);
      while(1)
      {
          ret_code_t err_code;
          size_t to_write = safe_strlen(str+addr, EEPROM_SIM_SEQ_WRITE_MAX);
          if(0 == to_write)
              break;
          err_code = eeprom_write(addr, (uint8_t const *)str+addr, to_write);
          if(NRF_SUCCESS != err_code)
          {
              printf("Communication error\n\r");
              return;
          }
          addr += to_write;
      }
  
      printf("OK\n\r");
  }
  
  /**
   * @brief Function that performs simulated EEPROM clearing
   *
   * Function fills the EEPROM with 0xFF value.
   * It is accessing the EEPROM writing only one byte at once.
   */
  static void do_clear_eeprom(void)
  {
      uint8_t clear_val = 0xff;
      size_t addr;
      for(addr=0; addr<(EEPROM_SIM_SIZE); ++addr)
      {
          ret_code_t err_code;
          err_code = eeprom_write(addr, &clear_val, 1);
          if(NRF_SUCCESS != err_code)
          {
              printf("Communication error\n\r");
              return;
          }
      }
      printf("Memory erased\n\r");
      UNUSED_VARIABLE(fflush(stdout));
  }
  
  /**
   * @brief Initialize the master TWI
   *
   * Function used to initialize master TWI interface that would communicate with simulated EEPROM.
   *
   * @return NRF_SUCCESS or the reason of failure
   */
  static ret_code_t twi_master_init(void)
  {
      ret_code_t ret;
      const nrf_drv_twi_config_t config =
      {
         .scl                = TWI_SCL_M,
         .sda                = TWI_SDA_M,
         .frequency          = NRF_TWI_FREQ_400K,
         .interrupt_priority = APP_IRQ_PRIORITY_HIGH
      };
  
      do
      {
          ret = nrf_drv_twi_init(&m_twi_master, &config, NULL, NULL);
          if(NRF_SUCCESS != ret)
          {
              break;
          }
          nrf_drv_twi_enable(&m_twi_master);
      }while(0);
      return ret;
  }
  
  /**
   * @brief Handle UART errors
   *
   * Simple function for handling any error from UART module.
   * See UART example for more information.
   *
   * @param[in] p_event Event structure
   */
  static void uart_error_handle(app_uart_evt_t * p_event)
  {
      if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
      {
          APP_ERROR_HANDLER(p_event->data.error_communication);
      }
      else if (p_event->evt_type == APP_UART_FIFO_ERROR)
      {
          APP_ERROR_HANDLER(p_event->data.error_code);
      }
  }
  
  
  
  
  /**
   *  The begin of the journey
   */
  int main(void)
  {
      ret_code_t err_code;
      /* Initialization of UART */
      LEDS_CONFIGURE(LEDS_MASK);
      LEDS_OFF(LEDS_MASK);
  
      const app_uart_comm_params_t comm_params =
      {
          RX_PIN_NUMBER,
          TX_PIN_NUMBER,
          RTS_PIN_NUMBER,
          CTS_PIN_NUMBER,
          APP_UART_FLOW_CONTROL_ENABLED,
          false,
          UART_BAUDRATE_BAUDRATE_Baud115200
      };
  
      APP_UART_FIFO_INIT(&comm_params,
                         UART_RX_BUF_SIZE,
                         UART_TX_BUF_SIZE,
                         uart_error_handle,
                         APP_IRQ_PRIORITY_LOW,
                         err_code);
  
      APP_ERROR_CHECK(err_code);
  
      /* Initializing simulated EEPROM */
      err_code = eeprom_simulator_init();
      APP_ERROR_CHECK(err_code);
  
      /* Initializing TWI master interface for EEPROM */
      err_code = twi_master_init();
      APP_ERROR_CHECK(err_code);
  
  
      /* Welcome message */
      printf(
              "This is TWIS and TWI usage example\n"
              "You can access simulated EEPROM memory using following commands:\n\r"
      );
      printf("%s",m_cmd_help_str);
      
      UNUSED_VARIABLE(fflush(stdout));
  
      /* Main loop */
      while(1)
      {
          uint8_t c;
          while(NRF_SUCCESS != app_uart_get(&c))
          {
              // Just waiting
          }
          switch((char)c)
          {
          case '\n':
          case '\r':
              break;
          case 'p':
              do_print_data();
              break;
          case 'w':
              do_string_write();
              break;
          case 'c':
              do_clear_eeprom();
              break;
          case 'x':
              {
                  uint32_t error = eeprom_simulator_error_get_and_clear();
                  printf("Error word: %x\n", (unsigned int)error);
              }
              break;
          default:
              printf("You selected %c\n", (char)c);
              printf("Unknown command, try one of the following:\n\r");
              printf("%s",m_cmd_help_str);
              break;
          }
          if(eeprom_simulator_error_check())
          {
              printf(
                      "WARNING: EEPROM transmission error detected.\n"
                      "Use 'x' command to read error word.\n\r"
              );
              UNUSED_VARIABLE(fflush(stdout));
          }
      }
  }
  
  /** @} */ /* End of group twi_master_with_twis_slave_example */
  