USBHost library

Dependencies:   FATFileSystem mbed-rtos

Fork of USBHost by mbed official

Committer:
donatien
Date:
Thu Apr 10 17:38:43 2014 +0000
Revision:
25:aa23798d4ab0
Parent:
13:b58a2204422f
Updated mbed-rtos; limiting to 1 connected device to avoid starving device's memory

Who changed what in which revision?

UserRevisionLine numberNew contents of line
samux 8:93da8ea2708b 1 /* mbed USBHost Library
samux 8:93da8ea2708b 2 * Copyright (c) 2006-2013 ARM Limited
samux 8:93da8ea2708b 3 *
samux 8:93da8ea2708b 4 * Licensed under the Apache License, Version 2.0 (the "License");
samux 8:93da8ea2708b 5 * you may not use this file except in compliance with the License.
samux 8:93da8ea2708b 6 * You may obtain a copy of the License at
samux 8:93da8ea2708b 7 *
samux 8:93da8ea2708b 8 * http://www.apache.org/licenses/LICENSE-2.0
samux 8:93da8ea2708b 9 *
samux 8:93da8ea2708b 10 * Unless required by applicable law or agreed to in writing, software
samux 8:93da8ea2708b 11 * distributed under the License is distributed on an "AS IS" BASIS,
samux 8:93da8ea2708b 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
samux 8:93da8ea2708b 13 * See the License for the specific language governing permissions and
samux 8:93da8ea2708b 14 * limitations under the License.
samux 8:93da8ea2708b 15 */
mbed_official 0:a554658735bf 16
mbed_official 0:a554658735bf 17 #include "mbed.h"
mbed_official 0:a554658735bf 18 #include "USBHALHost.h"
mbed_official 0:a554658735bf 19 #include "dbg.h"
mbed_official 0:a554658735bf 20
mbed_official 0:a554658735bf 21 // bits of the USB/OTG clock control register
mbed_official 0:a554658735bf 22 #define HOST_CLK_EN (1<<0)
mbed_official 0:a554658735bf 23 #define DEV_CLK_EN (1<<1)
mbed_official 0:a554658735bf 24 #define PORTSEL_CLK_EN (1<<3)
mbed_official 0:a554658735bf 25 #define AHB_CLK_EN (1<<4)
mbed_official 0:a554658735bf 26
mbed_official 0:a554658735bf 27 // bits of the USB/OTG clock status register
mbed_official 0:a554658735bf 28 #define HOST_CLK_ON (1<<0)
mbed_official 0:a554658735bf 29 #define DEV_CLK_ON (1<<1)
mbed_official 0:a554658735bf 30 #define PORTSEL_CLK_ON (1<<3)
mbed_official 0:a554658735bf 31 #define AHB_CLK_ON (1<<4)
mbed_official 0:a554658735bf 32
mbed_official 0:a554658735bf 33 // we need host clock, OTG/portsel clock and AHB clock
mbed_official 0:a554658735bf 34 #define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
mbed_official 0:a554658735bf 35
mbed_official 0:a554658735bf 36 #define HCCA_SIZE sizeof(HCCA)
mbed_official 0:a554658735bf 37 #define ED_SIZE sizeof(HCED)
mbed_official 0:a554658735bf 38 #define TD_SIZE sizeof(HCTD)
mbed_official 0:a554658735bf 39
mbed_official 0:a554658735bf 40 #define TOTAL_SIZE (HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE) + (MAX_TD*TD_SIZE))
mbed_official 0:a554658735bf 41
mbed_official 13:b58a2204422f 42 static volatile uint8_t usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM1"),aligned(256))); //256 bytes aligned!
mbed_official 0:a554658735bf 43
mbed_official 0:a554658735bf 44 USBHALHost * USBHALHost::instHost;
mbed_official 0:a554658735bf 45
mbed_official 0:a554658735bf 46 USBHALHost::USBHALHost() {
mbed_official 0:a554658735bf 47 instHost = this;
mbed_official 0:a554658735bf 48 memInit();
mbed_official 0:a554658735bf 49 memset((void*)usb_hcca, 0, HCCA_SIZE);
mbed_official 0:a554658735bf 50 for (int i = 0; i < MAX_ENDPOINT; i++) {
mbed_official 0:a554658735bf 51 edBufAlloc[i] = false;
mbed_official 0:a554658735bf 52 }
mbed_official 0:a554658735bf 53 for (int i = 0; i < MAX_TD; i++) {
mbed_official 0:a554658735bf 54 tdBufAlloc[i] = false;
mbed_official 0:a554658735bf 55 }
mbed_official 0:a554658735bf 56 }
mbed_official 0:a554658735bf 57
mbed_official 0:a554658735bf 58 void USBHALHost::init() {
mbed_official 0:a554658735bf 59 NVIC_DisableIRQ(USB_IRQn);
mbed_official 0:a554658735bf 60
mbed_official 0:a554658735bf 61 //Cut power
mbed_official 0:a554658735bf 62 LPC_SC->PCONP &= ~(1UL<<31);
mbed_official 0:a554658735bf 63 wait_ms(100);
mbed_official 0:a554658735bf 64
mbed_official 0:a554658735bf 65 // turn on power for USB
mbed_official 0:a554658735bf 66 LPC_SC->PCONP |= (1UL<<31);
mbed_official 0:a554658735bf 67
mbed_official 0:a554658735bf 68 // Enable USB host clock, port selection and AHB clock
mbed_official 0:a554658735bf 69 LPC_USB->USBClkCtrl |= CLOCK_MASK;
mbed_official 0:a554658735bf 70
mbed_official 0:a554658735bf 71 // Wait for clocks to become available
mbed_official 0:a554658735bf 72 while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK);
mbed_official 0:a554658735bf 73
mbed_official 0:a554658735bf 74 // it seems the bits[0:1] mean the following
mbed_official 0:a554658735bf 75 // 0: U1=device, U2=host
mbed_official 0:a554658735bf 76 // 1: U1=host, U2=host
mbed_official 0:a554658735bf 77 // 2: reserved
mbed_official 0:a554658735bf 78 // 3: U1=host, U2=device
mbed_official 0:a554658735bf 79 // NB: this register is only available if OTG clock (aka "port select") is enabled!!
mbed_official 0:a554658735bf 80 // since we don't care about port 2, set just bit 0 to 1 (U1=host)
mbed_official 0:a554658735bf 81 LPC_USB->OTGStCtrl |= 1;
mbed_official 0:a554658735bf 82
mbed_official 0:a554658735bf 83 // now that we've configured the ports, we can turn off the portsel clock
mbed_official 0:a554658735bf 84 LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
mbed_official 0:a554658735bf 85
mbed_official 0:a554658735bf 86 // configure USB D+/D- pins
mbed_official 0:a554658735bf 87 // P0[29] = USB_D+, 01
mbed_official 0:a554658735bf 88 // P0[30] = USB_D-, 01
mbed_official 0:a554658735bf 89 LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));
mbed_official 0:a554658735bf 90 LPC_PINCON->PINSEL1 |= ((1<<26) | (1<<28));
mbed_official 0:a554658735bf 91
mbed_official 0:a554658735bf 92 LPC_USB->HcControl = 0; // HARDWARE RESET
mbed_official 0:a554658735bf 93 LPC_USB->HcControlHeadED = 0; // Initialize Control list head to Zero
mbed_official 0:a554658735bf 94 LPC_USB->HcBulkHeadED = 0; // Initialize Bulk list head to Zero
mbed_official 0:a554658735bf 95
mbed_official 0:a554658735bf 96 // Wait 100 ms before apply reset
mbed_official 0:a554658735bf 97 wait_ms(100);
mbed_official 0:a554658735bf 98
mbed_official 0:a554658735bf 99 // software reset
mbed_official 0:a554658735bf 100 LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
mbed_official 0:a554658735bf 101
mbed_official 0:a554658735bf 102 // Write Fm Interval and Largest Data Packet Counter
mbed_official 0:a554658735bf 103 LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL;
mbed_official 0:a554658735bf 104 LPC_USB->HcPeriodicStart = FI * 90 / 100;
mbed_official 0:a554658735bf 105
mbed_official 0:a554658735bf 106 // Put HC in operational state
mbed_official 0:a554658735bf 107 LPC_USB->HcControl = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
mbed_official 0:a554658735bf 108 // Set Global Power
mbed_official 0:a554658735bf 109 LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;
mbed_official 0:a554658735bf 110
mbed_official 0:a554658735bf 111 LPC_USB->HcHCCA = (uint32_t)(usb_hcca);
mbed_official 0:a554658735bf 112
mbed_official 0:a554658735bf 113 // Clear Interrrupt Status
mbed_official 0:a554658735bf 114 LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;
mbed_official 0:a554658735bf 115
mbed_official 0:a554658735bf 116 LPC_USB->HcInterruptEnable = OR_INTR_ENABLE_MIE | OR_INTR_ENABLE_WDH | OR_INTR_ENABLE_RHSC;
mbed_official 0:a554658735bf 117
mbed_official 0:a554658735bf 118 // Enable the USB Interrupt
mbed_official 0:a554658735bf 119 NVIC_SetVector(USB_IRQn, (uint32_t)(_usbisr));
mbed_official 0:a554658735bf 120 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
mbed_official 0:a554658735bf 121 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
mbed_official 0:a554658735bf 122
mbed_official 0:a554658735bf 123 NVIC_EnableIRQ(USB_IRQn);
mbed_official 0:a554658735bf 124
mbed_official 0:a554658735bf 125 // Check for any connected devices
mbed_official 0:a554658735bf 126 if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) {
mbed_official 0:a554658735bf 127 //Device connected
samux 4:b320d68e98e7 128 wait_ms(150);
mbed_official 0:a554658735bf 129 USB_DBG("Device connected (%08x)\n\r", LPC_USB->HcRhPortStatus1);
mbed_official 0:a554658735bf 130 deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA);
mbed_official 0:a554658735bf 131 }
mbed_official 0:a554658735bf 132 }
mbed_official 0:a554658735bf 133
mbed_official 0:a554658735bf 134 uint32_t USBHALHost::controlHeadED() {
mbed_official 0:a554658735bf 135 return LPC_USB->HcControlHeadED;
mbed_official 0:a554658735bf 136 }
mbed_official 0:a554658735bf 137
mbed_official 0:a554658735bf 138 uint32_t USBHALHost::bulkHeadED() {
mbed_official 0:a554658735bf 139 return LPC_USB->HcBulkHeadED;
mbed_official 0:a554658735bf 140 }
mbed_official 0:a554658735bf 141
mbed_official 0:a554658735bf 142 uint32_t USBHALHost::interruptHeadED() {
mbed_official 0:a554658735bf 143 return usb_hcca->IntTable[0];
mbed_official 0:a554658735bf 144 }
mbed_official 0:a554658735bf 145
mbed_official 0:a554658735bf 146 void USBHALHost::updateBulkHeadED(uint32_t addr) {
mbed_official 0:a554658735bf 147 LPC_USB->HcBulkHeadED = addr;
mbed_official 0:a554658735bf 148 }
mbed_official 0:a554658735bf 149
mbed_official 0:a554658735bf 150
mbed_official 0:a554658735bf 151 void USBHALHost::updateControlHeadED(uint32_t addr) {
mbed_official 0:a554658735bf 152 LPC_USB->HcControlHeadED = addr;
mbed_official 0:a554658735bf 153 }
mbed_official 0:a554658735bf 154
mbed_official 0:a554658735bf 155 void USBHALHost::updateInterruptHeadED(uint32_t addr) {
mbed_official 0:a554658735bf 156 usb_hcca->IntTable[0] = addr;
mbed_official 0:a554658735bf 157 }
mbed_official 0:a554658735bf 158
mbed_official 0:a554658735bf 159
mbed_official 0:a554658735bf 160 void USBHALHost::enableList(ENDPOINT_TYPE type) {
mbed_official 0:a554658735bf 161 switch(type) {
mbed_official 0:a554658735bf 162 case CONTROL_ENDPOINT:
mbed_official 0:a554658735bf 163 LPC_USB->HcCommandStatus = OR_CMD_STATUS_CLF;
mbed_official 0:a554658735bf 164 LPC_USB->HcControl |= OR_CONTROL_CLE;
mbed_official 0:a554658735bf 165 break;
mbed_official 0:a554658735bf 166 case ISOCHRONOUS_ENDPOINT:
mbed_official 0:a554658735bf 167 break;
mbed_official 0:a554658735bf 168 case BULK_ENDPOINT:
mbed_official 0:a554658735bf 169 LPC_USB->HcCommandStatus = OR_CMD_STATUS_BLF;
mbed_official 0:a554658735bf 170 LPC_USB->HcControl |= OR_CONTROL_BLE;
mbed_official 0:a554658735bf 171 break;
mbed_official 0:a554658735bf 172 case INTERRUPT_ENDPOINT:
mbed_official 0:a554658735bf 173 LPC_USB->HcControl |= OR_CONTROL_PLE;
mbed_official 0:a554658735bf 174 break;
mbed_official 0:a554658735bf 175 }
mbed_official 0:a554658735bf 176 }
mbed_official 0:a554658735bf 177
mbed_official 0:a554658735bf 178
mbed_official 0:a554658735bf 179 bool USBHALHost::disableList(ENDPOINT_TYPE type) {
mbed_official 0:a554658735bf 180 switch(type) {
mbed_official 0:a554658735bf 181 case CONTROL_ENDPOINT:
mbed_official 0:a554658735bf 182 if(LPC_USB->HcControl & OR_CONTROL_CLE) {
mbed_official 0:a554658735bf 183 LPC_USB->HcControl &= ~OR_CONTROL_CLE;
mbed_official 0:a554658735bf 184 return true;
mbed_official 0:a554658735bf 185 }
mbed_official 0:a554658735bf 186 return false;
mbed_official 0:a554658735bf 187 case ISOCHRONOUS_ENDPOINT:
mbed_official 0:a554658735bf 188 return false;
mbed_official 0:a554658735bf 189 case BULK_ENDPOINT:
mbed_official 0:a554658735bf 190 if(LPC_USB->HcControl & OR_CONTROL_BLE){
mbed_official 0:a554658735bf 191 LPC_USB->HcControl &= ~OR_CONTROL_BLE;
mbed_official 0:a554658735bf 192 return true;
mbed_official 0:a554658735bf 193 }
mbed_official 0:a554658735bf 194 return false;
mbed_official 0:a554658735bf 195 case INTERRUPT_ENDPOINT:
mbed_official 0:a554658735bf 196 if(LPC_USB->HcControl & OR_CONTROL_PLE) {
mbed_official 0:a554658735bf 197 LPC_USB->HcControl &= ~OR_CONTROL_PLE;
mbed_official 0:a554658735bf 198 return true;
mbed_official 0:a554658735bf 199 }
mbed_official 0:a554658735bf 200 return false;
mbed_official 0:a554658735bf 201 }
mbed_official 0:a554658735bf 202 return false;
mbed_official 0:a554658735bf 203 }
mbed_official 0:a554658735bf 204
mbed_official 0:a554658735bf 205
mbed_official 0:a554658735bf 206 void USBHALHost::memInit() {
mbed_official 0:a554658735bf 207 usb_hcca = (volatile HCCA *)usb_buf;
mbed_official 0:a554658735bf 208 usb_edBuf = usb_buf + HCCA_SIZE;
mbed_official 0:a554658735bf 209 usb_tdBuf = usb_buf + HCCA_SIZE + (MAX_ENDPOINT*ED_SIZE);
mbed_official 0:a554658735bf 210 }
mbed_official 0:a554658735bf 211
mbed_official 0:a554658735bf 212 volatile uint8_t * USBHALHost::getED() {
mbed_official 0:a554658735bf 213 for (int i = 0; i < MAX_ENDPOINT; i++) {
mbed_official 0:a554658735bf 214 if ( !edBufAlloc[i] ) {
mbed_official 0:a554658735bf 215 edBufAlloc[i] = true;
mbed_official 0:a554658735bf 216 return (volatile uint8_t *)(usb_edBuf + i*ED_SIZE);
mbed_official 0:a554658735bf 217 }
mbed_official 0:a554658735bf 218 }
mbed_official 0:a554658735bf 219 perror("Could not allocate ED\r\n");
mbed_official 0:a554658735bf 220 return NULL; //Could not alloc ED
mbed_official 0:a554658735bf 221 }
mbed_official 0:a554658735bf 222
mbed_official 0:a554658735bf 223 volatile uint8_t * USBHALHost::getTD() {
mbed_official 0:a554658735bf 224 int i;
mbed_official 0:a554658735bf 225 for (i = 0; i < MAX_TD; i++) {
mbed_official 0:a554658735bf 226 if ( !tdBufAlloc[i] ) {
mbed_official 0:a554658735bf 227 tdBufAlloc[i] = true;
mbed_official 0:a554658735bf 228 return (volatile uint8_t *)(usb_tdBuf + i*TD_SIZE);
mbed_official 0:a554658735bf 229 }
mbed_official 0:a554658735bf 230 }
mbed_official 0:a554658735bf 231 perror("Could not allocate TD\r\n");
mbed_official 0:a554658735bf 232 return NULL; //Could not alloc TD
mbed_official 0:a554658735bf 233 }
mbed_official 0:a554658735bf 234
mbed_official 0:a554658735bf 235
mbed_official 0:a554658735bf 236 void USBHALHost::freeED(volatile uint8_t * ed) {
mbed_official 0:a554658735bf 237 int i;
mbed_official 0:a554658735bf 238 i = (ed - usb_edBuf) / ED_SIZE;
mbed_official 0:a554658735bf 239 edBufAlloc[i] = false;
mbed_official 0:a554658735bf 240 }
mbed_official 0:a554658735bf 241
mbed_official 0:a554658735bf 242 void USBHALHost::freeTD(volatile uint8_t * td) {
mbed_official 0:a554658735bf 243 int i;
mbed_official 0:a554658735bf 244 i = (td - usb_tdBuf) / TD_SIZE;
mbed_official 0:a554658735bf 245 tdBufAlloc[i] = false;
mbed_official 0:a554658735bf 246 }
mbed_official 0:a554658735bf 247
mbed_official 0:a554658735bf 248
mbed_official 0:a554658735bf 249 void USBHALHost::resetRootHub() {
mbed_official 0:a554658735bf 250 // Initiate port reset
samux 4:b320d68e98e7 251 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS;
mbed_official 0:a554658735bf 252
mbed_official 0:a554658735bf 253 while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS);
mbed_official 0:a554658735bf 254
mbed_official 0:a554658735bf 255 // ...and clear port reset signal
mbed_official 0:a554658735bf 256 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
mbed_official 0:a554658735bf 257 }
mbed_official 0:a554658735bf 258
mbed_official 0:a554658735bf 259
mbed_official 0:a554658735bf 260 void USBHALHost::_usbisr(void) {
mbed_official 0:a554658735bf 261 if (instHost) {
mbed_official 0:a554658735bf 262 instHost->UsbIrqhandler();
mbed_official 0:a554658735bf 263 }
mbed_official 0:a554658735bf 264 }
mbed_official 0:a554658735bf 265
mbed_official 0:a554658735bf 266 void USBHALHost::UsbIrqhandler() {
mbed_official 0:a554658735bf 267 if( LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable ) //Is there something to actually process?
mbed_official 0:a554658735bf 268 {
mbed_official 0:a554658735bf 269
mbed_official 0:a554658735bf 270 uint32_t int_status = LPC_USB->HcInterruptStatus & LPC_USB->HcInterruptEnable;
mbed_official 0:a554658735bf 271
mbed_official 0:a554658735bf 272 // Root hub status change interrupt
mbed_official 0:a554658735bf 273 if (int_status & OR_INTR_STATUS_RHSC) {
mbed_official 0:a554658735bf 274 if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC) {
mbed_official 0:a554658735bf 275 if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE) {
mbed_official 0:a554658735bf 276 // When DRWE is on, Connect Status Change
mbed_official 0:a554658735bf 277 // means a remote wakeup event.
mbed_official 0:a554658735bf 278 } else {
mbed_official 0:a554658735bf 279
mbed_official 0:a554658735bf 280 //Root device connected
mbed_official 0:a554658735bf 281 if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) {
mbed_official 0:a554658735bf 282
samux 4:b320d68e98e7 283 // wait 150ms to avoid bounce
samux 4:b320d68e98e7 284 wait_ms(150);
mbed_official 0:a554658735bf 285
mbed_official 0:a554658735bf 286 //Hub 0 (root hub), Port 1 (count starts at 1), Low or High speed
mbed_official 0:a554658735bf 287 deviceConnected(0, 1, LPC_USB->HcRhPortStatus1 & OR_RH_PORT_LSDA);
mbed_official 0:a554658735bf 288 }
mbed_official 0:a554658735bf 289
mbed_official 0:a554658735bf 290 //Root device disconnected
mbed_official 0:a554658735bf 291 else {
mbed_official 0:a554658735bf 292
mbed_official 0:a554658735bf 293 if (!(int_status & OR_INTR_STATUS_WDH)) {
mbed_official 0:a554658735bf 294 usb_hcca->DoneHead = 0;
mbed_official 0:a554658735bf 295 }
mbed_official 0:a554658735bf 296
samux 4:b320d68e98e7 297 // wait 200ms to avoid bounce
samux 4:b320d68e98e7 298 wait_ms(200);
mbed_official 0:a554658735bf 299
mbed_official 0:a554658735bf 300 deviceDisconnected(0, 1, NULL, usb_hcca->DoneHead & 0xFFFFFFFE);
mbed_official 0:a554658735bf 301
mbed_official 0:a554658735bf 302 if (int_status & OR_INTR_STATUS_WDH) {
mbed_official 0:a554658735bf 303 usb_hcca->DoneHead = 0;
mbed_official 0:a554658735bf 304 LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
mbed_official 0:a554658735bf 305 }
mbed_official 0:a554658735bf 306 }
mbed_official 0:a554658735bf 307 }
mbed_official 0:a554658735bf 308 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
mbed_official 0:a554658735bf 309 }
mbed_official 0:a554658735bf 310 if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC) {
mbed_official 0:a554658735bf 311 LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
mbed_official 0:a554658735bf 312 }
mbed_official 0:a554658735bf 313 LPC_USB->HcInterruptStatus = OR_INTR_STATUS_RHSC;
mbed_official 0:a554658735bf 314 }
mbed_official 0:a554658735bf 315
mbed_official 0:a554658735bf 316 // Writeback Done Head interrupt
mbed_official 0:a554658735bf 317 if (int_status & OR_INTR_STATUS_WDH) {
mbed_official 0:a554658735bf 318 transferCompleted(usb_hcca->DoneHead & 0xFFFFFFFE);
mbed_official 0:a554658735bf 319 LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
mbed_official 0:a554658735bf 320 }
mbed_official 0:a554658735bf 321 }
mbed_official 0:a554658735bf 322 }