Bluetooth SPI MBED
Embed:
(wiki syntax)
Show/hide line numbers
main.cpp
00001 #include "mbed.h" 00002 #include "rtos.h" 00003 #include "sdep.h " 00004 #include "Adafruit_FIFO.h " 00005 #include <stdio.h> 00006 00007 #define BLUEFRUIT_MODE_COMMAND HIGH 00008 #define BLUEFRUIT_MODE_DATA LOW 00009 #define BLE_BUFSIZE 4*SDEP_MAX_PACKETSIZE 00010 00011 00012 #define SPI_IGNORED_BYTE 0xFEu /**< SPI default character. Character clocked out in case of an ignored transaction. */ 00013 #define SPI_OVERREAD_BYTE 0xFFu /**< SPI over-read character. Character clocked out after an over-read of the transmit buffer. */ 00014 #define SPI_DEFAULT_DELAY_US 50 00015 00016 DigitalOut myled(LED1); 00017 00018 Serial pc(USBTX,USBRX); 00019 00020 SPI spi(p5, p6, p7); // mosi, miso, sclk 00021 00022 DigitalOut cs(p21); 00023 DigitalIn m_irq_pin(p22); 00024 DigitalOut ble_reset(p23); 00025 00026 void enable_spi() { 00027 cs = 0; 00028 } 00029 00030 void disable_spi() { 00031 cs = 1; 00032 } 00033 00034 // TX 00035 uint8_t m_tx_buffer[SDEP_MAX_PACKETSIZE] = {0}; 00036 uint8_t m_tx_count = 0; 00037 00038 // RX 00039 uint8_t m_rx_buffer[BLE_BUFSIZE * 2] = {0}; 00040 Adafruit_FIFO m_rx_fifo(m_rx_buffer, sizeof(m_rx_buffer), 1, true); 00041 00042 enum BLE_MODE { 00043 COMMAND = 0, 00044 DATA 00045 }; 00046 00047 BLE_MODE bleMode = COMMAND; 00048 00049 00050 // prototypes 00051 void spixfer(void *buff, size_t len); 00052 uint8_t spixfer(uint8_t x); 00053 bool bleGetResponse(void); 00054 bool sendPacket(uint16_t command, const uint8_t* buf, uint8_t count, uint8_t more_data); 00055 size_t bleWriteChar(uint8_t c); 00056 void bleWrite(char *cmd); 00057 00058 00059 volatile unsigned long _millis = 0; 00060 unsigned long millis(void) { 00061 return _millis; 00062 } 00063 00064 00065 class TimeoutTimer 00066 { 00067 private: 00068 uint32_t start; 00069 uint32_t interval; 00070 00071 public: 00072 TimeoutTimer() { start = millis(); interval = 0; } 00073 TimeoutTimer(uint32_t msec) { set(msec); } 00074 00075 void set(uint32_t msec) { start = millis(); interval = msec; } 00076 bool expired(void) const { return (millis() - start) >= interval; } 00077 void restart(void) { start = millis(); } 00078 void reset(void) { start += interval; } // used for periodic invoke to prevent drift 00079 }; 00080 00081 00082 uint16_t word(uint8_t h, uint8_t l) { 00083 uint16_t res = h; 00084 res <<= 8; 00085 res |= l; 00086 return res; 00087 } 00088 00089 00090 uint32_t _timeout = 250; 00091 00092 00093 bool bleGetPacket(sdepMsgResponse_t * p_response) 00094 { 00095 // Wait until IRQ is asserted, double timeout since some commands take long time to start responding 00096 TimeoutTimer tt(2*_timeout); 00097 00098 while ( !m_irq_pin ) { 00099 if (tt.expired()) return false; 00100 } 00101 00102 sdepMsgHeader_t * p_header = &p_response->header; 00103 00104 enable_spi(); 00105 00106 tt.set(_timeout); 00107 00108 do { 00109 if ( tt.expired() ) break; 00110 00111 p_header->msg_type = spixfer(0xff); 00112 00113 if (p_header->msg_type == SPI_IGNORED_BYTE) 00114 { 00115 // Bluefruit may not be ready 00116 // Disable & Re-enable CS with a bit of delay for Bluefruit to ready itself 00117 disable_spi(); 00118 wait_us(50); 00119 enable_spi(); 00120 } 00121 else if (p_header->msg_type == SPI_OVERREAD_BYTE) 00122 { 00123 // IRQ may not be pulled down by Bluefruit when returning all data in previous transfer. 00124 // This could happen when Arduino MCU is running at fast rate comparing to Bluefruit's MCU, 00125 // causing an SPI_OVERREAD_BYTE to be returned at stage. 00126 // 00127 // Walkaround: Disable & Re-enable CS with a bit of delay and keep waiting 00128 // TODO IRQ is supposed to be OFF then ON, it is better to use GPIO trigger interrupt. 00129 00130 disable_spi(); 00131 // wait for the clock to be enabled.. 00132 // while (!digitalRead(m_irq_pin)) { 00133 // if ( tt.expired() ) break; 00134 // } 00135 // if (!digitalRead(m_irq_pin)) break; 00136 wait_us(50); 00137 enable_spi(); 00138 } 00139 } while (p_header->msg_type == SPI_IGNORED_BYTE || p_header->msg_type == SPI_OVERREAD_BYTE); 00140 00141 bool result=false; 00142 00143 // Not a loop, just a way to avoid goto with error handling 00144 do 00145 { 00146 // Look for the header 00147 // note that we should always get the right header at this point, and not doing so will really mess up things. 00148 while ( p_header->msg_type != SDEP_MSGTYPE_RESPONSE && p_header->msg_type != SDEP_MSGTYPE_ERROR && !tt.expired() ) 00149 { 00150 p_header->msg_type = spixfer(0xff); 00151 } 00152 00153 if ( tt.expired() ) break; 00154 00155 memset( (&p_header->msg_type)+1, 0xff, sizeof(sdepMsgHeader_t ) - 1); 00156 spixfer((&p_header->msg_type)+1, sizeof(sdepMsgHeader_t ) - 1); 00157 00158 // Command is 16-bit at odd address, may have alignment issue with 32-bit chip 00159 uint16_t cmd_id = word(p_header->cmd_id_high, p_header->cmd_id_low); 00160 00161 // Error Message Response 00162 if ( p_header->msg_type == SDEP_MSGTYPE_ERROR ) break; 00163 00164 // Invalid command 00165 if (!(cmd_id == SDEP_CMDTYPE_AT_WRAPPER || 00166 cmd_id == SDEP_CMDTYPE_BLE_UARTTX || 00167 cmd_id == SDEP_CMDTYPE_BLE_UARTRX) ) 00168 { 00169 break; 00170 } 00171 00172 // Invalid length 00173 if(p_header->length > SDEP_MAX_PACKETSIZE) break; 00174 00175 // read payload 00176 memset(p_response->payload, 0xff, p_header->length); 00177 spixfer(p_response->payload, p_header->length); 00178 00179 result = true; 00180 }while(0); 00181 00182 disable_spi(); 00183 00184 return result; 00185 } 00186 00187 00188 /******************************************************************************/ 00189 /*! 00190 00191 */ 00192 /******************************************************************************/ 00193 void spixfer(void *buff, size_t len) { 00194 uint8_t *p = (uint8_t *)buff; 00195 while (len--) { 00196 p[0] = spixfer(p[0]); 00197 p++; 00198 } 00199 } 00200 00201 /******************************************************************************/ 00202 /*! 00203 00204 */ 00205 /******************************************************************************/ 00206 uint8_t spixfer(uint8_t x) { 00207 return spi.write(x); 00208 } 00209 00210 00211 /******************************************************************************/ 00212 /*! 00213 @brief Try to perform an full AT response transfer from Bluefruit, or execute 00214 as many SPI transaction as internal FIFO can hold up. 00215 00216 @note If verbose is enabled, all the received data will be print to Serial 00217 00218 @return 00219 - true : if succeeded 00220 - false : if failed 00221 */ 00222 /******************************************************************************/ 00223 bool bleGetResponse(void) 00224 { 00225 // Try to read data from Bluefruit if there is enough room in the fifo 00226 while ( m_rx_fifo.remaining() >= SDEP_MAX_PACKETSIZE ) 00227 { 00228 // Get a SDEP packet 00229 sdepMsgResponse_t msg_response; 00230 memset(&msg_response, 0, sizeof(sdepMsgResponse_t )); 00231 00232 if ( !bleGetPacket(&msg_response) ) return false; 00233 00234 // Write to fifo 00235 if ( msg_response.header.length > 0) 00236 { 00237 m_rx_fifo.write_n(msg_response.payload, msg_response.header.length); 00238 } 00239 00240 // No more packet data 00241 if ( !msg_response.header.more_data ) break; 00242 00243 // It takes a bit since all Data received to IRQ to get LOW 00244 // May need to delay a bit for it to be stable before the next try 00245 wait_us(50); 00246 } 00247 00248 return true; 00249 } 00250 00251 00252 void bleWrite(char *cmd) { 00253 while (*cmd != '\0') { 00254 bleWriteChar((uint8_t) *cmd); 00255 cmd += 1; 00256 } 00257 } 00258 00259 00260 /******************************************************************************/ 00261 /*! 00262 @brief Check if the response from the previous command is ready 00263 00264 @return 'true' if a response is ready, otherwise 'false' 00265 */ 00266 /******************************************************************************/ 00267 int bleAvailable(void) 00268 { 00269 if (! m_rx_fifo.empty() ) { 00270 return m_rx_fifo.count(); 00271 } 00272 00273 if ( bleMode == DATA ) 00274 { 00275 // DATA Mode: query for BLE UART data 00276 sendPacket(SDEP_CMDTYPE_BLE_UARTRX, NULL, 0, 0); 00277 00278 // Waiting to get response from Bluefruit 00279 bleGetResponse(); 00280 00281 return m_rx_fifo.count(); 00282 }else{ 00283 return m_irq_pin; 00284 } 00285 } 00286 00287 /******************************************************************************/ 00288 /*! 00289 @brief Get a byte from response data, perform SPI transaction if needed 00290 00291 @return -1 if no data is available 00292 */ 00293 /******************************************************************************/ 00294 int bleRead(void) 00295 { 00296 uint8_t ch; 00297 00298 // try to grab from buffer first... 00299 if (!m_rx_fifo.empty()) { 00300 m_rx_fifo.read(&ch); 00301 return (int)ch; 00302 } 00303 00304 if ( bleMode == DATA ) 00305 { 00306 // DATA Mode: query for BLE UART data 00307 sendPacket(SDEP_CMDTYPE_BLE_UARTRX, NULL, 0, 0); 00308 00309 // Waiting to get response from Bluefruit 00310 bleGetResponse(); 00311 }else 00312 { 00313 // COMMAND Mode: Only read data from Bluefruit if IRQ is raised 00314 if ( m_irq_pin ) bleGetResponse(); 00315 } 00316 00317 return m_rx_fifo.read(&ch) ? ((int) ch) : EOF; 00318 00319 } 00320 00321 00322 uint16_t bleReadLine(char * buf, uint16_t bufsize) 00323 { 00324 uint16_t timeout = 1000 * 10; 00325 bool multiline = false; 00326 uint16_t replyidx = 0; 00327 00328 while (timeout--) { 00329 while(bleAvailable()) { 00330 //pc.printf("SPI: Available\n"); 00331 char c = bleRead(); 00332 //pc.printf("SPI: %d\n", (int) c); 00333 //SerialDebug.println(c); 00334 00335 if (c == '\r') continue; 00336 00337 if (c == '\n') { 00338 // the first '\n' is ignored 00339 if (replyidx == 0) continue; 00340 00341 if (!multiline) { 00342 timeout = 0; 00343 break; 00344 } 00345 } 00346 buf[replyidx] = c; 00347 replyidx++; 00348 00349 // Buffer is full 00350 if (replyidx >= bufsize) { 00351 //if (_verbose) { SerialDebug.println("*overflow*"); } // for my debuggin' only! 00352 timeout = 0; 00353 break; 00354 } 00355 } 00356 00357 // delay if needed 00358 if (timeout) { 00359 Thread::wait(1); 00360 } 00361 } 00362 00363 buf[replyidx] = 0; // null term 00364 00365 return replyidx; 00366 } 00367 00368 bool waitForOK(void) 00369 { 00370 // Use temp buffer to avoid overwrite returned result if any 00371 char tempbuf[BLE_BUFSIZE + 1]; 00372 00373 while (bleReadLine(tempbuf, BLE_BUFSIZE)) { 00374 // pc.printf("SPI: %s\n", tempbuf); 00375 if (strcmp(tempbuf, "OK") == 0) return true; 00376 if (strcmp(tempbuf, "ERROR") == 0) return false; 00377 } 00378 return false; 00379 } 00380 00381 bool sendATCommand(char *cmd) { 00382 bleMode = COMMAND; 00383 bleWrite(cmd); 00384 bleWrite("\r\n"); 00385 bool result = waitForOK(); 00386 bleMode = DATA; 00387 return result; 00388 } 00389 00390 bool sendCommandCheckOK(char *cmd) { 00391 return sendATCommand(cmd); 00392 } 00393 00394 // ============================================================================= 00395 00396 void flush(void) 00397 { 00398 m_rx_fifo.clear(); 00399 } 00400 00401 uint8_t highByte(uint16_t x) { 00402 return (uint8_t) (x >> 8); 00403 } 00404 00405 uint8_t lowByte(uint16_t x) { 00406 return (uint8_t) (x & 0xff); 00407 } 00408 00409 bool sendPacket(uint16_t command, const uint8_t* buf, uint8_t count, uint8_t more_data) { 00410 // flush old response before sending the new command 00411 if (more_data == 0) flush(); 00412 00413 sdepMsgCommand_t msgCmd; 00414 00415 msgCmd.header.msg_type = SDEP_MSGTYPE_COMMAND; 00416 msgCmd.header.cmd_id_high = highByte(command); 00417 msgCmd.header.cmd_id_low = lowByte(command); 00418 msgCmd.header.length = count; 00419 msgCmd.header.more_data = (count == SDEP_MAX_PACKETSIZE) ? more_data : 0; 00420 00421 // Copy payload 00422 if ( buf != NULL && count > 0) memcpy(msgCmd.payload, buf, count); 00423 00424 enable_spi(); 00425 00426 TimeoutTimer tt(_timeout); 00427 00428 // Bluefruit may not be ready 00429 while ( ( spixfer(msgCmd.header.msg_type) == SPI_IGNORED_BYTE ) && !tt.expired() ) 00430 { 00431 // Disable & Re-enable CS with a bit of delay for Bluefruit to ready itself 00432 disable_spi(); 00433 wait_us(SPI_DEFAULT_DELAY_US); 00434 enable_spi(); 00435 } 00436 00437 bool result = !tt.expired(); 00438 if ( result ) 00439 { 00440 // transfer the rest of the data 00441 spixfer((void*) (((uint8_t*)&msgCmd) +1), sizeof(sdepMsgHeader_t )+count-1); 00442 } 00443 00444 disable_spi(); 00445 00446 return result; 00447 } 00448 00449 size_t bleWriteChar(uint8_t c) { 00450 if (bleMode == DATA) 00451 { 00452 sendPacket(SDEP_CMDTYPE_BLE_UARTTX, &c, 1, 0); 00453 bleGetResponse(); 00454 return 1; 00455 } 00456 00457 // Following code handle BLUEFRUIT_MODE_COMMAND 00458 00459 // Final packet due to \r or \n terminator 00460 if (c == '\r' || c == '\n') 00461 { 00462 if (m_tx_count > 0) 00463 { 00464 bool result = sendPacket(SDEP_CMDTYPE_AT_WRAPPER, m_tx_buffer, m_tx_count, 0); 00465 m_tx_count = 0; 00466 if (!result) { 00467 return 0; 00468 } 00469 } 00470 } 00471 // More than max packet buffered --> send with more_data = 1 00472 else if (m_tx_count == SDEP_MAX_PACKETSIZE) 00473 { 00474 bool result = sendPacket(SDEP_CMDTYPE_AT_WRAPPER, m_tx_buffer, m_tx_count, 1); 00475 00476 m_tx_buffer[0] = c; 00477 m_tx_count = 1; 00478 if (!result) { 00479 return 0; 00480 } 00481 } 00482 // Not enough data, continue to buffer 00483 else 00484 { 00485 m_tx_buffer[m_tx_count++] = c; 00486 } 00487 return 1; 00488 } 00489 00490 int32_t readline_parseInt(void) 00491 { 00492 char buffer[101] = { 0 }; 00493 uint16_t len = bleReadLine(buffer, 100); 00494 if (len == 0) return 0; 00495 00496 // also parsed hex number e.g 0xADAF 00497 int32_t val = strtol(buffer, NULL, 0); 00498 00499 return val; 00500 } 00501 00502 bool send_arg_get_resp(int32_t *reply) { 00503 bleWrite("\r\n"); // execute command 00504 00505 // parse integer response if required 00506 if (reply) 00507 { 00508 (*reply) = readline_parseInt(); 00509 } 00510 00511 // check OK or ERROR status 00512 return waitForOK(); 00513 } 00514 00515 int32_t sendATCommandIntReply(char *cmd) { 00516 int32_t reply = 0; 00517 BLE_MODE current_mode = bleMode; 00518 00519 // switch mode if necessary to execute command 00520 bleMode = COMMAND; 00521 00522 // Execute command with parameter and get response 00523 bleWrite(cmd); 00524 send_arg_get_resp(&reply); 00525 00526 // switch back if necessary 00527 if ( current_mode == DATA ) bleMode = DATA; 00528 return reply; 00529 } 00530 00531 bool sendInitializePattern() { 00532 return sendPacket(SDEP_CMDTYPE_INITIALIZE, NULL, 0, 0); 00533 } 00534 00535 bool isBLEConnected() { 00536 int32_t connected = 0; 00537 connected = sendATCommandIntReply("AT+GAPGETCONN"); 00538 return connected; 00539 } 00540 00541 00542 void timer_function_millis(void const *n) { 00543 _millis += 1; 00544 } 00545 00546 int main() { 00547 // timer to bump _millis 00548 RtosTimer millis_timer(timer_function_millis, osTimerPeriodic, (void *)0); 00549 millis_timer.start(1); 00550 00551 pc.baud(115200); 00552 spi.format(8, 0); 00553 spi.frequency(4000000); 00554 00555 pc.printf("Prepare to initiaize BLE\n"); 00556 00557 // initialize BLE 00558 cs = 1; 00559 if (!sendInitializePattern()) pc.printf("BLE Init Failed\n"); 00560 ble_reset = 1; 00561 ble_reset = 0; 00562 Thread::wait(10); 00563 ble_reset = 1; 00564 Thread::wait(500); 00565 cs = 1; 00566 00567 pc.printf("Set BLE Name\n"); 00568 00569 // change name 00570 if (!sendCommandCheckOK("AT+GAPDEVNAME=AdafruitDJ")) { 00571 pc.printf("Could not set device name.\n"); 00572 } 00573 00574 // wait until connected 00575 while (!isBLEConnected()) { 00576 Thread::wait(100); 00577 } 00578 00579 pc.printf("Ready\n"); 00580 00581 Thread::wait(1000); 00582 00583 int i = 0; 00584 while (true) { 00585 bleMode = DATA; 00586 // send character 00587 char str[40] = { 0 }; 00588 sprintf(str, "Hello DJ %d\r\n", i); 00589 bleWrite(str); 00590 Thread::wait(1000); 00591 pc.printf("Send\n"); 00592 i += 1; 00593 } 00594 00595 Thread::wait(osWaitForever); 00596 }
Generated on Fri Aug 12 2022 08:42:17 by
1.7.2