Working fork to test F0 application

Dependents:   ppCANOpen_Example

Fork of CANnucleo by Zoltan Hudak

Committer:
hudakz
Date:
Tue Dec 22 18:19:16 2015 +0000
Revision:
11:439f3a34c42e
Parent:
10:227a455d0f9f
Child:
12:c45310ff2233
Support for NUCLEO-F303RE added.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
hudakz 0:e29bc8e0dddd 1 /*
hudakz 5:b53e5ee15315 2 ******************************************************************************
hudakz 5:b53e5ee15315 3 * @file can_api.c
hudakz 5:b53e5ee15315 4 * @author Zoltan Hudak
hudakz 5:b53e5ee15315 5 * @version
hudakz 5:b53e5ee15315 6 * @date 04-August-2015
hudakz 5:b53e5ee15315 7 * @brief CAN api for NUCLEO-F103RB platform
hudakz 5:b53e5ee15315 8 ******************************************************************************
hudakz 5:b53e5ee15315 9 * @attention
hudakz 5:b53e5ee15315 10 *
hudakz 5:b53e5ee15315 11 * <h2><center>&copy; COPYRIGHT(c) 2015 Zoltan Hudak <hudakz@inbox.com>
hudakz 5:b53e5ee15315 12 *
hudakz 5:b53e5ee15315 13 * All rights reserved.
hudakz 0:e29bc8e0dddd 14
hudakz 0:e29bc8e0dddd 15 This program is free software: you can redistribute it and/or modify
hudakz 0:e29bc8e0dddd 16 it under the terms of the GNU General Public License as published by
hudakz 0:e29bc8e0dddd 17 the Free Software Foundation, either version 3 of the License, or
hudakz 0:e29bc8e0dddd 18 (at your option) any later version.
hudakz 0:e29bc8e0dddd 19
hudakz 0:e29bc8e0dddd 20 This program is distributed in the hope that it will be useful,
hudakz 0:e29bc8e0dddd 21 but WITHOUT ANY WARRANTY; without even the implied warranty of
hudakz 0:e29bc8e0dddd 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
hudakz 0:e29bc8e0dddd 23 GNU General Public License for more details.
hudakz 0:e29bc8e0dddd 24
hudakz 0:e29bc8e0dddd 25 You should have received a copy of the GNU General Public License
hudakz 0:e29bc8e0dddd 26 along with this program. If not, see <http://www.gnu.org/licenses/>.
hudakz 0:e29bc8e0dddd 27 */
hudakz 11:439f3a34c42e 28 #if defined(TARGET_NUCLEO_F103RB)
hudakz 11:439f3a34c42e 29 #include "stm32f1xx_hal.h"
hudakz 11:439f3a34c42e 30 #elif defined(TARGET_NUCLEO_F303RE)
hudakz 11:439f3a34c42e 31 #include "stm32f3xx_hal.h"
hudakz 11:439f3a34c42e 32 #endif
hudakz 0:e29bc8e0dddd 33 #include "can_api.h"
hudakz 0:e29bc8e0dddd 34 #include "can_helper.h"
hudakz 0:e29bc8e0dddd 35 #include "pinmap.h"
hudakz 0:e29bc8e0dddd 36
hudakz 0:e29bc8e0dddd 37 extern void (*rxCompleteCallback) (void);
hudakz 0:e29bc8e0dddd 38 extern CAN_HandleTypeDef _canHandle;
hudakz 0:e29bc8e0dddd 39
hudakz 0:e29bc8e0dddd 40 /**
hudakz 0:e29bc8e0dddd 41 * @brief
hudakz 0:e29bc8e0dddd 42 * @note
hudakz 0:e29bc8e0dddd 43 * @param
hudakz 0:e29bc8e0dddd 44 * @retval
hudakz 0:e29bc8e0dddd 45 */
hudakz 6:c5a40d5fd9f1 46 void can_init(can_t* obj, PinName rd, PinName td, FunctionalState abom) {
hudakz 6:c5a40d5fd9f1 47 initCAN(obj, rd, td, abom);
hudakz 0:e29bc8e0dddd 48 can_filter(obj, 0, 0, CANAny, 0);
hudakz 0:e29bc8e0dddd 49 }
hudakz 0:e29bc8e0dddd 50
hudakz 0:e29bc8e0dddd 51 /**
hudakz 0:e29bc8e0dddd 52 * @brief
hudakz 0:e29bc8e0dddd 53 * @note
hudakz 0:e29bc8e0dddd 54 * @param
hudakz 0:e29bc8e0dddd 55 * @retval
hudakz 0:e29bc8e0dddd 56 */
hudakz 0:e29bc8e0dddd 57 void can_free(can_t* obj) {
hudakz 0:e29bc8e0dddd 58 HAL_CAN_MspDeInit(obj);
hudakz 0:e29bc8e0dddd 59 }
hudakz 0:e29bc8e0dddd 60
hudakz 0:e29bc8e0dddd 61 /**
hudakz 0:e29bc8e0dddd 62 * @brief
hudakz 0:e29bc8e0dddd 63 * @note
hudakz 0:e29bc8e0dddd 64 * @param
hudakz 0:e29bc8e0dddd 65 * @retval
hudakz 0:e29bc8e0dddd 66 */
hudakz 0:e29bc8e0dddd 67 int can_frequency(can_t* obj, int hz) {
hudakz 11:439f3a34c42e 68 #if defined(TARGET_NUCLEO_F103RB)
hudakz 0:e29bc8e0dddd 69 HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
hudakz 11:439f3a34c42e 70 #elif defined(TARGET_NUCLEO_F303RB)
hudakz 11:439f3a34c42e 71 HAL_NVIC_DisableIRQ(CAN_RX1_IRQn);
hudakz 11:439f3a34c42e 72 #endif
hudakz 0:e29bc8e0dddd 73
hudakz 5:b53e5ee15315 74 // APB1 peripheral clock = 36000000Hz
hudakz 0:e29bc8e0dddd 75
hudakz 0:e29bc8e0dddd 76 switch(hz) {
hudakz 0:e29bc8e0dddd 77 case 1000000:
hudakz 0:e29bc8e0dddd 78 // 1000kbps bit rate
hudakz 0:e29bc8e0dddd 79 _canHandle.Init.Prescaler = 3; // number of time quanta = 36000000/3/1000000 = 12
hudakz 0:e29bc8e0dddd 80 _canHandle.Init.SJW = CAN_SJW_1TQ;
hudakz 0:e29bc8e0dddd 81 _canHandle.Init.BS1 = CAN_BS1_8TQ; // sample point at: (1 + 8) / 12 * 100 = 75%
hudakz 0:e29bc8e0dddd 82 _canHandle.Init.BS2 = CAN_BS2_3TQ;
hudakz 0:e29bc8e0dddd 83 break;
hudakz 0:e29bc8e0dddd 84
hudakz 0:e29bc8e0dddd 85 case 500000:
hudakz 0:e29bc8e0dddd 86 // 500kbps bit rate
hudakz 0:e29bc8e0dddd 87 _canHandle.Init.Prescaler = 6; // number of time quanta = 36000000/6/500000 = 12
hudakz 0:e29bc8e0dddd 88 _canHandle.Init.SJW = CAN_SJW_1TQ;
hudakz 0:e29bc8e0dddd 89 _canHandle.Init.BS1 = CAN_BS1_8TQ; // sample point at: (1 + 8) / 12 * 100 = 75%
hudakz 0:e29bc8e0dddd 90 _canHandle.Init.BS2 = CAN_BS2_3TQ;
hudakz 0:e29bc8e0dddd 91 break;
hudakz 0:e29bc8e0dddd 92
hudakz 0:e29bc8e0dddd 93 case 250000:
hudakz 0:e29bc8e0dddd 94 // 250kbps
hudakz 0:e29bc8e0dddd 95 _canHandle.Init.Prescaler = 9; // number of time quanta = 36000000/9/250000 = 16
hudakz 0:e29bc8e0dddd 96 _canHandle.Init.SJW = CAN_SJW_1TQ;
hudakz 0:e29bc8e0dddd 97 _canHandle.Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
hudakz 0:e29bc8e0dddd 98 _canHandle.Init.BS2 = CAN_BS2_4TQ;
hudakz 0:e29bc8e0dddd 99 break;
hudakz 0:e29bc8e0dddd 100
hudakz 0:e29bc8e0dddd 101 case 125000:
hudakz 0:e29bc8e0dddd 102 // 125kbps
hudakz 0:e29bc8e0dddd 103 _canHandle.Init.Prescaler = 18; // number of time quanta = 36000000/18/125000 = 16
hudakz 0:e29bc8e0dddd 104 _canHandle.Init.SJW = CAN_SJW_1TQ;
hudakz 0:e29bc8e0dddd 105 _canHandle.Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
hudakz 0:e29bc8e0dddd 106 _canHandle.Init.BS2 = CAN_BS2_4TQ;
hudakz 0:e29bc8e0dddd 107 break;
hudakz 0:e29bc8e0dddd 108
hudakz 0:e29bc8e0dddd 109 default:
hudakz 0:e29bc8e0dddd 110 // 125kbps (default)
hudakz 5:b53e5ee15315 111 #if DEBUG
hudakz 0:e29bc8e0dddd 112 printf("Unknown frequency specified!\r\n");
hudakz 0:e29bc8e0dddd 113 printf("Using default 125kbps\r\n");
hudakz 5:b53e5ee15315 114 #endif
hudakz 0:e29bc8e0dddd 115 _canHandle.Init.Prescaler = 18; // number of time quanta = 36000000/18/125000 = 16
hudakz 0:e29bc8e0dddd 116 _canHandle.Init.SJW = CAN_SJW_1TQ;
hudakz 0:e29bc8e0dddd 117 _canHandle.Init.BS1 = CAN_BS1_11TQ; // sample point at: (1 + 11) / 16 * 100 = 75%
hudakz 0:e29bc8e0dddd 118 _canHandle.Init.BS2 = CAN_BS2_4TQ;
hudakz 0:e29bc8e0dddd 119 }
hudakz 0:e29bc8e0dddd 120
hudakz 0:e29bc8e0dddd 121 HAL_CAN_Init(&_canHandle);
hudakz 11:439f3a34c42e 122 #if defined(TARGET_NUCLEO_F103RB)
hudakz 11:439f3a34c42e 123 HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
hudakz 11:439f3a34c42e 124 #elif defined(TARGET_NUCLEO_F303RB)
hudakz 11:439f3a34c42e 125 HAL_NVIC_EnableIRQ(CAN_RX1_IRQn);
hudakz 11:439f3a34c42e 126 #endif
hudakz 11:439f3a34c42e 127
hudakz 5:b53e5ee15315 128 return 1;
hudakz 0:e29bc8e0dddd 129 }
hudakz 0:e29bc8e0dddd 130
hudakz 0:e29bc8e0dddd 131 /**
hudakz 0:e29bc8e0dddd 132 * @brief
hudakz 0:e29bc8e0dddd 133 * @note
hudakz 0:e29bc8e0dddd 134 * @param
hudakz 0:e29bc8e0dddd 135 * @retval
hudakz 0:e29bc8e0dddd 136 */
hudakz 0:e29bc8e0dddd 137 void can_irq_init(can_t* obj, can_irq_handler handler, uint32_t id) {
hudakz 0:e29bc8e0dddd 138 if(HAL_CAN_Receive_IT(&_canHandle, CAN_FIFO0) != HAL_OK) {
hudakz 5:b53e5ee15315 139 #ifdef DEBUG
hudakz 0:e29bc8e0dddd 140 printf("CAN reception initialization error\r\n");
hudakz 5:b53e5ee15315 141 #endif
hudakz 0:e29bc8e0dddd 142 }
hudakz 0:e29bc8e0dddd 143 }
hudakz 0:e29bc8e0dddd 144
hudakz 0:e29bc8e0dddd 145 /**
hudakz 0:e29bc8e0dddd 146 * @brief
hudakz 0:e29bc8e0dddd 147 * @note
hudakz 0:e29bc8e0dddd 148 * @param
hudakz 0:e29bc8e0dddd 149 * @retval
hudakz 0:e29bc8e0dddd 150 */
hudakz 0:e29bc8e0dddd 151 void can_irq_free(can_t* obj) {
hudakz 11:439f3a34c42e 152 rxCompleteCallback = 0;
hudakz 0:e29bc8e0dddd 153 }
hudakz 0:e29bc8e0dddd 154
hudakz 0:e29bc8e0dddd 155 /**
hudakz 0:e29bc8e0dddd 156 * @brief
hudakz 0:e29bc8e0dddd 157 * @note
hudakz 0:e29bc8e0dddd 158 * @param
hudakz 0:e29bc8e0dddd 159 * @retval
hudakz 0:e29bc8e0dddd 160 */
hudakz 0:e29bc8e0dddd 161 void can_irq_set(void (*fptr) (void)) {
hudakz 0:e29bc8e0dddd 162 rxCompleteCallback = fptr;
hudakz 0:e29bc8e0dddd 163 }
hudakz 0:e29bc8e0dddd 164
hudakz 0:e29bc8e0dddd 165 /**
hudakz 0:e29bc8e0dddd 166 * @brief
hudakz 0:e29bc8e0dddd 167 * @note
hudakz 0:e29bc8e0dddd 168 * @param
hudakz 0:e29bc8e0dddd 169 * @retval
hudakz 0:e29bc8e0dddd 170 */
hudakz 0:e29bc8e0dddd 171 int can_write(can_t* obj, CAN_Message msg, int cc) {
hudakz 0:e29bc8e0dddd 172 int i = 0;
hudakz 0:e29bc8e0dddd 173
hudakz 0:e29bc8e0dddd 174 if(msg.format == CANStandard) {
hudakz 0:e29bc8e0dddd 175 _canHandle.pTxMsg->StdId = msg.id;
hudakz 0:e29bc8e0dddd 176 _canHandle.pTxMsg->ExtId = 0x00;
hudakz 0:e29bc8e0dddd 177 }
hudakz 0:e29bc8e0dddd 178 else {
hudakz 0:e29bc8e0dddd 179 _canHandle.pTxMsg->StdId = 0x00;
hudakz 0:e29bc8e0dddd 180 _canHandle.pTxMsg->ExtId = msg.id;
hudakz 0:e29bc8e0dddd 181 }
hudakz 0:e29bc8e0dddd 182
hudakz 0:e29bc8e0dddd 183 _canHandle.pTxMsg->RTR = msg.type == CANData ? CAN_RTR_DATA : CAN_RTR_REMOTE;
hudakz 0:e29bc8e0dddd 184 _canHandle.pTxMsg->IDE = msg.format == CANStandard ? CAN_ID_STD : CAN_ID_EXT;
hudakz 0:e29bc8e0dddd 185 _canHandle.pTxMsg->DLC = msg.len;
hudakz 0:e29bc8e0dddd 186
hudakz 0:e29bc8e0dddd 187 for(i = 0; i < msg.len; i++)
hudakz 0:e29bc8e0dddd 188 _canHandle.pTxMsg->Data[i] = msg.data[i];
hudakz 0:e29bc8e0dddd 189
hudakz 0:e29bc8e0dddd 190 if(HAL_CAN_Transmit(&_canHandle, 10) != HAL_OK) {
hudakz 5:b53e5ee15315 191 #ifdef DEBUG
hudakz 0:e29bc8e0dddd 192 printf("Transmission error\r\n");
hudakz 5:b53e5ee15315 193 #endif
hudakz 5:b53e5ee15315 194 return 0;
hudakz 0:e29bc8e0dddd 195 }
hudakz 5:b53e5ee15315 196 else
hudakz 5:b53e5ee15315 197 return 1;
hudakz 0:e29bc8e0dddd 198 }
hudakz 0:e29bc8e0dddd 199
hudakz 0:e29bc8e0dddd 200 /**
hudakz 0:e29bc8e0dddd 201 * @brief
hudakz 0:e29bc8e0dddd 202 * @note
hudakz 0:e29bc8e0dddd 203 * @param
hudakz 0:e29bc8e0dddd 204 * @retval
hudakz 0:e29bc8e0dddd 205 */
hudakz 0:e29bc8e0dddd 206 int can_read(can_t* obj, CAN_Message* msg, int handle) {
hudakz 10:227a455d0f9f 207 int i;
hudakz 0:e29bc8e0dddd 208 msg->id = _canHandle.pRxMsg->IDE == CAN_ID_STD ? _canHandle.pRxMsg->StdId : _canHandle.pRxMsg->ExtId;
hudakz 0:e29bc8e0dddd 209 msg->type = _canHandle.pRxMsg->RTR == CAN_RTR_DATA ? CANData : CANRemote;
hudakz 0:e29bc8e0dddd 210 msg->format = _canHandle.pRxMsg->IDE == CAN_ID_STD ? CANStandard : CANExtended;
hudakz 0:e29bc8e0dddd 211 msg->len = _canHandle.pRxMsg->DLC;
hudakz 10:227a455d0f9f 212 for(i = 0; i < msg->len; i++)
hudakz 0:e29bc8e0dddd 213 msg->data[i] = _canHandle.pRxMsg->Data[i];
hudakz 5:b53e5ee15315 214
hudakz 5:b53e5ee15315 215 return msg->len;
hudakz 0:e29bc8e0dddd 216 }
hudakz 0:e29bc8e0dddd 217
hudakz 0:e29bc8e0dddd 218 /**
hudakz 0:e29bc8e0dddd 219 * @brief
hudakz 0:e29bc8e0dddd 220 * @note
hudakz 0:e29bc8e0dddd 221 * @param
hudakz 0:e29bc8e0dddd 222 * @retval
hudakz 0:e29bc8e0dddd 223 */
hudakz 0:e29bc8e0dddd 224 int can_mode(can_t* obj, CanMode mode) {
hudakz 0:e29bc8e0dddd 225 switch(mode) {
hudakz 0:e29bc8e0dddd 226 case MODE_RESET:
hudakz 0:e29bc8e0dddd 227 return HAL_ERROR;
hudakz 0:e29bc8e0dddd 228
hudakz 0:e29bc8e0dddd 229 case MODE_NORMAL:
hudakz 0:e29bc8e0dddd 230 _canHandle.Init.Mode = CAN_MODE_NORMAL;
hudakz 0:e29bc8e0dddd 231 break;
hudakz 0:e29bc8e0dddd 232
hudakz 0:e29bc8e0dddd 233 case MODE_SILENT:
hudakz 0:e29bc8e0dddd 234 _canHandle.Init.Mode = CAN_MODE_SILENT;
hudakz 0:e29bc8e0dddd 235 break;
hudakz 0:e29bc8e0dddd 236
hudakz 0:e29bc8e0dddd 237 case MODE_TEST_GLOBAL:
hudakz 0:e29bc8e0dddd 238 _canHandle.Init.Mode = CAN_MODE_LOOPBACK;
hudakz 0:e29bc8e0dddd 239 break;
hudakz 0:e29bc8e0dddd 240
hudakz 0:e29bc8e0dddd 241 case MODE_TEST_LOCAL:
hudakz 0:e29bc8e0dddd 242 _canHandle.Init.Mode = CAN_MODE_LOOPBACK;
hudakz 0:e29bc8e0dddd 243 break;
hudakz 0:e29bc8e0dddd 244
hudakz 0:e29bc8e0dddd 245 case MODE_TEST_SILENT:
hudakz 0:e29bc8e0dddd 246 _canHandle.Init.Mode = CAN_MODE_SILENT_LOOPBACK;
hudakz 0:e29bc8e0dddd 247 break;
hudakz 0:e29bc8e0dddd 248 }
hudakz 0:e29bc8e0dddd 249
hudakz 3:0fae6b54a2ee 250 return HAL_CAN_Init(&_canHandle);
hudakz 0:e29bc8e0dddd 251 }
hudakz 0:e29bc8e0dddd 252
hudakz 0:e29bc8e0dddd 253 /**
hudakz 0:e29bc8e0dddd 254 * @brief
hudakz 0:e29bc8e0dddd 255 * @note
hudakz 0:e29bc8e0dddd 256 * @param
hudakz 0:e29bc8e0dddd 257 * @retval
hudakz 0:e29bc8e0dddd 258 */
hudakz 0:e29bc8e0dddd 259 int can_filter(can_t* obj, uint32_t id, uint32_t mask, CANFormat format /*=CANAny*/, int32_t handle /*=0*/ ) {
hudakz 0:e29bc8e0dddd 260 CAN_FilterConfTypeDef sFilterConfig;
hudakz 0:e29bc8e0dddd 261
hudakz 8:5c90d6b9a382 262 sFilterConfig.FilterNumber = handle; // Specifies the filter number (must be a number between 0 and 13 at 32-bit filter scale)
hudakz 0:e29bc8e0dddd 263 sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
hudakz 0:e29bc8e0dddd 264 sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
hudakz 0:e29bc8e0dddd 265 sFilterConfig.FilterIdHigh = (((id) >> 16) & 0xFFFF);
hudakz 0:e29bc8e0dddd 266 sFilterConfig.FilterIdLow = ((id) & 0xFFFF);
hudakz 0:e29bc8e0dddd 267 sFilterConfig.FilterMaskIdHigh = (((mask) >> 16) & 0xFFFF);
hudakz 0:e29bc8e0dddd 268 sFilterConfig.FilterMaskIdLow = ((mask) & 0xFFFF);
hudakz 0:e29bc8e0dddd 269 sFilterConfig.FilterFIFOAssignment = 0;
hudakz 0:e29bc8e0dddd 270 sFilterConfig.FilterActivation = ENABLE;
hudakz 8:5c90d6b9a382 271 sFilterConfig.BankNumber = 0; // Selects the start bank filter
hudakz 8:5c90d6b9a382 272 return HAL_CAN_ConfigFilter(&_canHandle, &sFilterConfig);
hudakz 0:e29bc8e0dddd 273 }
hudakz 0:e29bc8e0dddd 274
hudakz 0:e29bc8e0dddd 275 /**
hudakz 0:e29bc8e0dddd 276 * @brief
hudakz 0:e29bc8e0dddd 277 * @note
hudakz 0:e29bc8e0dddd 278 * @param
hudakz 0:e29bc8e0dddd 279 * @retval
hudakz 0:e29bc8e0dddd 280 */
hudakz 0:e29bc8e0dddd 281 void can_reset(can_t* obj) {
hudakz 0:e29bc8e0dddd 282 __HAL_CAN_RESET_HANDLE_STATE(&_canHandle);
hudakz 0:e29bc8e0dddd 283 }
hudakz 0:e29bc8e0dddd 284
hudakz 0:e29bc8e0dddd 285 /**
hudakz 0:e29bc8e0dddd 286 * @brief
hudakz 0:e29bc8e0dddd 287 * @note
hudakz 0:e29bc8e0dddd 288 * @param
hudakz 0:e29bc8e0dddd 289 * @retval
hudakz 0:e29bc8e0dddd 290 */
hudakz 0:e29bc8e0dddd 291 unsigned char can_rderror(can_t* obj) {
hudakz 0:e29bc8e0dddd 292 return HAL_CAN_GetError(&_canHandle);
hudakz 0:e29bc8e0dddd 293 }
hudakz 0:e29bc8e0dddd 294
hudakz 0:e29bc8e0dddd 295 /**
hudakz 0:e29bc8e0dddd 296 * @brief
hudakz 0:e29bc8e0dddd 297 * @note
hudakz 0:e29bc8e0dddd 298 * @param
hudakz 0:e29bc8e0dddd 299 * @retval
hudakz 0:e29bc8e0dddd 300 */
hudakz 0:e29bc8e0dddd 301 unsigned char can_tderror(can_t* obj) {
hudakz 0:e29bc8e0dddd 302 return HAL_CAN_GetError(&_canHandle);
hudakz 0:e29bc8e0dddd 303 }
hudakz 0:e29bc8e0dddd 304
hudakz 0:e29bc8e0dddd 305 /**
hudakz 0:e29bc8e0dddd 306 * @brief
hudakz 0:e29bc8e0dddd 307 * @note
hudakz 0:e29bc8e0dddd 308 * @param
hudakz 0:e29bc8e0dddd 309 * @retval
hudakz 0:e29bc8e0dddd 310 */
hudakz 0:e29bc8e0dddd 311 void can_monitor(can_t* obj, int silent) {
hudakz 0:e29bc8e0dddd 312
hudakz 0:e29bc8e0dddd 313 // not implemented
hudakz 0:e29bc8e0dddd 314 }
hudakz 1:eb04f7f0478d 315
hudakz 5:b53e5ee15315 316
hudakz 6:c5a40d5fd9f1 317
hudakz 11:439f3a34c42e 318