added debugging

Fork of BLE_nRF8001 by RedBearLab

Revision:
0:075ea2812998
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BLE_nRF8001/utility/hal_aci_tl.cpp	Fri Oct 17 22:40:32 2014 +0800
@@ -0,0 +1,432 @@
+/* Copyright (c) 2014, Nordic Semiconductor ASA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/** @file
+@brief Implementation of the ACI transport layer module
+*/
+
+#include "hal_platform.h"
+#include "hal_aci_tl.h"
+#include "aci_queue.h"
+
+/*
+PIC32 supports only MSbit transfer on SPI and the nRF8001 uses LSBit
+Use the REVERSE_BITS macro to convert from MSBit to LSBit
+The outgoing command and the incoming event needs to be converted
+*/
+//For mbed as the transmission has to be reversed, the next definitions have to be added
+#define REVERSE_BITS(byte) (((reverse_lookup[(byte & 0x0F)]) << 4) + reverse_lookup[((byte & 0xF0) >> 4)])
+static const uint8_t reverse_lookup[] = { 0, 8,  4, 12, 2, 10, 6, 14,1, 9, 5, 13,3, 11, 7, 15 };
+
+static void m_aci_data_print(hal_aci_data_t *p_data);
+static void m_aci_event_check(void);
+static void m_aci_isr(void);
+static void m_aci_pins_set(aci_pins_t *a_pins_ptr);
+static inline void m_aci_reqn_disable (void);
+static inline void m_aci_reqn_enable (void);
+static void m_aci_q_flush(void);
+static bool m_aci_spi_transfer(hal_aci_data_t * data_to_send, hal_aci_data_t * received_data);
+
+static uint8_t        spi_readwrite(uint8_t aci_byte);
+
+static bool           aci_debug_print = false;
+
+aci_queue_t    aci_tx_q;
+aci_queue_t    aci_rx_q;
+
+static aci_pins_t    *a_pins_local_ptr;
+
+void m_aci_data_print(hal_aci_data_t *p_data)
+{
+  const uint8_t length = p_data->buffer[0];
+  uint8_t i;
+  serial.printf("%d", length);
+  serial.printf(" :");
+  for (i=0; i<=length; i++)
+  {
+    serial.printf("%02X", p_data->buffer[i]);
+    serial.printf(", ");
+  }
+  serial.printf("");
+}
+
+/*
+  Interrupt service routine called when the RDYN line goes low. Runs the SPI transfer.
+*/
+static void m_aci_isr(void)
+{
+  hal_aci_data_t data_to_send;
+  hal_aci_data_t received_data;
+
+  // Receive from queue
+  if (!aci_queue_dequeue_from_isr(&aci_tx_q, &data_to_send))
+  {
+    /* queue was empty, nothing to send */
+    data_to_send.status_byte = 0;
+    data_to_send.buffer[0] = 0;
+  }
+
+  // Receive and/or transmit data
+  m_aci_spi_transfer(&data_to_send, &received_data);
+
+  if (!aci_queue_is_full_from_isr(&aci_rx_q) && !aci_queue_is_empty_from_isr(&aci_tx_q))
+  {
+    m_aci_reqn_enable();
+  }
+
+  // Check if we received data
+  if (received_data.buffer[0] > 0)
+  {
+    if (!aci_queue_enqueue_from_isr(&aci_rx_q, &received_data))
+    {
+      /* Receive Buffer full.
+         Should never happen.
+         Spin in a while loop.
+      */
+      while(1);
+    }
+
+    // Disable ready line interrupt until we have room to store incoming messages
+    if (aci_queue_is_full_from_isr(&aci_rx_q))
+    {
+      //detachInterrupt(a_pins_local_ptr->interrupt_number);
+    }
+  }
+
+  return;
+}
+
+/*
+  Checks the RDYN line and runs the SPI transfer if required.
+*/
+static void m_aci_event_check(void)
+{
+  hal_aci_data_t data_to_send;
+  hal_aci_data_t received_data;
+
+  // No room to store incoming messages
+  if (aci_queue_is_full(&aci_rx_q))
+  {
+    return;
+  }
+
+  // If the ready line is disabled and we have pending messages outgoing we enable the request line
+  if (HIGH == digitalRead(a_pins_local_ptr->rdyn_pin))
+  {
+    if (!aci_queue_is_empty(&aci_tx_q))
+    {
+      m_aci_reqn_enable();
+    }
+
+    return;
+  }
+
+  // Receive from queue
+  if (!aci_queue_dequeue(&aci_tx_q, &data_to_send))
+  {
+    /* queue was empty, nothing to send */
+    data_to_send.status_byte = 0;
+    data_to_send.buffer[0] = 0;
+  }
+
+  // Receive and/or transmit data
+  m_aci_spi_transfer(&data_to_send, &received_data);
+
+  /* If there are messages to transmit, and we can store the reply, we request a new transfer */
+  if (!aci_queue_is_full(&aci_rx_q) && !aci_queue_is_empty(&aci_tx_q))
+  {
+    m_aci_reqn_enable();
+  }
+
+  // Check if we received data
+  if (received_data.buffer[0] > 0)
+  {
+    if (!aci_queue_enqueue(&aci_rx_q, &received_data))
+    {
+      /* Receive Buffer full.
+         Should never happen.
+         Spin in a while loop.
+      */
+      while(1);
+    }
+  }
+
+  return;
+}
+
+/** @brief Point the low level library at the ACI pins specified
+ *  @details
+ *  The ACI pins are specified in the application and a pointer is made available for
+ *  the low level library to use
+ */
+static void m_aci_pins_set(aci_pins_t *a_pins_ptr)
+{
+  a_pins_local_ptr = a_pins_ptr;
+}
+
+static inline void m_aci_reqn_disable (void)
+{
+  digitalWrite(a_pins_local_ptr->reqn_pin, 1);
+}
+
+static inline void m_aci_reqn_enable (void)
+{
+  digitalWrite(a_pins_local_ptr->reqn_pin, 0);
+}
+
+static void m_aci_q_flush(void)
+{
+  //noInterrupts();
+  /* re-initialize aci cmd queue and aci event queue to flush them*/
+  aci_queue_init(&aci_tx_q);
+  aci_queue_init(&aci_rx_q);
+  //interrupts();
+}
+
+static bool m_aci_spi_transfer(hal_aci_data_t * data_to_send, hal_aci_data_t * received_data)
+{
+  uint8_t byte_cnt;
+  uint8_t byte_sent_cnt;
+  uint8_t max_bytes;
+
+  m_aci_reqn_enable();
+
+  // Send length, receive header
+  byte_sent_cnt = 0;
+  received_data->status_byte = spi_readwrite(data_to_send->buffer[byte_sent_cnt++]);
+  // Send first byte, receive length from slave
+  received_data->buffer[0] = spi_readwrite(data_to_send->buffer[byte_sent_cnt++]);
+  if (0 == data_to_send->buffer[0])
+  {
+    max_bytes = received_data->buffer[0];
+  }
+  else
+  {
+    // Set the maximum to the biggest size. One command byte is already sent
+    max_bytes = (received_data->buffer[0] > (data_to_send->buffer[0] - 1))
+                                          ? received_data->buffer[0]
+                                          : (data_to_send->buffer[0] - 1);
+  }
+
+  if (max_bytes > HAL_ACI_MAX_LENGTH)
+  {
+    max_bytes = HAL_ACI_MAX_LENGTH;
+  }
+
+  // Transmit/receive the rest of the packet
+  for (byte_cnt = 0; byte_cnt < max_bytes; byte_cnt++)
+  {
+    received_data->buffer[byte_cnt+1] =  spi_readwrite(data_to_send->buffer[byte_sent_cnt++]);
+  }
+
+  // RDYN should follow the REQN line in approx 100ns
+  m_aci_reqn_disable();
+
+  return (max_bytes > 0);
+}
+
+void hal_aci_tl_debug_print(bool enable)
+{
+    aci_debug_print = enable;
+}
+
+void hal_aci_tl_pin_reset(void)
+{
+    if (NULL != a_pins_local_ptr->reset_pin)
+    {
+        pinMode(a_pins_local_ptr->reset_pin, OUTPUT);
+
+        if ((REDBEARLAB_SHIELD_V1_1     == a_pins_local_ptr->board_name) ||
+            (REDBEARLAB_SHIELD_V2012_07 == a_pins_local_ptr->board_name))
+        {
+            //The reset for the Redbearlab v1.1 and v2012.07 boards are inverted and has a Power On Reset
+            //circuit that takes about 100ms to trigger the reset
+            digitalWrite(a_pins_local_ptr->reset_pin, 1);
+            delay(100);
+            digitalWrite(a_pins_local_ptr->reset_pin, 0);
+        }
+        else
+        {
+            digitalWrite(a_pins_local_ptr->reset_pin, 1);
+            digitalWrite(a_pins_local_ptr->reset_pin, 0);
+            digitalWrite(a_pins_local_ptr->reset_pin, 1);
+        }
+    }
+}
+
+bool hal_aci_tl_event_peek(hal_aci_data_t *p_aci_data)
+{
+  if (!a_pins_local_ptr->interface_is_interrupt)
+  {
+    m_aci_event_check();
+  }
+
+  if (aci_queue_peek(&aci_rx_q, p_aci_data))
+  {
+    return true;
+  }
+
+  return false;
+}
+
+bool hal_aci_tl_event_get(hal_aci_data_t *p_aci_data)
+{
+  bool was_full;
+
+  if (!a_pins_local_ptr->interface_is_interrupt && !aci_queue_is_full(&aci_rx_q))
+  {
+    m_aci_event_check();
+  }
+
+  was_full = aci_queue_is_full(&aci_rx_q);
+
+  if (aci_queue_dequeue(&aci_rx_q, p_aci_data))
+  {
+    if (aci_debug_print)
+    {
+      serial.printf(" E");
+      m_aci_data_print(p_aci_data);
+    }
+
+    if (was_full && a_pins_local_ptr->interface_is_interrupt)
+      {
+      /* Enable RDY line interrupt again */
+      //attachInterrupt(a_pins_local_ptr->interrupt_number, m_aci_isr, LOW);
+    }
+
+    /* Attempt to pull REQN LOW since we've made room for new messages */
+    if (!aci_queue_is_full(&aci_rx_q) && !aci_queue_is_empty(&aci_tx_q))
+    {
+      m_aci_reqn_enable();
+    }
+
+    return true;
+  }
+
+  return false;
+}
+
+void hal_aci_tl_init(aci_pins_t *a_pins, bool debug)
+{
+  aci_debug_print = debug;
+
+  /* Needs to be called as the first thing for proper intialization*/
+  m_aci_pins_set(a_pins);
+
+  /*
+  The SPI lines used are mapped directly to the hardware SPI
+  MISO MOSI and SCK
+  Change here if the pins are mapped differently
+
+  The SPI library assumes that the hardware pins are used
+  */
+  spi.format(8,0);
+  spi.frequency(2000000);
+
+  /* Initialize the ACI Command queue. This must be called after the delay above. */
+  aci_queue_init(&aci_tx_q);
+  aci_queue_init(&aci_rx_q);
+
+  //Configure the IO lines
+  pinMode(a_pins->rdyn_pin,     INPUT_PULLUP);
+  pinMode(a_pins->reqn_pin,     OUTPUT);
+
+  if (NULL != a_pins->active_pin)
+  {
+    pinMode(a_pins->active_pin, INPUT);
+  }
+  /* Pin reset the nRF8001, required when the nRF8001 setup is being changed */
+  hal_aci_tl_pin_reset();
+
+  /* Set the nRF8001 to a known state as required by the datasheet*/
+  digitalWrite(a_pins->reqn_pin, 1);
+
+  delay(30); //Wait for the nRF8001 to get hold of its lines - the lines float for a few ms after the reset
+
+  /* Attach the interrupt to the RDYN line as requested by the caller */
+  if (a_pins->interface_is_interrupt)
+  {
+    // We use the LOW level of the RDYN line as the atmega328 can wakeup from sleep only on LOW
+    //attachInterrupt(a_pins->interrupt_number, m_aci_isr, LOW);
+  }
+}
+
+bool hal_aci_tl_send(hal_aci_data_t *p_aci_cmd)
+{
+  const uint8_t length = p_aci_cmd->buffer[0];
+  bool ret_val = false;
+
+  if (length > HAL_ACI_MAX_LENGTH)
+  {
+    return false;
+  }
+
+  ret_val = aci_queue_enqueue(&aci_tx_q, p_aci_cmd);
+  if (ret_val)
+  {
+    if(!aci_queue_is_full(&aci_rx_q))
+    {
+      // Lower the REQN only when successfully enqueued
+      m_aci_reqn_enable();
+    }
+
+    if (aci_debug_print)
+    {
+      serial.printf("C"); //ACI Command
+      m_aci_data_print(p_aci_cmd);
+    }
+  }
+
+  return ret_val;
+}
+
+static uint8_t spi_readwrite(const uint8_t aci_byte)
+{
+    uint8_t tmp_byte;
+    tmp_byte = REVERSE_BITS(aci_byte);
+    tmp_byte = spi.write(tmp_byte);
+    return REVERSE_BITS(tmp_byte);
+}
+
+bool hal_aci_tl_rx_q_empty (void)
+{
+  return aci_queue_is_empty(&aci_rx_q);
+}
+
+bool hal_aci_tl_rx_q_full (void)
+{
+  return aci_queue_is_full(&aci_rx_q);
+}
+
+bool hal_aci_tl_tx_q_empty (void)
+{
+  return aci_queue_is_empty(&aci_tx_q);
+}
+
+bool hal_aci_tl_tx_q_full (void)
+{
+  return aci_queue_is_full(&aci_tx_q);
+}
+
+void hal_aci_tl_q_flush (void)
+{
+  m_aci_q_flush();
+}