2016年3月30日 星期三

nRF51x22 SPI Master Driver

1. 簡單替nRF51822 SPI Driver做個備份.

2. 本範例搭配ST LIS3DH Sensor Board測試使用.

3. 需先include spi_master.c 和 spi_master.h, 路徑在 \app\components\drivers_nrf\spi_master\ 底下.



4. spi_master.c 和 spi_master.h 的下載連結:

    spi_master.c

    spi_master.h

5. code:

spi_example.h

#include "app_util_platform.h"
#include "bsp.h"
#include "spi_master.h"

bool SPI_Write( uint8_t register_address, uint8_t *value, uint8_t number_of_bytes );
bool SPI_Read( uint8_t register_address, uint8_t *destination, uint8_t number_of_bytes );


spi_example.c

#include "spi_example.h"


static void spi_master_init(spi_master_hw_instance_t   spi_master_instance )
{
    uint32_t err_code = NRF_SUCCESS;

    // Configure SPI master.
    spi_master_config_t spi_config = SPI_MASTER_INIT_DEFAULT;
    spi_config.SPI_CONFIG_CPHA=SPI_CONFIG_CPHA_Leading ;
    spi_config.SPI_CONFIG_CPOL=SPI_CONFIG_CPOL_ActiveHigh;
    spi_config.SPI_Pin_SCK  = SPIM0_SCK_PIN;
    spi_config.SPI_Pin_MISO = SPIM0_MISO_PIN;
    spi_config.SPI_Pin_MOSI = SPIM0_MOSI_PIN;
    spi_config.SPI_Pin_SS   = SPIM0_SS_PIN;
    spi_config.SPI_Freq     = SPI_FREQUENCY_FREQUENCY_M4;
    spi_config.SPI_CONFIG_ORDER = SPI_CONFIG_ORDER_MsbFirst;
    spi_config.SPI_PriorityIRQ = APP_IRQ_PRIORITY_LOW;
    spi_config.SPI_DisableAllIRQ = 0;
    err_code = spi_master_open(spi_master_instance, &spi_config);
    APP_ERROR_CHECK(err_code);
}

static void spi_send_recv(const spi_master_hw_instance_t spi_master_hw_instance, uint8_t * const p_tx_data, uint8_t * const p_rx_data, const uint16_t len)
{
    
    // Start transfer.
  spi_master_init(spi_master_hw_instance);
    uint32_t err_code = spi_master_send_recv(spi_master_hw_instance, p_tx_data, len, p_rx_data, len);
    APP_ERROR_CHECK(err_code);
  for (int i = 0 ; i < 1000 ; i++ ) {
   if ( spi_master_get_state(spi_master_hw_instance) != SPI_MASTER_STATE_BUSY )
    break;
  }
  spi_master_close(spi_master_hw_instance);
}

bool SPI_Write( uint8_t register_address, uint8_t *value, uint8_t number_of_bytes )
{
  #define i2c_write_data_len 6
 
  uint8_t w2_data[i2c_write_data_len+1];
  uint8_t r2_data[i2c_write_data_len+1];
  uint8_t i;

  if(number_of_bytes > 0x01)
  {
   register_address |= (uint8_t)MULTIPLEBYTE_CMD;
  }
 
  w2_data[0] = register_address;
  for ( i = 0 ; i < number_of_bytes ; i++ ) {
   w2_data[i+1] = value[i];
  }

  spi_send_recv(SPI_MASTER_0, w2_data, r2_data, number_of_bytes +1);

                return true;
}

bool SPI_Read(uint8_t register_address, uint8_t * destination, uint8_t number_of_bytes)
{
 
  #define i2c_read_data_len 8

  uint8_t w2_data[i2c_write_data_len+1];
  uint8_t r2_data[i2c_write_data_len+1];
  uint8_t i;

  if(number_of_bytes > 0x01)
  {
   register_address |= (uint8_t)(READWRITE_CMD | MULTIPLEBYTE_CMD);
  }
  else
  {
   register_address |= (uint8_t)READWRITE_CMD;
  }
 
  w2_data[0] = register_address;
  spi_send_recv(SPI_MASTER_0, w2_data, r2_data, number_of_bytes+1);
 
  for( i = 0 ; i < number_of_bytes ; i++ ) {
   destination[i] = r2_data[i+1];
  }

                return true;
}


6. example-1:  read WHO_AM_I register(0x0F) from LIS3DH.

#define  LIS3DH_REGISTER_WHO_AM_I             0x0F

void Read_LIS3DH_WHOAMI_Register(void)
{
 uint8_t temp[6];
 
 SPI_Read(LIS3DH_REGISTER_WHO_AM_I, temp, 1);
 if ( temp[0] == 0x33 ) {
   while(1);
 }
}





7. example-2: Enable LIS3DH Sensor.

#define  LIS3DH_CTRL_REG1_DATARATE_100HZ      0x50
#define  LIS3DH_CTRL_REG1_XYZEN               0x07
#define  LIS3DH_REGISTER_CTRL_REG1            0x20

void Enable_LIS3DH_Sensor(void)
{
 uint8_t temp[6];
 
 temp[0] = (LIS3DH_CTRL_REG1_DATARATE_100HZ | LIS3DH_CTRL_REG1_XYZEN);
 SPI_Write( LIS3DH_REGISTER_CTRL_REG1, temp, 1 );
 
}
 




2016年3月9日 星期三

[News] Eachpal HALO Bracelet

        沒想到跟客戶合作的體感手鐲居然在MWC展覽上有展出, 不過目前看到的新聞都是主打丹麥設計師Jacob Jensen的噱頭, 還沒秀出相關的Gesture功能來展示.








2016年3月4日 星期五

2016年3月2日 星期三

STM32F103 I2C Master Driver

話不說多, 直接上code.

I2C_Master.h

#include "stm32f10x.h"

void I2C_Master_Init(void);
void I2C_Master_DeInit(void);
int I2C_Master_Read(uint8_t deviceAddr, uint8_t readAddr, uint8_t* pBuffer, uint16_t numByteToRead);
int I2C_Master_Write(uint8_t deviceAddress, uint8_t WriteAddr, uint8_t* pBuffer, uint16_t numByteToWrite);
int I2C_TIMEOUT_UserCallback(void);


I2C_Master.c

#include "I2C_Master.h"

#define I2C_MEMS                I2C1
#define I2C_MEMS_CLK            RCC_APB1Periph_I2C1
#define I2C_MEMS_GPIO           GPIOB
#define I2C_MEMS_GPIO_CLK       RCC_APB2Periph_GPIOB
#define I2C_MEMS_SCL            GPIO_Pin_6
#define I2C_MEMS_SDA            GPIO_Pin_7

#define I2C_Speed               400000
#define I2C_SLAVE_ADDRESS7      0xA0
#define I2C_TIMEOUT             3000

/* I2C STOP mask */
#define CR1_STOP_Set            ((uint16_t)0x0200)
#define CR1_STOP_Reset          ((uint16_t)0xFDFF)

/* I2C ACK mask */
#define CR1_ACK_Set             ((uint16_t)0x0400)
#define CR1_ACK_Reset           ((uint16_t)0xFBFF)

/* I2C POS mask */
#define CR1_POS_Set             ((uint16_t)0x0800)
#define CR1_POS_Reset           ((uint16_t)0xF7FF)

#define NULL ((void *)0)


void I2C_Master_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure; 
  I2C_InitTypeDef  I2C_InitStructure; 
  
  /* I2C Periph clock enable */
  RCC_APB1PeriphClockCmd(I2C_MEMS_CLK, ENABLE);   
  
  /* GPIO Periph clock enable */
  RCC_APB2PeriphClockCmd(I2C_MEMS_GPIO_CLK, ENABLE);    
  
  /* GPIO configuration */
  GPIO_InitStructure.GPIO_Pin =  I2C_MEMS_SCL | I2C_MEMS_SDA; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_Init(I2C_MEMS_GPIO, &GPIO_InitStructure);  
  
   /* I2C configuration */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS7;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
  
  /* I2C Peripheral Enable */
  I2C_Cmd(I2C_MEMS, ENABLE);
  /* Apply I2C configuration after enabling it */
  I2C_Init(I2C_MEMS, &I2C_InitStructure);
}

void I2C_Master_DeInit(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;  
    
  /* UnConfigure I2C */
  I2C_DeInit(I2C_MEMS);
  I2C_Cmd(I2C_MEMS, DISABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, DISABLE);
           
  /* UnConfigure I2C_MEMS pins: SCL and SDA */
  GPIO_InitStructure.GPIO_Pin = I2C_MEMS_SCL | I2C_MEMS_SDA; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(I2C_MEMS_GPIO, &GPIO_InitStructure);  
  
}


int I2C_Master_Read(uint8_t deviceAddr, uint8_t readAddr, uint8_t* pBuffer, uint16_t numByteToRead) {
  
  __IO uint32_t temp = 0;
  volatile int I2C_TimeOut = 0;
  
  // /* While the bus is busy * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(I2C_GetFlagStatus(I2C_MEMS, I2C_FLAG_BUSY))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }
  
  
  // * Send START condition * /
  I2C_GenerateSTART(I2C_MEMS, ENABLE);

  // / * Test on EV5 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_MODE_SELECT))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }
  
  // / * Send EEPROM address for write  * /
  I2C_Send7bitAddress(I2C_MEMS, deviceAddr, I2C_Direction_Transmitter);

  // / * Test on EV6 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }
  
  // / * Send the EEPROM's internal address to read from: Only one byte address  * /
  I2C_SendData(I2C_MEMS, readAddr);  

  /// * Test on EV8 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }  
    
  /// * Send STRAT condition a second time * /  
  I2C_GenerateSTART(I2C_MEMS, ENABLE);

  /// * Test on EV5 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_MODE_SELECT))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }  
  
    // * Send EEPROM address for read * /
  I2C_Send7bitAddress(I2C_MEMS, deviceAddr, I2C_Direction_Receiver);  

  if (numByteToRead == 1)  {
    /* Wait until ADDR is set */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1&0x0002) != 0x0002)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }    
   /* Clear ACK bit */
    I2C_MEMS->CR1 &= CR1_ACK_Reset;
    /* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
    software sequence must complete before the current byte end of transfer */
    __disable_irq();
    /* Clear ADDR flag */
    temp = I2C_MEMS->SR2;
    /* Program the STOP */
    I2C_GenerateSTOP(I2C_MEMS, ENABLE);
    /* Re-enable IRQs */
    __enable_irq();
    /* Wait until a data is received in DR register (RXNE = 1) EV7 */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1 & 0x00040) != 0x000040)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }    
    /* Read the data */
      *pBuffer = I2C_MEMS->DR;
  
  }
  else if (numByteToRead == 2) {
                  
    /* Set POS bit */
    I2C_MEMS->CR1 |= CR1_POS_Set;
    /* Wait until ADDR is set: EV6 */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1&0x0002) != 0x0002)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }      
    /* EV6_1: The acknowledge disable should be done just after EV6,
    that is after ADDR is cleared, so disable all active IRQs around ADDR clearing and 
    ACK clearing */
    __disable_irq();
    /* Clear ADDR by reading SR2 register  */
    temp = I2C_MEMS->SR2;
    /* Clear ACK */
    I2C_MEMS->CR1 &= CR1_ACK_Reset;
    /*Re-enable IRQs */
    __enable_irq();
    /* Wait until BTF is set */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1 & 0x00004) != 0x000004)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }        
    /* Disable IRQs around STOP programming and data reading */
    __disable_irq();
    /* Program the STOP */
    I2C_GenerateSTOP(I2C_MEMS, ENABLE);
    /* Read first data */
    *pBuffer = I2C_MEMS->DR;
    /* Re-enable IRQs */
    __enable_irq();
    /**/
    pBuffer++;
    /* Read second data */
    *pBuffer = I2C_MEMS->DR;
    /* Clear POS bit */
    I2C_MEMS->CR1  &= CR1_POS_Reset;
  }
  

  else { //numByteToRead > 2 
    // * Test on EV6 and clear it * /
    I2C_TimeOut = I2C_TIMEOUT;
    while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }         
    // * While there is data to be read * /
    while(numByteToRead)   {
      /* Receive bytes from first byte until byte N-3 */
      if (numByteToRead != 3) {
        /* Poll on BTF to receive data because in polling mode we can not guarantee the
        EV7 software sequence is managed before the current byte transfer completes */
        I2C_TimeOut = I2C_TIMEOUT;
        while ((I2C_MEMS->SR1 & 0x00004) != 0x000004)
        {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
        }           
        /* Read data */
        *pBuffer = I2C_MEMS->DR;
         pBuffer++;
        /* Decrement the read bytes counter */
        numByteToRead--;
      }
      
      /* it remains to read three data: data N-2, data N-1, Data N */
      if (numByteToRead == 3) {
        /* Wait until BTF is set: Data N-2 in DR and data N -1 in shift register */
        I2C_TimeOut = I2C_TIMEOUT;
        while ((I2C_MEMS->SR1 & 0x00004) != 0x000004)
        {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
        }            
        /* Clear ACK */
        I2C_MEMS->CR1 &= CR1_ACK_Reset;
    
        /* Disable IRQs around data reading and STOP programming */
        __disable_irq();
        /* Read Data N-2 */
        *pBuffer = I2C_MEMS->DR;
        /* Increment */
        pBuffer++;
        /* Program the STOP */
        I2C_MEMS->CR1 |= CR1_STOP_Set;
        /* Read DataN-1 */
        *pBuffer = I2C_MEMS->DR;
        /* Re-enable IRQs */
        __enable_irq();
        /* Increment */
        pBuffer++;
        /* Wait until RXNE is set (DR contains the last data) */
        I2C_TimeOut = I2C_TIMEOUT;
        while ((I2C_MEMS->SR1 & 0x00040) != 0x000040)
        {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
        }           
        /* Read DataN */
        *pBuffer = I2C_MEMS->DR;
        /* Reset the number of bytes to be read by master */
        numByteToRead = 0;
      }
    }
  }
 
  /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
  I2C_TimeOut = I2C_TIMEOUT;
  while ((I2C_MEMS->CR1&0x200) == 0x200)
  {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
  }             
  
  // * Enable Acknowledgement to be ready for another reception * /
  I2C_AcknowledgeConfig(I2C_MEMS, ENABLE);
  
  return 0;
    
}

int I2C_Master_Write(uint8_t deviceAddress, uint8_t WriteAddr, uint8_t* pBuffer, uint16_t numByteToWrite) {    
  
  volatile int I2C_TimeOut = 0;  
  
  /* Send STRAT condition */
  I2C_GenerateSTART(I2C_MEMS, ENABLE);

  /* Test on EV5 and clear it */
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
    }
  }       

  /* Send EEPROM address for write */
  I2C_Send7bitAddress(I2C_MEMS, deviceAddress, I2C_Direction_Transmitter);
  
  /* Test on EV6 and clear it */
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
    }
  }        
 
  /* Send the EEPROM's internal address to write to : only one byte Address */
  I2C_SendData(I2C_MEMS, WriteAddr); 
  
  /* Test on EV8 and clear it */
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
    }
  }        
 
  while(numByteToWrite > 0) {
    /* Send the byte to be written */
    I2C_SendData(I2C_MEMS, *pBuffer); 
   
    /* Test on EV8 and clear it */
    I2C_TimeOut = I2C_TIMEOUT;
    while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
      if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
      }
    }     
  
    pBuffer++;
    numByteToWrite--;
  }
  
  /* Send STOP condition */
  I2C_GenerateSTOP(I2C_MEMS, ENABLE);
  
  return 0;
}

int I2C_TIMEOUT_UserCallback(void)
{
  /* User can add his own implementation to manage TimeOut Communication failure */
  /* Block communication and all processes */
  I2C_Master_DeInit();
  for(int i=0; i<3000; i++){__asm("nop");}
  I2C_Master_Init();
  for(int i=0; i<3000; i++){__asm("nop");}
  return -1;
}


2016年3月1日 星期二

[Unboxing]] eMotion: ST MEMS adapters motherboard

1. 之前經常會拿到st提供的sensor board來進行相關的測試或是移植, 有時候在沒有ap或是pc端驗
   證軟體的時候, 就比較難以知道sensor是否正常動作, 因此st mems team有提供了專屬的開發板
   和pc tool來協助驗證.

2. mems motherboard如下圖所示, 編號是STEVAL-MKI109V2, 而搭配測試的sensor board編號
   是STEVAL-MKI108V2.

[將SWD拉出來]


3. 接下來將sensor board與mems motherboard相接, 基本上要測試的硬體已經準備好了.


4. 接著到ST官方網頁去下載STEVAL-MKI109V2的相關資料.

    http://www.st.com/web/en/catalog/tools/PF252688



5. 解壓縮後, 可以看到底下的相關檔案, 最重要的就是FIRMWAREUnico的執行檔.



6. 我們首先先使用IAR EWARM先開啟FIRMWARE, 並將Build好的檔案燒錄至mems
    motherboard, 這邊有一個需要特別注意的地方就是需要先將project的Workspace從
    EMOTION_DFU切換到EMOTION_FLASH, 否則一斷電後, 程式就會消失, 需要每次都
    使用ICE將程式燒錄進去.


7. 燒錄完成後, 接著就是安裝Setup_Unico_4.2.0.0.exe程式了, 完成後, pc上會產生Unico tool.


8. 接著就是透過usb cable將mems motherboard與pc相連接, 並開啟Unico tool, 若程式正常執行且
    sensor board也沒有接反的話, 則會有藍色LED亮起.


9. 開啟Unico tool後, 第一個會先要你選擇搭配的sensor board編號, 例如我們使用的為STEVAL-
    MKI108V2, 從清單列表上可以找到此顆編號, 看起來是一顆包含acc/gyro/mag sensor的9 axis
    sensor module, 最後按下Select Device.



10. 接著開始設定Unico tool, 首先先設定STM32 Virtual com-port的編號, 並按下Connect.




11. 連線成功後, 就可以開始對sensor module進行相關的設定, 如果還不熟的話, 也可以先選擇
     Easy Configuration來快速設定, 且連線成功後, mems motherboard上會多亮起3個LED.




12. 也可以直接對各個暫存器進行數值修改.


13. 當設定完成後, 接著按下Start按鈕, 便可以開始觀察數值上的變化.


14. 可以選擇各種不同的觀察資料方式, 例如可以選擇Plot等方式.


15. 這樣比看暫存器輸出的數值要方便地多了.