感謝同事鼎力相借.......T_T
2016年3月31日 星期四
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
spi_example.c
6. example-1: read WHO_AM_I register(0x0F) from LIS3DH.
7. example-2: Enable LIS3DH Sensor.
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
I2C_Master.c
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.
3. 接下來將sensor board與mems motherboard相接, 基本上要測試的硬體已經準備好了.
4. 接著到ST官方網頁去下載STEVAL-MKI109V2的相關資料.
http://www.st.com/web/en/catalog/tools/PF252688
5. 解壓縮後, 可以看到底下的相關檔案, 最重要的就是FIRMWARE和Unico的執行檔.
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. 這樣比看暫存器輸出的數值要方便地多了.
證軟體的時候, 就比較難以知道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. 解壓縮後, 可以看到底下的相關檔案, 最重要的就是FIRMWARE和Unico的執行檔.
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. 這樣比看暫存器輸出的數值要方便地多了.
訂閱:
文章 (Atom)