diff options
| author | danicampora | 2015-02-06 15:35:48 +0100 |
|---|---|---|
| committer | Damien George | 2015-02-06 22:10:11 +0000 |
| commit | 8785645a952c03315dbf93667b5f7c7eec49762f (patch) | |
| tree | 267e2d572d87e92bfc0bfabf83859231152a2162 /cc3200/mods | |
| parent | 97f14606f528180d1482cffbe3571163a1dd9273 (diff) | |
cc3200: Add cc3200 port of MicroPython.
The port currently implements support for GPIO, RTC, ExtInt and the WiFi
subsystem. A small file system is available in the serial flash. A
bootloader which makes OTA updates possible, is also part of this initial
implementation.
Diffstat (limited to 'cc3200/mods')
| -rw-r--r-- | cc3200/mods/modnetwork.c | 152 | ||||
| -rw-r--r-- | cc3200/mods/modnetwork.h | 81 | ||||
| -rw-r--r-- | cc3200/mods/modpyb.c | 318 | ||||
| -rw-r--r-- | cc3200/mods/moduos.c | 337 | ||||
| -rw-r--r-- | cc3200/mods/modusocket.c | 451 | ||||
| -rw-r--r-- | cc3200/mods/modutime.c | 333 | ||||
| -rw-r--r-- | cc3200/mods/modutime.h | 43 | ||||
| -rw-r--r-- | cc3200/mods/modwlan.c | 1199 | ||||
| -rw-r--r-- | cc3200/mods/modwlan.h | 71 | ||||
| -rw-r--r-- | cc3200/mods/pybextint.c | 357 | ||||
| -rw-r--r-- | cc3200/mods/pybextint.h | 47 | ||||
| -rw-r--r-- | cc3200/mods/pybgpio.c | 529 | ||||
| -rw-r--r-- | cc3200/mods/pybgpio.h | 68 | ||||
| -rw-r--r-- | cc3200/mods/pybrtc.c | 158 | ||||
| -rw-r--r-- | cc3200/mods/pybrtc.h | 33 | ||||
| -rw-r--r-- | cc3200/mods/pybstdio.c | 174 | ||||
| -rw-r--r-- | cc3200/mods/pybstdio.h | 31 | ||||
| -rw-r--r-- | cc3200/mods/pybsystick.c | 93 | ||||
| -rw-r--r-- | cc3200/mods/pybsystick.h | 30 | ||||
| -rw-r--r-- | cc3200/mods/pybuart.c | 682 | ||||
| -rw-r--r-- | cc3200/mods/pybuart.h | 45 |
21 files changed, 5232 insertions, 0 deletions
diff --git a/cc3200/mods/modnetwork.c b/cc3200/mods/modnetwork.c new file mode 100644 index 000000000..2e100aecb --- /dev/null +++ b/cc3200/mods/modnetwork.c @@ -0,0 +1,152 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <std.h> +#include <stdint.h> +#include <string.h> + +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "objlist.h" +#include "runtime.h" +#include "modnetwork.h" +#include "mpexception.h" +#include "mpstate.h" + +/// \module network - network configuration +/// +/// This module provides network drivers and routing configuration. + +void mod_network_init(void) { + mp_obj_list_init(&MP_STATE_PORT(mod_network_nic_list), 0); +} + +void mod_network_register_nic(mp_obj_t nic) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + if (MP_STATE_PORT(mod_network_nic_list).items[i] == nic) { + // nic already registered + return; + } + } + // nic not registered so add to list + mp_obj_list_append(&MP_STATE_PORT(mod_network_nic_list), nic); +} + +mp_obj_t mod_network_find_nic(const uint8_t *ip) { + // find a NIC that is suited to given IP address + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; + return nic; + } + + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_resource_not_avaliable)); +} + +STATIC mp_obj_t network_route(void) { + return &MP_STATE_PORT(mod_network_nic_list); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(network_route_obj, network_route); + +STATIC const mp_map_elem_t mp_module_network_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_network) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WLAN), (mp_obj_t)&mod_network_nic_type_wlan }, + { MP_OBJ_NEW_QSTR(MP_QSTR_route), (mp_obj_t)&network_route_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_network_globals, mp_module_network_globals_table); + +const mp_obj_module_t mp_module_network = { + .base = { &mp_type_module }, + .name = MP_QSTR_network, + .globals = (mp_obj_dict_t*)&mp_module_network_globals, +}; + +/******************************************************************************/ +// Miscellaneous helpers + +void mod_network_convert_ipv4_endianness(uint8_t *ip) { + uint8_t ip0 = ip[0]; ip[0] = ip[3]; ip[3] = ip0; + uint8_t ip1 = ip[1]; ip[1] = ip[2]; ip[2] = ip1; +} + +// Takes an address of the form '192.168.0.1' and converts it to network format +// in out_ip (big endian, so the 192 is the first byte). +void mod_network_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip) { + mp_uint_t addr_len; + const char *addr_str = mp_obj_str_get_data(addr_in, &addr_len); + if (addr_len == 0) { + // special case of no address given + memset(out_ip, 0, MOD_NETWORK_IPV4ADDR_BUF_SIZE); + return; + } + const char *s = addr_str; + const char *s_top = addr_str + addr_len; + for (mp_uint_t i = 0;; i++) { + mp_uint_t val = 0; + for (; s < s_top && *s != '.'; s++) { + val = val * 10 + *s - '0'; + } + out_ip[i] = val; + if (i == 3 && s == s_top) { + return; + } else if (i < 3 && s < s_top && *s == '.') { + s++; + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + } + } +} + +// Takes an address of the form ('192.168.0.1', 8080), returns the port and +// puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). +mp_uint_t mod_network_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip) { + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + mod_network_parse_ipv4_addr(addr_items[0], out_ip); + return mp_obj_get_int(addr_items[1]); +} + +// Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. +mp_obj_t mod_network_format_ipv4_addr(uint8_t *ip) { + char ip_str[16]; + mp_uint_t ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + return mp_obj_new_str(ip_str, ip_len, false); +} + +// Takes an array with a raw IP address, and a port, and returns a net-address +// tuple such as ('192.168.0.1', 8080). +mp_obj_t mod_network_format_inet_addr(uint8_t *ip, mp_uint_t port) { + mp_obj_t tuple[2] = { + tuple[0] = mod_network_format_ipv4_addr(ip), + tuple[1] = mp_obj_new_int(port), + }; + return mp_obj_new_tuple(2, tuple); +} diff --git a/cc3200/mods/modnetwork.h b/cc3200/mods/modnetwork.h new file mode 100644 index 000000000..838073395 --- /dev/null +++ b/cc3200/mods/modnetwork.h @@ -0,0 +1,81 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define MOD_NETWORK_IPV4ADDR_BUF_SIZE (4) + +// Forward declaration +struct _mod_network_socket_obj_t; + +typedef struct _mod_network_nic_type_t { + mp_obj_type_t base; + + // API for non-socket operations + int (*gethostbyname)(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *ip_out, uint8_t family); + + // API for socket operations; return -1 on error + int (*socket)(struct _mod_network_socket_obj_t *s, int *_errno); + void (*close)(struct _mod_network_socket_obj_t *socket); + int (*bind)(struct _mod_network_socket_obj_t *s, byte *ip, mp_uint_t port, int *_errno); + int (*listen)(struct _mod_network_socket_obj_t *s, mp_int_t backlog, int *_errno); + int (*accept)(struct _mod_network_socket_obj_t *s, struct _mod_network_socket_obj_t *s2, byte *ip, mp_uint_t *port, int *_errno); + int (*connect)(struct _mod_network_socket_obj_t *s, byte *ip, mp_uint_t port, int *_errno); + int (*send)(struct _mod_network_socket_obj_t *s, const byte *buf, mp_uint_t len, int *_errno); + int (*recv)(struct _mod_network_socket_obj_t *s, byte *buf, mp_uint_t len, int *_errno); + int (*sendto)(struct _mod_network_socket_obj_t *s, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno); + int (*recvfrom)(struct _mod_network_socket_obj_t *s, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno); + int (*setsockopt)(struct _mod_network_socket_obj_t *s, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno); + int (*settimeout)(struct _mod_network_socket_obj_t *s, mp_uint_t timeout_ms, int *_errno); + int (*ioctl)(struct _mod_network_socket_obj_t *s, mp_uint_t request, mp_uint_t arg, int *_errno); +} mod_network_nic_type_t; + +typedef struct _mod_network_socket_obj_t { + mp_obj_base_t base; + mp_obj_t nic; + mod_network_nic_type_t *nic_type; + union { + struct { + uint8_t domain; + uint8_t type; + uint8_t proto; + int8_t fileno; + } u_param; + int16_t sd; + bool closed; + }; +} mod_network_socket_obj_t; + +extern const mod_network_nic_type_t mod_network_nic_type_wlan; + +void mod_network_init(void); +void mod_network_register_nic(mp_obj_t nic); +mp_obj_t mod_network_find_nic(const uint8_t *ip); + +void mod_network_convert_ipv4_endianness(uint8_t *ip); +void mod_network_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip); +mp_uint_t mod_network_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip); +mp_obj_t mod_network_format_ipv4_addr(uint8_t *ip); +mp_obj_t mod_network_format_inet_addr(uint8_t *ip, mp_uint_t port); diff --git a/cc3200/mods/modpyb.c b/cc3200/mods/modpyb.c new file mode 100644 index 000000000..f17d6a257 --- /dev/null +++ b/cc3200/mods/modpyb.c @@ -0,0 +1,318 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +#include <std.h> +#include <stdint.h> + +#include "py/mpstate.h" +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "misc.h" +#include "nlr.h" +#include "qstr.h" +#include "obj.h" +#include "gc.h" +#include "gccollect.h" +#include "irq.h" +#include "inc/hw_types.h" +#include "inc/hw_gpio.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_uart.h" +#include "prcm.h" +#include "pyexec.h" +#include "pybuart.h" +#include "pybgpio.h" +#include "pybstdio.h" +#include "pybrtc.h" +#include "pybsystick.h" +#include "simplelink.h" +#include "modwlan.h" +#include "telnet.h" +#include "ff.h" +#include "diskio.h" +#include "sflash_diskio.h" +#include "FreeRTOS.h" +#include "portable.h" +#include "task.h" +#include "mpexception.h" +#include "random.h" +#include "pybextint.h" + + +#ifdef DEBUG +extern OsiTaskHandle mpTaskHandle; +extern OsiTaskHandle svTaskHandle; +extern TaskHandle_t xSimpleLinkSpawnTaskHndl; +#endif + +/// \module pyb - functions related to the pyboard +/// +/// The `pyb` module contains specific functions related to the pyboard. + +/// \function hard_reset() +/// Resets the pyboard in a manner similar to pushing the external RESET +/// button. +STATIC mp_obj_t pyb_hard_reset(void) { + // disable wlan services + wlan_servers_stop(); + wlan_sl_disable(); + // perform a SoC reset + PRCMSOCReset(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_hard_reset_obj, pyb_hard_reset); + +#ifdef DEBUG +/// \function info([dump_alloc_table]) +/// Print out some run time info which is helpful duirng development. +STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) { + // FreeRTOS info + { + printf("---------------------------------------------\n"); + printf("FreeRTOS\n"); + printf("---------------------------------------------\n"); + printf("Total heap: %u\n", configTOTAL_HEAP_SIZE); + printf("Free heap: %u\n", xPortGetFreeHeapSize()); + printf("MpTask min free stack: %u\n", (unsigned int)uxTaskGetStackHighWaterMark((TaskHandle_t)mpTaskHandle)); + printf("ServersTask min free stack: %u\n", (unsigned int)uxTaskGetStackHighWaterMark((TaskHandle_t)svTaskHandle)); + printf("SlTask min free stack: %u\n", (unsigned int)uxTaskGetStackHighWaterMark(xSimpleLinkSpawnTaskHndl)); + printf("IdleTask min free stack: %u\n", (unsigned int)uxTaskGetStackHighWaterMark(xTaskGetIdleTaskHandle())); + + uint32_t *pstack = (uint32_t *)&_stack; + while (*pstack == 0x55555555) { + pstack++; + } + printf("MAIN min free stack: %u\n", pstack - ((uint32_t *)&_stack)); + printf("---------------------------------------------\n"); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_info_obj, 0, 1, pyb_info); +#endif + +/// \function unique_id() +/// Returns a string of 6 bytes (48 bits), which is the unique MAC address of the SoC +STATIC mp_obj_t pyb_mac(void) { + uint8_t mac[6]; + wlan_get_mac (mac); + return mp_obj_new_bytes(mac, 6); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_mac_obj, pyb_mac); + +/// \function freq() +/// Returns the CPU frequency: (F_CPU). +STATIC mp_obj_t pyb_freq(void) { + return mp_obj_new_int(HAL_FCPU_HZ); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_freq_obj, pyb_freq); + +/// \function sync() +/// Sync all file systems. +STATIC mp_obj_t pyb_sync(void) { + sflash_disk_flush(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_sync_obj, pyb_sync); + +/// \function millis() +/// Returns the number of milliseconds since the board was last reset. +/// +/// The result is always a micropython smallint (31-bit signed number), so +/// after 2^30 milliseconds (about 12.4 days) this will start to return +/// negative numbers. +STATIC mp_obj_t pyb_millis(void) { + // We want to "cast" the 32 bit unsigned into a small-int. This means + // copying the MSB down 1 bit (extending the sign down), which is + // equivalent to just using the MP_OBJ_NEW_SMALL_INT macro. + return MP_OBJ_NEW_SMALL_INT(HAL_GetTick()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_millis_obj, pyb_millis); + +/// \function elapsed_millis(start) +/// Returns the number of milliseconds which have elapsed since `start`. +/// +/// This function takes care of counter wrap, and always returns a positive +/// number. This means it can be used to measure periods upto about 12.4 days. +/// +/// Example: +/// start = pyb.millis() +/// while pyb.elapsed_millis(start) < 1000: +/// # Perform some operation +STATIC mp_obj_t pyb_elapsed_millis(mp_obj_t start) { + uint32_t startMillis = mp_obj_get_int(start); + uint32_t currMillis = HAL_GetTick(); + return MP_OBJ_NEW_SMALL_INT((currMillis - startMillis) & 0x3fffffff); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_millis_obj, pyb_elapsed_millis); + +/// \function micros() +/// Returns the number of microseconds since the board was last reset. +/// +/// The result is always a micropython smallint (31-bit signed number), so +/// after 2^30 microseconds (about 17.8 minutes) this will start to return +/// negative numbers. +STATIC mp_obj_t pyb_micros(void) { + // We want to "cast" the 32 bit unsigned into a small-int. This means + // copying the MSB down 1 bit (extending the sign down), which is + // equivalent to just using the MP_OBJ_NEW_SMALL_INT macro. + return MP_OBJ_NEW_SMALL_INT(sys_tick_get_microseconds()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_micros_obj, pyb_micros); + +/// \function elapsed_micros(start) +/// Returns the number of microseconds which have elapsed since `start`. +/// +/// This function takes care of counter wrap, and always returns a positive +/// number. This means it can be used to measure periods upto about 17.8 minutes. +/// +/// Example: +/// start = pyb.micros() +/// while pyb.elapsed_micros(start) < 1000: +/// # Perform some operation +STATIC mp_obj_t pyb_elapsed_micros(mp_obj_t start) { + uint32_t startMicros = mp_obj_get_int(start); + uint32_t currMicros = sys_tick_get_microseconds(); + return MP_OBJ_NEW_SMALL_INT((currMicros - startMicros) & 0x3fffffff); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_micros_obj, pyb_elapsed_micros); + +/// \function delay(ms) +/// Delay for the given number of milliseconds. +STATIC mp_obj_t pyb_delay(mp_obj_t ms_in) { + mp_int_t ms = mp_obj_get_int(ms_in); + if (ms > 0) { + HAL_Delay(ms); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_delay_obj, pyb_delay); + +/// \function udelay(us) +/// Delay for the given number of microseconds. +STATIC mp_obj_t pyb_udelay(mp_obj_t usec_in) { + mp_int_t usec = mp_obj_get_int(usec_in); + if (usec > 0) { + uint32_t count = 0; + const uint32_t utime = ((HAL_FCPU_HZ / 1000000) * (usec / 4)); + while (++count <= utime) { + } + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_udelay_obj, pyb_udelay); + +STATIC mp_obj_t pyb_stop(void) { + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_0(pyb_stop_obj, pyb_stop); + +STATIC mp_obj_t pyb_standby(void) { + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_0(pyb_standby_obj, pyb_standby); + +/// \function repl_uart(uart) +/// Get or set the UART object that the REPL is repeated on. +STATIC mp_obj_t pyb_repl_uart(uint n_args, const mp_obj_t *args) { + if (n_args == 0) { + if (MP_STATE_PORT(pyb_stdio_uart) == NULL) { + return mp_const_none; + } else { + return MP_STATE_PORT(pyb_stdio_uart); + } + } else { + if (args[0] == mp_const_none) { + MP_STATE_PORT(pyb_stdio_uart) = NULL; + } else if (mp_obj_get_type(args[0]) == &pyb_uart_type) { + MP_STATE_PORT(pyb_stdio_uart) = args[0]; + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_num_type_invalid_arguments)); + } + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_repl_uart_obj, 0, 1, pyb_repl_uart); + +MP_DECLARE_CONST_FUN_OBJ(pyb_main_obj); // defined in main.c + +STATIC const mp_map_elem_t pyb_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_pyb) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_hard_reset), (mp_obj_t)&pyb_hard_reset_obj }, +#ifdef DEBUG + { MP_OBJ_NEW_QSTR(MP_QSTR_info), (mp_obj_t)&pyb_info_obj }, +#endif + { MP_OBJ_NEW_QSTR(MP_QSTR_mac), (mp_obj_t)&pyb_mac_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_freq_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_repl_info), (mp_obj_t)&pyb_set_repl_info_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_wfi), (mp_obj_t)&pyb_wfi_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disable_irq), (mp_obj_t)&pyb_disable_irq_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_enable_irq), (mp_obj_t)&pyb_enable_irq_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_stop), (mp_obj_t)&pyb_stop_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_standby), (mp_obj_t)&pyb_standby_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_main), (mp_obj_t)&pyb_main_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_repl_uart), (mp_obj_t)&pyb_repl_uart_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_millis), (mp_obj_t)&pyb_millis_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_elapsed_millis), (mp_obj_t)&pyb_elapsed_millis_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_micros), (mp_obj_t)&pyb_micros_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_elapsed_micros), (mp_obj_t)&pyb_elapsed_micros_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_delay), (mp_obj_t)&pyb_delay_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_udelay), (mp_obj_t)&pyb_udelay_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&pyb_sync_obj }, + + //{ MP_OBJ_NEW_QSTR(MP_QSTR_Timer), (mp_obj_t)&pyb_timer_type }, + +#if MICROPY_HW_ENABLE_RNG + { MP_OBJ_NEW_QSTR(MP_QSTR_rng), (mp_obj_t)&pyb_rng_get_obj }, +#endif + +#if MICROPY_HW_ENABLE_RTC + { MP_OBJ_NEW_QSTR(MP_QSTR_RTC), (mp_obj_t)&pyb_rtc_type }, +#endif + + { MP_OBJ_NEW_QSTR(MP_QSTR_GPIO), (mp_obj_t)&gpio_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ExtInt), (mp_obj_t)&extint_type }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_UART), (mp_obj_t)&pyb_uart_type }, +}; + +STATIC MP_DEFINE_CONST_DICT(pyb_module_globals, pyb_module_globals_table); + +const mp_obj_module_t pyb_module = { + .base = { &mp_type_module }, + .name = MP_QSTR_pyb, + .globals = (mp_obj_dict_t*)&pyb_module_globals, +}; diff --git a/cc3200/mods/moduos.c b/cc3200/mods/moduos.c new file mode 100644 index 000000000..9058a4817 --- /dev/null +++ b/cc3200/mods/moduos.c @@ -0,0 +1,337 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdint.h> +#include <string.h> +#include "std.h" + +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "objtuple.h" +#include "ff.h" +#include "diskio.h" +#include "sflash_diskio.h" +#include "file.h" +#include "modutime.h" +#include "random.h" +#include "sd_diskio.h" + +/// \module os - basic "operating system" services +/// +/// The `os` module contains functions for filesystem access and `urandom`. +/// +/// The filesystem has `/` as the root directory, and the available physical +/// drives are accessible from here. They are currently: +/// +/// /SFLASH -- the serial flash filesystem +/// /SD -- the SD card (if it exists) +/// +/// On boot up, the current directory is `/SFLASH` if no SD card is inserted, +/// otherwise it is `/SD`. + +STATIC bool sd_in_root(void) { +#if MICROPY_HW_HAS_SDCARD + return sd_disk_ready(); +#else + return false; +#endif +} + +/// \function chdir(path) +/// Change current directory. +STATIC mp_obj_t os_chdir(mp_obj_t path_in) { + const char *path; + path = mp_obj_str_get_str(path_in); + + FRESULT res = f_chdrive(path); + + if (res == FR_OK) { + res = f_chdir(path); + } + // TODO: Warn if too many open files... + if (res != FR_OK) { + // TODO should be mp_type_FileNotFoundError + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "No such file or directory: '%s'", path)); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_chdir_obj, os_chdir); + +/// \function getcwd() +/// Get the current directory. +STATIC mp_obj_t os_getcwd(void) { + char buf[MICROPY_ALLOC_PATH_MAX + 1]; + FRESULT res = f_getcwd(buf, sizeof buf); + + if (res != FR_OK) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res]))); + } + + return mp_obj_new_str(buf, strlen(buf), false); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); + +/// \function listdir([dir]) +/// With no argument, list the current directory. Otherwise list the given directory. +STATIC mp_obj_t os_listdir(mp_uint_t n_args, const mp_obj_t *args) { + bool is_str_type = true; + const char *path; + if (n_args == 1) { + if (mp_obj_get_type(args[0]) == &mp_type_bytes) { + is_str_type = false; + } + path = mp_obj_str_get_str(args[0]); + } else { + path = ""; + } + + // "hack" to list root directory + if (path[0] == '/' && path[1] == '\0') { + mp_obj_t dir_list = mp_obj_new_list(0, NULL); + mp_obj_list_append(dir_list, MP_OBJ_NEW_QSTR(MP_QSTR_SFLASH)); + if (sd_in_root()) { + mp_obj_list_append(dir_list, MP_OBJ_NEW_QSTR(MP_QSTR_SD)); + } + return dir_list; + } + + FRESULT res; + FILINFO fno; + DIR dir; + + res = f_opendir(&dir, path); /* Open the directory */ + if (res != FR_OK) { + // TODO should be mp_type_FileNotFoundError + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "No such file or directory: '%s'", path)); + } + + mp_obj_t dir_list = mp_obj_new_list(0, NULL); + + for (;;) { + res = f_readdir(&dir, &fno); /* Read a directory item */ + if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ + if (fno.fname[0] == '.' && fno.fname[1] == 0) continue; /* Ignore . entry */ + if (fno.fname[0] == '.' && fno.fname[1] == '.' && fno.fname[2] == 0) continue; /* Ignore .. entry */ + + char *fn = fno.fname; + + // make a string object for this entry + mp_obj_t entry_o; + if (is_str_type) { + entry_o = mp_obj_new_str(fn, strlen(fn), false); + } else { + entry_o = mp_obj_new_bytes((const byte*)fn, strlen(fn)); + } + + // add the entry to the list + mp_obj_list_append(dir_list, entry_o); + } + + f_closedir(&dir); + + return dir_list; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(os_listdir_obj, 0, 1, os_listdir); + +/// \function mkdir(path) +/// Create a new directory. +STATIC mp_obj_t os_mkdir(mp_obj_t path_o) { + const char *path = mp_obj_str_get_str(path_o); + FRESULT res = f_mkdir(path); + switch (res) { + case FR_OK: + return mp_const_none; + case FR_EXIST: + // TODO should be FileExistsError + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "File exists: '%s'", path)); + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error creating directory '%s'", path)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_mkdir_obj, os_mkdir); + +/// \function remove(path) +/// Remove a file. +STATIC mp_obj_t os_remove(mp_obj_t path_o) { + const char *path = mp_obj_str_get_str(path_o); + // TODO check that path is actually a file before trying to unlink it + FRESULT res = f_unlink(path); + switch (res) { + case FR_OK: + return mp_const_none; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing file '%s'", path)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_remove_obj, os_remove); + +/// \function rmdir(path) +/// Remove a directory. +STATIC mp_obj_t os_rmdir(mp_obj_t path_o) { + const char *path = mp_obj_str_get_str(path_o); + // TODO check that path is actually a directory before trying to unlink it + FRESULT res = f_unlink(path); + switch (res) { + case FR_OK: + return mp_const_none; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing directory '%s'", path)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_rmdir_obj, os_rmdir); + +// Checks for path equality, ignoring trailing slashes: +// path_equal(/, /) -> true +// path_equal(/flash//, /flash) -> true +// second argument must be in canonical form (meaning no trailing slash, unless it's just /) +STATIC bool path_equal(const char *path, const char *path_canonical) { + for (; *path_canonical != '\0' && *path == *path_canonical; ++path, ++path_canonical) { + } + if (*path_canonical != '\0') { + return false; + } + for (; *path == '/'; ++path) { + } + return *path == '\0'; +} + +/// \function stat(path) +/// Get the status of a file or directory. +STATIC mp_obj_t os_stat(mp_obj_t path_in) { + const char *path = mp_obj_str_get_str(path_in); + stoupper((char *)path); + + FILINFO fno; + FRESULT res; + if (path_equal(path, "/") || path_equal(path, "/SFLASH") || path_equal(path, "/SD")) { + // stat built-in directory + if (path[1] == 'S' && !sd_in_root()) { + // no /SD directory + res = FR_NO_PATH; + goto error; + } + fno.fsize = 0; + fno.fdate = 0; + fno.ftime = 0; + fno.fattrib = AM_DIR; + } else { + res = f_stat(path, &fno); + if (res != FR_OK) { + goto error; + } + } + + mp_obj_tuple_t *t = mp_obj_new_tuple(10, NULL); + mp_int_t mode = 0; + if (fno.fattrib & AM_DIR) { + mode |= 0x4000; // stat.S_IFDIR + } else { + mode |= 0x8000; // stat.S_IFREG + } + mp_int_t seconds = mod_time_seconds_since_2000( + 1980 + ((fno.fdate >> 9) & 0x7f), + (fno.fdate >> 5) & 0x0f, + fno.fdate & 0x1f, + (fno.ftime >> 11) & 0x1f, + (fno.ftime >> 5) & 0x3f, + 2 * (fno.ftime & 0x1f) + ); + t->items[0] = mp_obj_new_int(mode); // st_mode + t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino + t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev + t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink + t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid + t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid + t->items[6] = mp_obj_new_int(fno.fsize); // st_size + t->items[7] = mp_obj_new_int(seconds); // st_atime + t->items[8] = t->items[7]; // st_mtime + t->items[9] = t->items[7]; // st_ctime + + return t; + +error: + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res]))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_stat_obj, os_stat); + +/// \function sync() +/// Sync all filesystems. +STATIC mp_obj_t os_sync(void) { + sflash_disk_flush(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_sync_obj, os_sync); + +#if MICROPY_HW_ENABLE_RNG +/// \function urandom(n) +/// Return a bytes object with n random bytes, generated by the hardware +/// random number generator. +STATIC mp_obj_t os_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + for (int i = 0; i < n; i++) { + vstr.buf[i] = rng_get(); + } + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); +#endif + +STATIC const mp_map_elem_t os_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uos) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_chdir), (mp_obj_t)&os_chdir_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_getcwd), (mp_obj_t)&os_getcwd_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_listdir), (mp_obj_t)&os_listdir_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_mkdir), (mp_obj_t)&os_mkdir_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_remove), (mp_obj_t)&os_remove_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_rmdir), (mp_obj_t)&os_rmdir_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_stat), (mp_obj_t)&os_stat_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_unlink), (mp_obj_t)&os_remove_obj }, // unlink aliases to remove + + { MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&os_sync_obj }, + + /// \constant sep - separation character used in paths + { MP_OBJ_NEW_QSTR(MP_QSTR_sep), MP_OBJ_NEW_QSTR(MP_QSTR__slash_) }, + +#if MICROPY_HW_ENABLE_RNG + { MP_OBJ_NEW_QSTR(MP_QSTR_urandom), (mp_obj_t)&os_urandom_obj }, +#endif +}; + +STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t mp_module_uos = { + .base = { &mp_type_module }, + .name = MP_QSTR_uos, + .globals = (mp_obj_dict_t*)&os_module_globals, +}; diff --git a/cc3200/mods/modusocket.c b/cc3200/mods/modusocket.c new file mode 100644 index 000000000..7356942f2 --- /dev/null +++ b/cc3200/mods/modusocket.c @@ -0,0 +1,451 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <std.h> +#include <stdint.h> +#include <string.h> + +#include "simplelink.h" +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "objtuple.h" +#include "objlist.h" +#include "runtime.h" +#include "modnetwork.h" +#include "mpexception.h" +#include "mpstate.h" + +/******************************************************************************/ +// socket class + +STATIC const mp_obj_type_t socket_type; + +// constructor socket(family=AF_INET, type=SOCK_STREAM, proto=IPPROTO_TCP, fileno=None) +STATIC mp_obj_t socket_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 4, false); + + // create socket object (not bound to any NIC yet) + mod_network_socket_obj_t *s = m_new_obj_with_finaliser(mod_network_socket_obj_t); + s->base.type = (mp_obj_t)&socket_type; + s->nic = MP_OBJ_NULL; + s->nic_type = NULL; + s->u_param.domain = AF_INET; + s->u_param.type = SOCK_STREAM; + s->u_param.proto = IPPROTO_TCP; + s->u_param.fileno = -1; + if (n_args >= 1) { + s->u_param.domain = mp_obj_get_int(args[0]); + if (n_args >= 2) { + s->u_param.type = mp_obj_get_int(args[1]); + if (n_args >= 3) { + s->u_param.proto = mp_obj_get_int(args[2]); + if (n_args == 4) { + s->u_param.fileno = mp_obj_get_int(args[3]); + } + } + } + } + return s; +} + +STATIC void socket_select_nic(mod_network_socket_obj_t *self, const byte *ip) { + if (self->nic == MP_OBJ_NULL) { + // select NIC based on IP + self->nic = mod_network_find_nic(ip); + self->nic_type = (mod_network_nic_type_t*)mp_obj_get_type(self->nic); + + // call the NIC to open the socket + int _errno; + if (self->nic_type->socket(self, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + } +} +// method socket.close() +STATIC mp_obj_t socket_close(mp_obj_t self_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic != MP_OBJ_NULL) { + self->nic_type->close(self); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); + +// method socket.bind(address) +STATIC mp_obj_t socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { + mod_network_socket_obj_t *self = self_in; + + // get address + uint8_t ip[MOD_NETWORK_IPV4ADDR_BUF_SIZE]; + mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); + + // check if we need to select a NIC + socket_select_nic(self, ip); + + // call the NIC to bind the socket + int _errno; + if (self->nic_type->bind(self, ip, port, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); + +// method socket.listen(backlog) +STATIC mp_obj_t socket_listen(mp_obj_t self_in, mp_obj_t backlog) { + mod_network_socket_obj_t *self = self_in; + + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + + int _errno; + if (self->nic_type->listen(self, mp_obj_get_int(backlog), &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen); + +// method socket.accept() +STATIC mp_obj_t socket_accept(mp_obj_t self_in) { + mod_network_socket_obj_t *self = self_in; + + // create new socket object + // starts with empty NIC so that finaliser doesn't run close() method if accept() fails + mod_network_socket_obj_t *socket2 = m_new_obj_with_finaliser(mod_network_socket_obj_t); + socket2->base.type = (mp_obj_t)&socket_type; + socket2->nic = MP_OBJ_NULL; + socket2->nic_type = NULL; + + // accept incoming connection + uint8_t ip[MOD_NETWORK_IPV4ADDR_BUF_SIZE]; + mp_uint_t port; + int _errno; + if (self->nic_type->accept(self, socket2, ip, &port, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + // new socket has valid state, so set the NIC to the same as parent + socket2->nic = self->nic; + socket2->nic_type = self->nic_type; + + // make the return value + mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL); + client->items[0] = socket2; + client->items[1] = mod_network_format_inet_addr(ip, port); + + return client; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); + +// method socket.connect(address) +STATIC mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { + mod_network_socket_obj_t *self = self_in; + + // get address + uint8_t ip[MOD_NETWORK_IPV4ADDR_BUF_SIZE]; + mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); + + // check if we need to select a NIC + socket_select_nic(self, ip); + + // call the NIC to connect the socket + int _errno; + if (self->nic_type->connect(self, ip, port, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); + +// method socket.send(bytes) +STATIC mp_obj_t socket_send(mp_obj_t self_in, mp_obj_t buf_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EBADF))); + } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + int _errno; + mp_uint_t ret = self->nic_type->send(self, bufinfo.buf, bufinfo.len, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + return mp_obj_new_int_from_uint(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send); + +// method socket.recv(bufsize) +STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + mp_int_t len = mp_obj_get_int(len_in); + vstr_t vstr; + vstr_init_len(&vstr, len); + int _errno; + mp_uint_t ret = self->nic_type->recv(self, (byte*)vstr.buf, len, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + if (ret == 0) { + return mp_const_empty_bytes; + } + vstr.len = ret; + vstr.buf[vstr.len] = '\0'; + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); + +// method socket.sendto(bytes, address) +STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { + mod_network_socket_obj_t *self = self_in; + + // get the data + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); + + // get address + uint8_t ip[MOD_NETWORK_IPV4ADDR_BUF_SIZE]; + mp_uint_t port = mod_network_parse_inet_addr(addr_in, ip); + + // check if we need to select a NIC + socket_select_nic(self, ip); + + // call the NIC to sendto + int _errno; + mp_int_t ret = self->nic_type->sendto(self, bufinfo.buf, bufinfo.len, ip, port, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_obj_new_int(ret); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(socket_sendto_obj, socket_sendto); + +// method socket.recvfrom(bufsize) +STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + vstr_t vstr; + vstr_init_len(&vstr, mp_obj_get_int(len_in)); + byte ip[4]; + mp_uint_t port; + int _errno; + mp_int_t ret = self->nic_type->recvfrom(self, (byte*)vstr.buf, vstr.len, ip, &port, &_errno); + if (ret == -1) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + mp_obj_t tuple[2]; + if (ret == 0) { + tuple[0] = mp_const_empty_bytes; + } else { + vstr.len = ret; + vstr.buf[vstr.len] = '\0'; + tuple[0] = mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + } + tuple[1] = mod_network_format_inet_addr(ip, port); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); + +// method socket.setsockopt(level, optname, value) +STATIC mp_obj_t socket_setsockopt(mp_uint_t n_args, const mp_obj_t *args) { + mod_network_socket_obj_t *self = args[0]; + + mp_int_t level = mp_obj_get_int(args[1]); + mp_int_t opt = mp_obj_get_int(args[2]); + + const void *optval; + mp_uint_t optlen; + mp_int_t val; + if (mp_obj_is_integer(args[3])) { + val = mp_obj_int_get_truncated(args[3]); + optval = &val; + optlen = sizeof(val); + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + optval = bufinfo.buf; + optlen = bufinfo.len; + } + + int _errno; + if (self->nic_type->setsockopt(self, level, opt, optval, optlen, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt); + +// method socket.settimeout(value) +// timeout=0 means non-blocking +// timeout=None means blocking +// otherwise, timeout is in seconds +STATIC mp_obj_t socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) { + mod_network_socket_obj_t *self = self_in; + if (self->nic == MP_OBJ_NULL) { + // not connected + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOTCONN))); + } + mp_uint_t timeout; + if (timeout_in == mp_const_none) { + timeout = -1; + } else { + timeout = 1000 * mp_obj_get_int(timeout_in); + } + int _errno; + if (self->nic_type->settimeout(self, timeout, &_errno) != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno))); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout); + +// method socket.setblocking(flag) +STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t blocking) { + if (mp_obj_is_true(blocking)) { + return socket_settimeout(self_in, mp_const_none); + } else { + return socket_settimeout(self_in, MP_OBJ_NEW_SMALL_INT(0)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); + +STATIC const mp_map_elem_t socket_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&socket_bind_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&socket_listen_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&socket_accept_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&socket_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&socket_send_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&socket_recv_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&socket_sendto_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&socket_recvfrom_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&socket_setsockopt_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_settimeout), (mp_obj_t)&socket_settimeout_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setblocking), (mp_obj_t)&socket_setblocking_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); + +mp_uint_t socket_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + mod_network_socket_obj_t *self = self_in; + return self->nic_type->ioctl(self, request, arg, errcode); +} + +STATIC const mp_stream_p_t socket_stream_p = { + .ioctl = socket_ioctl, + .is_text = false, +}; + +STATIC const mp_obj_type_t socket_type = { + { &mp_type_type }, + .name = MP_QSTR_socket, + .make_new = socket_make_new, + .stream_p = &socket_stream_p, + .locals_dict = (mp_obj_t)&socket_locals_dict, +}; + +/******************************************************************************/ +// usocket module + +// function usocket.getaddrinfo(host, port) +/// \function getaddrinfo(host, port) +STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { + mp_uint_t hlen; + const char *host = mp_obj_str_get_data(host_in, &hlen); + mp_int_t port = mp_obj_get_int(port_in); + + // find a NIC that can do a name lookup + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; + mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); + if (nic_type->gethostbyname != NULL) { + // Only IPv4 is supported + uint8_t out_ip[MOD_NETWORK_IPV4ADDR_BUF_SIZE]; + int32_t result = nic_type->gethostbyname(nic, host, hlen, out_ip, AF_INET); + if (result != 0) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(result))); + } + mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL); + tuple->items[0] = MP_OBJ_NEW_SMALL_INT(AF_INET); + tuple->items[1] = MP_OBJ_NEW_SMALL_INT(SOCK_STREAM); + tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); + tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_); + tuple->items[4] = mod_network_format_inet_addr(out_ip, port); + return mp_obj_new_list(1, (mp_obj_t*)&tuple); + } + } + + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_resource_not_avaliable)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_usocket_getaddrinfo_obj, mod_usocket_getaddrinfo); + +STATIC const mp_map_elem_t mp_module_usocket_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usocket) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&socket_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&mod_usocket_getaddrinfo_obj }, + + // class constants + { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET), MP_OBJ_NEW_SMALL_INT(AF_INET) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET6), MP_OBJ_NEW_SMALL_INT(AF_INET6) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_STREAM), MP_OBJ_NEW_SMALL_INT(SOCK_STREAM) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_DGRAM), MP_OBJ_NEW_SMALL_INT(SOCK_DGRAM) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_RAW), MP_OBJ_NEW_SMALL_INT(SOCK_RAW) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_TCP), MP_OBJ_NEW_SMALL_INT(IPPROTO_TCP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_UDP), MP_OBJ_NEW_SMALL_INT(IPPROTO_UDP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_RAW), MP_OBJ_NEW_SMALL_INT(IPPROTO_RAW) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_usocket_globals, mp_module_usocket_globals_table); + +const mp_obj_module_t mp_module_usocket = { + .base = { &mp_type_module }, + .name = MP_QSTR_usocket, + .globals = (mp_obj_dict_t*)&mp_module_usocket_globals, +}; diff --git a/cc3200/mods/modutime.c b/cc3200/mods/modutime.c new file mode 100644 index 000000000..9dae880e6 --- /dev/null +++ b/cc3200/mods/modutime.c @@ -0,0 +1,333 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> + +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "modutime.h" +#include "inc/hw_types.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "rom_map.h" +#include "prcm.h" +#include "pybrtc.h" +#include "mpexception.h" + + +// LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately +// after Feb 29. We calculate seconds as a signed integer relative to that. +// +// Our timebase is relative to 2000-01-01. + +#define LEAPOCH ((31 + 29) * 86400) + +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y (365*4 + 1) + + +/// \module time - time related functions +/// +/// The `time` module provides functions for getting the current time and date, +/// and for sleeping. + +STATIC const uint16_t days_since_jan1[]= { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; + +STATIC bool is_leap_year(mp_uint_t year) { + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} + +// Month is one based +STATIC mp_uint_t mod_time_days_in_month(mp_uint_t year, mp_uint_t month) { + mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1]; + if (month == 2 && is_leap_year(year)) { + mdays++; + } + return mdays; +} + +// compute the day of the year, between 1 and 366 +// month should be between 1 and 12, date should start at 1 +STATIC mp_uint_t mod_time_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { + mp_uint_t yday = days_since_jan1[month - 1] + date; + if (month >= 3 && is_leap_year(year)) { + yday += 1; + } + return yday; +} + +// returns the number of seconds, as an integer, since 2000-01-01 +mp_uint_t mod_time_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return + second + + minute * 60 + + hour * 3600 + + (mod_time_year_day(year, month, date) - 1 + + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001 + - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 + + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 + ) * 86400 + + (year - 2000) * 31536000; +} + +void mod_time_seconds_since_2000_to_struct_time(mp_uint_t t, mod_struct_time *tm) { + // The following algorithm was adapted from musl's __secs_to_tm and adapted + // for differences in Micro Python's timebase. + + mp_int_t seconds = t - LEAPOCH; + + mp_int_t days = seconds / 86400; + seconds %= 86400; + tm->tm_hour = seconds / 3600; + tm->tm_min = seconds / 60 % 60; + tm->tm_sec = seconds % 60; + + mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2) + if (wday < 0) { + wday += 7; + } + tm->tm_wday = wday; + + mp_int_t qc_cycles = days / DAYS_PER_400Y; + days %= DAYS_PER_400Y; + if (days < 0) { + days += DAYS_PER_400Y; + qc_cycles--; + } + mp_int_t c_cycles = days / DAYS_PER_100Y; + if (c_cycles == 4) { + c_cycles--; + } + days -= (c_cycles * DAYS_PER_100Y); + + mp_int_t q_cycles = days / DAYS_PER_4Y; + if (q_cycles == 25) { + q_cycles--; + } + days -= q_cycles * DAYS_PER_4Y; + + mp_int_t years = days / 365; + if (years == 4) { + years--; + } + days -= (years * 365); + + tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; + + // Note: days_in_month[0] corresponds to March + STATIC const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; + + mp_int_t month; + for (month = 0; days_in_month[month] <= days; month++) { + days -= days_in_month[month]; + } + + tm->tm_mon = month + 2; + if (tm->tm_mon >= 12) { + tm->tm_mon -= 12; + tm->tm_year++; + } + tm->tm_mday = days + 1; // Make one based + tm->tm_mon++; // Make one based + + tm->tm_yday = mod_time_year_day(tm->tm_year, tm->tm_mon, tm->tm_mday); +} + +/// \function localtime([secs]) +/// Convert a time expressed in seconds since Jan 1, 2000 into an 8-tuple which +/// contains: (year, month, mday, hour, minute, second, weekday, yearday) +/// If secs is not provided or None, then the current time from the RTC is used. +/// year includes the century (for example 2014) +/// month is 1-12 +/// mday is 1-31 +/// hour is 0-23 +/// minute is 0-59 +/// second is 0-59 +/// weekday is 0-6 for Mon-Sun. +/// yearday is 1-366 +STATIC mp_obj_t time_localtime(mp_uint_t n_args, const mp_obj_t *args) { + if (n_args == 0 || args[0] == mp_const_none) { + mod_struct_time tm; + uint32_t seconds; + uint16_t mseconds; + + // get the seconds and the milliseconds from the RTC + MAP_PRCMRTCGet(&seconds, &mseconds); + mseconds = RTC_CYCLES_U16MS(mseconds); + mod_time_seconds_since_2000_to_struct_time(seconds, &tm); + + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(mseconds) + }; + return mp_obj_new_tuple(8, tuple); + } else { + mp_int_t seconds = mp_obj_get_int(args[0]); + mod_struct_time tm; + mod_time_seconds_since_2000_to_struct_time(seconds, &tm); + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime); + + +/// \function mktime() +/// This is inverse function of localtime. It's argument is a full 8-tuple +/// which expresses a time as per localtime. It returns an integer which is +/// the number of seconds since Jan 1, 2000. +STATIC mp_obj_t time_mktime(mp_obj_t tuple) { + + mp_uint_t len; + mp_obj_t *elem; + + mp_obj_get_array(tuple, &len, &elem); + + // localtime generates a tuple of len 8. CPython uses 9, so we accept both. + if (len < 8 || len > 9) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, mpexception_num_type_invalid_arguments)); + } + + mp_int_t year = mp_obj_get_int(elem[0]); + mp_int_t month = mp_obj_get_int(elem[1]); + mp_int_t mday = mp_obj_get_int(elem[2]); + mp_int_t hours = mp_obj_get_int(elem[3]); + mp_int_t minutes = mp_obj_get_int(elem[4]); + mp_int_t seconds = mp_obj_get_int(elem[5]); + + // Normalize the tuple. This allows things like: + // + // tm_tomorrow = list(time.localtime()) + // tm_tomorrow[2] += 1 # Adds 1 to mday + // tomorrow = time.mktime(tm_tommorrow) + // + // And not have to worry about all the weird overflows. + // + // You can subtract dates/times this way as well. + + minutes += seconds / 60; + if ((seconds = seconds % 60) < 0) { + seconds += 60; + minutes--; + } + + hours += minutes / 60; + if ((minutes = minutes % 60) < 0) { + minutes += 60; + hours--; + } + + mday += hours / 24; + if ((hours = hours % 24) < 0) { + hours += 24; + mday--; + } + + month--; // make month zero based + year += month / 12; + if ((month = month % 12) < 0) { + month += 12; + year--; + } + month++; // back to one based + + while (mday < 1) { + if (--month == 0) { + month = 12; + year--; + } + mday += mod_time_days_in_month(year, month); + } + while (mday > mod_time_days_in_month(year, month)) { + mday -= mod_time_days_in_month(year, month); + if (++month == 13) { + month = 1; + year++; + } + } + return mp_obj_new_int_from_uint(mod_time_seconds_since_2000(year, month, mday, hours, minutes, seconds)); +} +MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); + + +/// \function sleep(milliseconds) +/// Sleep for the given number of milliseconds. +STATIC mp_obj_t time_sleep(mp_obj_t milliseconds_o) { + HAL_Delay(mp_obj_get_int(milliseconds_o)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_obj, time_sleep); + +/// \function time() +/// Returns the number of seconds, as an integer, since 1/1/2000. +STATIC mp_obj_t time_time(void) { + uint32_t seconds; + uint16_t mseconds; + + // get the seconds and the milliseconds from the RTC + MAP_PRCMRTCGet(&seconds, &mseconds); + return mp_obj_new_int(seconds); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); + +STATIC const mp_map_elem_t time_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_utime) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_localtime), (mp_obj_t)&time_localtime_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_mktime), (mp_obj_t)&time_mktime_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sleep), (mp_obj_t)&time_sleep_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_time_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t mp_module_utime = { + .base = { &mp_type_module }, + .name = MP_QSTR_utime, + .globals = (mp_obj_dict_t*)&time_module_globals, +}; diff --git a/cc3200/mods/modutime.h b/cc3200/mods/modutime.h new file mode 100644 index 000000000..5f1e8ed74 --- /dev/null +++ b/cc3200/mods/modutime.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +typedef struct { + uint16_t tm_year; // i.e. 2014 + uint8_t tm_mon; // 1..12 + uint8_t tm_mday; // 1..31 + uint8_t tm_hour; // 0..23 + uint8_t tm_min; // 0..59 + uint8_t tm_sec; // 0..59 + uint8_t tm_wday; // 0..6 0 = Monday + uint16_t tm_yday; // 1..366 +} mod_struct_time; + + +extern mp_uint_t mod_time_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, + mp_uint_t hour, mp_uint_t minute, mp_uint_t second); + +extern void mod_time_seconds_since_2000_to_struct_time(mp_uint_t t, mod_struct_time *tm); diff --git a/cc3200/mods/modwlan.c b/cc3200/mods/modwlan.c new file mode 100644 index 000000000..22b8cae9c --- /dev/null +++ b/cc3200/mods/modwlan.c @@ -0,0 +1,1199 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "std.h" +#include <stdint.h> +#include <stdbool.h> + +#include "simplelink.h" +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "misc.h" +#include "nlr.h" +#include "qstr.h" +#include "obj.h" +#include "objtuple.h" +#include "objlist.h" +#include "runtime.h" +#include "modnetwork.h" +#include "modwlan.h" +#include "pybioctl.h" +#include "pybuart.h" +#include "pybstdio.h" +#include "osi.h" +#include "debug.h" +#include "serverstask.h" +#include "mpexception.h" + +#ifdef USE_FREERTOS +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#endif + + +/****************************************************************************** + DEFINE TYPES + ******************************************************************************/ +// Status bits - These are used to set/reset the corresponding bits in a given variable +typedef enum{ + STATUS_BIT_NWP_INIT = 0, // If this bit is set: Network Processor is + // powered up + + STATUS_BIT_CONNECTION, // If this bit is set: the device is connected to + // the AP or client is connected to device (AP) + + STATUS_BIT_IP_LEASED, // If this bit is set: the device has leased IP to + // any connected client + + STATUS_BIT_IP_ACQUIRED, // If this bit is set: the device has acquired an IP + + STATUS_BIT_SMARTCONFIG_START, // If this bit is set: the SmartConfiguration + // process is started from SmartConfig app + + STATUS_BIT_P2P_DEV_FOUND, // If this bit is set: the device (P2P mode) + // found any p2p-device in scan + + STATUS_BIT_P2P_REQ_RECEIVED, // If this bit is set: the device (P2P mode) + // found any p2p-negotiation request + + STATUS_BIT_CONNECTION_FAILED, // If this bit is set: the device(P2P mode) + // connection to client(or reverse way) is failed + + STATUS_BIT_PING_DONE // If this bit is set: the device has completed + // the ping operation +}e_StatusBits; + +typedef struct _wlan_obj_t { + mp_obj_base_t base; + SlWlanMode_t mode; + uint32_t status; + uint8_t macAddr[SL_MAC_ADDR_LEN]; + uint8_t ssid_name[33]; + uint8_t bssid[6]; + bool servers_enabled; + + // IPVv4 data + uint32_t ip; + uint32_t gateway; + uint32_t dns; + +} wlan_obj_t; + +/****************************************************************************** + DEFINE CONSTANTS + ******************************************************************************/ +#define CLR_STATUS_BIT_ALL(status) (status = 0) +#define SET_STATUS_BIT(status, bit) (status |= ( 1 << (bit))) +#define CLR_STATUS_BIT(status, bit) (status &= ~(1 << (bit))) +#define GET_STATUS_BIT(status, bit) (0 != (status & (1 << (bit)))) + +#define IS_NW_PROCSR_ON(status) GET_STATUS_BIT(status, STATUS_BIT_NWP_INIT) +#define IS_CONNECTED(status) GET_STATUS_BIT(status, STATUS_BIT_CONNECTION) +#define IS_IP_LEASED(status) GET_STATUS_BIT(status, STATUS_BIT_IP_LEASED) +#define IS_IP_ACQUIRED(status) GET_STATUS_BIT(status, STATUS_BIT_IP_ACQUIRED) +#define IS_SMART_CFG_START(status) GET_STATUS_BIT(status, STATUS_BIT_SMARTCONFIG_START) +#define IS_P2P_DEV_FOUND(status) GET_STATUS_BIT(status, STATUS_BIT_P2P_DEV_FOUND) +#define IS_P2P_REQ_RCVD(status) GET_STATUS_BIT(status, STATUS_BIT_P2P_REQ_RECEIVED) +#define IS_CONNECT_FAILED(status) GET_STATUS_BIT(status, STATUS_BIT_CONNECTION_FAILED) +#define IS_PING_DONE(status) GET_STATUS_BIT(status, STATUS_BIT_PING_DONE) + +#define MODWLAN_SL_SCAN_ENABLE 1 +#define MODWLAN_SL_SCAN_DISABLE 0 +#define MODWLAN_SL_MAX_NETWORKS 20 + +#define MODWLAN_TIMEOUT_MS 5000 +#define MODWLAN_MAX_NETWORKS 20 + +#define ASSERT_ON_ERROR( x ) ASSERT((x) >= 0 ) + +#define IPV4_ADDR_STR_LEN_MAX (16) +#define SL_STOP_TIMEOUT 500 + +#define WLAN_MAX_RX_SIZE 16000 + +#define MAKE_SOCKADDR(addr, ip, port) sockaddr addr; \ + addr.sa_family = AF_INET; \ + addr.sa_data[0] = port >> 8; \ + addr.sa_data[1] = port; \ + addr.sa_data[2] = ip[0]; \ + addr.sa_data[3] = ip[1]; \ + addr.sa_data[4] = ip[2]; \ + addr.sa_data[5] = ip[3]; + +#define UNPACK_SOCKADDR(addr, ip, port) port = (addr.sa_data[0] << 8) | addr.sa_data[1]; \ + ip[0] = addr.sa_data[2]; \ + ip[1] = addr.sa_data[3]; \ + ip[2] = addr.sa_data[4]; \ + ip[3] = addr.sa_data[5]; + +/****************************************************************************** + DECLARE PUBLIC DATA + ******************************************************************************/ +STATIC wlan_obj_t wlan_obj; + +/****************************************************************************** + DECLARE EXPORTED DATA + ******************************************************************************/ +SemaphoreHandle_t xWlanSemaphore = NULL; + +/****************************************************************************** + DECLARE PRIVATE FUNCTIONS + ******************************************************************************/ +STATIC void wlan_initialize_data (void); +STATIC void wlan_reenable (SlWlanMode_t mode); +STATIC void wlan_get_sl_mac (void); +STATIC modwlan_Status_t wlan_do_connect (const char* ssid, uint32_t ssid_len, const char* bssid, uint8_t sec, + const char* key, uint32_t key_len); + + +//***************************************************************************** +// +//! \brief The Function Handles WLAN Events +//! +//! \param[in] pWlanEvent - Pointer to WLAN Event Info +//! +//! \return None +//! +//***************************************************************************** +void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent) +{ + if(!pWlanEvent) { + return; + } + + switch(pWlanEvent->Event) + { + case SL_WLAN_CONNECT_EVENT: + { + SET_STATUS_BIT(wlan_obj.status, STATUS_BIT_CONNECTION); + // + // Information about the connected AP (like name, MAC etc) will be + // available in 'slWlanConnectAsyncResponse_t'-Applications + // can use it if required + // + slWlanConnectAsyncResponse_t *pEventData = &pWlanEvent->EventData.STAandP2PModeWlanConnected; + + // Copy new connection SSID and BSSID to global parameters + memcpy(wlan_obj.ssid_name, pEventData->ssid_name, pEventData->ssid_len); + memcpy(wlan_obj.bssid, pEventData->bssid, SL_BSSID_LENGTH); + } + break; + case SL_WLAN_DISCONNECT_EVENT: + { + slWlanConnectAsyncResponse_t* pEventData = NULL; + + CLR_STATUS_BIT(wlan_obj.status, STATUS_BIT_CONNECTION); + CLR_STATUS_BIT(wlan_obj.status, STATUS_BIT_IP_ACQUIRED); + + pEventData = &pWlanEvent->EventData.STAandP2PModeDisconnected; + + // If the user has initiated the 'Disconnect' request, + //'reason_code' is SL_USER_INITIATED_DISCONNECTION + if (SL_USER_INITIATED_DISCONNECTION == pEventData->reason_code) { + + } + else { + + } + memset(wlan_obj.ssid_name, 0, sizeof(wlan_obj.ssid_name)); + memset(wlan_obj.bssid, 0, sizeof(wlan_obj.bssid)); + } + break; + case SL_WLAN_STA_CONNECTED_EVENT: + break; + case SL_WLAN_STA_DISCONNECTED_EVENT: + break; + case SL_WLAN_P2P_DEV_FOUND_EVENT: + break; + case SL_WLAN_P2P_NEG_REQ_RECEIVED_EVENT: + break; + case SL_WLAN_CONNECTION_FAILED_EVENT: + break; + default: + break; + } +} + +//***************************************************************************** +// +//! \brief This function handles network events such as IP acquisition, IP +//! leased, IP released etc. +//! +//! \param[in] pNetAppEvent - Pointer to NetApp Event Info +//! +//! \return None +//! +//***************************************************************************** +void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *pNetAppEvent) +{ + if(!pNetAppEvent) { + return; + } + + switch(pNetAppEvent->Event) + { + case SL_NETAPP_IPV4_IPACQUIRED_EVENT: + { + SlIpV4AcquiredAsync_t *pEventData = NULL; + + SET_STATUS_BIT(wlan_obj.status, STATUS_BIT_IP_ACQUIRED); + + // Ip Acquired Event Data + pEventData = &pNetAppEvent->EventData.ipAcquiredV4; + + // Get the IP addresses + wlan_obj.gateway = ntohl(pEventData->gateway); + wlan_obj.ip = ntohl(pEventData->ip); + wlan_obj.dns = ntohl(pEventData->dns); + } + break; + case SL_NETAPP_IPV6_IPACQUIRED_EVENT: + break; + case SL_NETAPP_IP_LEASED_EVENT: + break; + case SL_NETAPP_IP_RELEASED_EVENT: + break; + default: + break; + } +} + + +//***************************************************************************** +// +//! \brief This function handles HTTP server events +//! +//! \param[in] pServerEvent - Contains the relevant event information +//! \param[in] pServerResponse - Should be filled by the user with the +//! relevant response information +//! +//! \return None +//! +//**************************************************************************** +void SimpleLinkHttpServerCallback(SlHttpServerEvent_t *pHttpEvent, SlHttpServerResponse_t *pHttpResponse) +{ + if (!pHttpEvent) { + return; + } + + switch (pHttpEvent->Event) { + case SL_NETAPP_HTTPGETTOKENVALUE_EVENT: + break; + case SL_NETAPP_HTTPPOSTTOKENVALUE_EVENT: + break; + default: + break; + } +} + +//***************************************************************************** +// +//! \brief This function handles General Events +//! +//! \param[in] pDevEvent - Pointer to General Event Info +//! +//! \return None +//! +//***************************************************************************** +void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *pDevEvent) +{ + if (!pDevEvent) { + return; + } + + ASSERT (false); +} + + +//***************************************************************************** +// +//! This function handles socket events indication +//! +//! \param[in] pSock - Pointer to Socket Event Info +//! +//! \return None +//! +//***************************************************************************** +void SimpleLinkSockEventHandler(SlSockEvent_t *pSock) +{ + if (!pSock) { + return; + } + + switch( pSock->Event ) { + case SL_SOCKET_TX_FAILED_EVENT: + break; + default: + break; + } +} + +//***************************************************************************** +// SimpleLink Asynchronous Event Handlers -- End +//***************************************************************************** + +void wlan_init0 (void) { + // Set the mode to an invalid one + wlan_obj.mode = -1; + wlan_obj.base.type = NULL; + memset (wlan_obj.macAddr, 0, SL_MAC_ADDR_LEN); +#ifdef USE_FREERTOS + if (NULL == xWlanSemaphore) { + xWlanSemaphore = xSemaphoreCreateBinary(); + } +#endif + wlan_initialize_data (); +} + +modwlan_Status_t wlan_sl_enable (SlWlanMode_t mode, const char *ssid, uint8_t ssid_len, uint8_t sec, + const char *key, uint8_t key_len, uint8_t channel) { + + if (mode == ROLE_STA || mode == ROLE_AP || mode == ROLE_P2P) { + if (wlan_obj.mode < 0) { + wlan_obj.mode = sl_Start(0, 0, 0); + #ifdef USE_FREERTOS + xSemaphoreGive (xWlanSemaphore); + #endif + } + + // get the mac address + wlan_get_sl_mac(); + + // stop the device if it's not in station mode + if (wlan_obj.mode != ROLE_STA) { + if (ROLE_AP == wlan_obj.mode) { + // if the device is in AP mode, we need to wait for this event + // before doing anything + while (!IS_IP_ACQUIRED(wlan_obj.status)) { + HAL_Delay (5); + } + } + // switch to STA mode + ASSERT_ON_ERROR(sl_WlanSetMode(ROLE_STA)); + // stop and start again + wlan_reenable(ROLE_STA); + } + + // Device in station-mode. Disconnect previous connection if any + // The function returns 0 if 'Disconnected done', negative number if already + // disconnected Wait for 'disconnection' event if 0 is returned, Ignore + // other return-codes + if (0 == sl_WlanDisconnect()) { + while (IS_CONNECTED (wlan_obj.status)) { + HAL_Delay (5); + } + } + + // clear wlan data after checking any of the status flags + wlan_initialize_data (); + + // Set connection policy to Auto + SmartConfig (Device's default connection policy) + ASSERT_ON_ERROR(sl_WlanPolicySet(SL_POLICY_CONNECTION, SL_CONNECTION_POLICY(1, 0, 0, 0, 1), NULL, 0)); + + // Remove all profiles + ASSERT_ON_ERROR(sl_WlanProfileDel(0xFF)); + + // Enable the DHCP client + uint8_t value = 1; + ASSERT_ON_ERROR(sl_NetCfgSet(SL_IPV4_STA_P2P_CL_DHCP_ENABLE, 1, 1, &value)); + + // Set PM policy to normal + ASSERT_ON_ERROR(sl_WlanPolicySet(SL_POLICY_PM, SL_NORMAL_POLICY, NULL, 0)); + + // Unregister mDNS services + ASSERT_ON_ERROR(sl_NetAppMDNSUnRegisterService(0, 0)); + + // Remove all 64 filters (8 * 8) + _WlanRxFilterOperationCommandBuff_t RxFilterIdMask; + memset ((void *)&RxFilterIdMask, 0 ,sizeof(RxFilterIdMask)); + memset(RxFilterIdMask.FilterIdMask, 0xFF, 8); + ASSERT_ON_ERROR(sl_WlanRxFilterSet(SL_REMOVE_RX_FILTER, (_u8 *)&RxFilterIdMask, sizeof(_WlanRxFilterOperationCommandBuff_t))); + + // Set Tx power level for station or AP mode + // Number between 0-15, as dB offset from max power - 0 will set max power + uint8_t ucPower = 0; + if (mode == ROLE_AP) { + // Disable the scanning + ASSERT_ON_ERROR(sl_WlanPolicySet(SL_POLICY_SCAN, MODWLAN_SL_SCAN_DISABLE, NULL, 0)); + + // Switch to AP mode + ASSERT_ON_ERROR(sl_WlanSetMode(mode)); + ASSERT (ssid != NULL && key != NULL); + ASSERT_ON_ERROR(sl_WlanSet(SL_WLAN_CFG_GENERAL_PARAM_ID, WLAN_GENERAL_PARAM_OPT_AP_TX_POWER, sizeof(ucPower), + (unsigned char *)&ucPower)); + ASSERT_ON_ERROR(sl_WlanSet(SL_WLAN_CFG_AP_ID, WLAN_AP_OPT_SSID, ssid_len, (unsigned char *)ssid)); + ASSERT_ON_ERROR(sl_WlanSet(SL_WLAN_CFG_AP_ID, WLAN_AP_OPT_SECURITY_TYPE, sizeof(uint8_t), &sec)); + ASSERT_ON_ERROR(sl_WlanSet(SL_WLAN_CFG_AP_ID, WLAN_AP_OPT_PASSWORD, key_len, (unsigned char *)key)); + _u8* country = (_u8*)"EU"; + ASSERT_ON_ERROR(sl_WlanSet(SL_WLAN_CFG_GENERAL_PARAM_ID, WLAN_GENERAL_PARAM_OPT_COUNTRY_CODE, 2, country)); + ASSERT_ON_ERROR(sl_WlanSet(SL_WLAN_CFG_AP_ID, WLAN_AP_OPT_CHANNEL, 1, (_u8 *)&channel)); + + // Stop and start again + wlan_reenable(mode); + ASSERT (wlan_obj.mode == mode); + + SlNetAppDhcpServerBasicOpt_t dhcpParams; + dhcpParams.lease_time = 4096; // lease time (in seconds) of the IP Address + dhcpParams.ipv4_addr_start = SL_IPV4_VAL(192,168,1,2); // first IP Address for allocation. + dhcpParams.ipv4_addr_last = SL_IPV4_VAL(192,168,1,254); // last IP Address for allocation. + ASSERT_ON_ERROR(sl_NetAppStop(SL_NET_APP_DHCP_SERVER_ID)); // Stop DHCP server before settings + ASSERT_ON_ERROR(sl_NetAppSet(SL_NET_APP_DHCP_SERVER_ID, NETAPP_SET_DHCP_SRV_BASIC_OPT, + sizeof(SlNetAppDhcpServerBasicOpt_t), (_u8* )&dhcpParams)); // set parameters + ASSERT_ON_ERROR(sl_NetAppStart(SL_NET_APP_DHCP_SERVER_ID)); // Start DHCP server with new settings + + SlNetCfgIpV4Args_t ipV4; + ipV4.ipV4 = (_u32)SL_IPV4_VAL(192,168,1,1); // _u32 IP address + ipV4.ipV4Mask = (_u32)SL_IPV4_VAL(255,255,255,0); // _u32 Subnet mask for this AP + ipV4.ipV4Gateway = (_u32)SL_IPV4_VAL(192,168,1,1); // _u32 Default gateway address + ipV4.ipV4DnsServer = (_u32)SL_IPV4_VAL(192,168,1,1); // _u32 DNS server address + ASSERT_ON_ERROR(sl_NetCfgSet(SL_IPV4_AP_P2P_GO_STATIC_ENABLE, IPCONFIG_MODE_ENABLE_IPV4, + sizeof(SlNetCfgIpV4Args_t), (_u8 *)&ipV4)); + + // Stop and start again + wlan_reenable(mode); + } + // STA and P2P modes + else { + ASSERT_ON_ERROR(sl_WlanSet(SL_WLAN_CFG_GENERAL_PARAM_ID, WLAN_GENERAL_PARAM_OPT_STA_TX_POWER, + sizeof(ucPower), (unsigned char *)&ucPower)); + // Enable scanning every 60 seconds + uint32_t scanSeconds = 60; + ASSERT_ON_ERROR(sl_WlanPolicySet(SL_POLICY_SCAN , MODWLAN_SL_SCAN_ENABLE, (_u8 *)&scanSeconds, sizeof(scanSeconds))); + + if (mode == ROLE_P2P) { + // Switch to P2P mode + ASSERT_ON_ERROR(sl_WlanSetMode(mode)); + // Stop and start again + wlan_reenable(mode); + } + } + return MODWLAN_OK; + } + return MODWLAN_ERROR_INVALID_PARAMS; +} + +void wlan_sl_disable (void) { + if (wlan_obj.mode >= 0) { + #ifdef USE_FREERTOS + xSemaphoreTake (xWlanSemaphore, portMAX_DELAY); + #endif + wlan_obj.mode = -1; + ASSERT_ON_ERROR (sl_Stop(SL_STOP_TIMEOUT)); + } +} + +SlWlanMode_t wlan_get_mode (void) { + return wlan_obj.mode; +} + +void wlan_get_mac (uint8_t *macAddress) { + if (macAddress) { + memcpy (macAddress, wlan_obj.macAddr, SL_MAC_ADDR_LEN); + } +} + +void wlan_get_ip (uint32_t *ip) { + if (ip) { + *ip = IS_IP_ACQUIRED(wlan_obj.status) ? wlan_obj.ip : 0; + } +} + +void wlan_set_pm_policy (uint8_t policy) { + ASSERT_ON_ERROR(sl_WlanPolicySet(SL_POLICY_PM, policy, NULL, 0)); +} + +void wlan_servers_stop (void) { + servers_disable(); + do { + HAL_Delay (2); + } while (servers_are_enabled()); +} + +//***************************************************************************** +// DEFINE STATIC FUNCTIONS +//***************************************************************************** + +STATIC void wlan_initialize_data (void) { + wlan_obj.status = 0; + wlan_obj.dns = 0; + wlan_obj.gateway = 0; + wlan_obj.ip = 0; + memset(wlan_obj.ssid_name, 0, sizeof(wlan_obj.ssid_name)); + memset(wlan_obj.bssid, 0, sizeof(wlan_obj.bssid)); +} + +STATIC void wlan_reenable (SlWlanMode_t mode) { + // Stop and start again + wlan_obj.mode = -1; +#ifdef USE_FREERTOS + xSemaphoreTake (xWlanSemaphore, portMAX_DELAY); +#endif + ASSERT_ON_ERROR(sl_Stop(SL_STOP_TIMEOUT)); + wlan_obj.mode = sl_Start(0, 0, 0); +#ifdef USE_FREERTOS + xSemaphoreGive (xWlanSemaphore); +#endif + ASSERT (wlan_obj.mode == mode); +} + +STATIC modwlan_Status_t wlan_do_connect (const char* ssid, uint32_t ssid_len, const char* bssid, uint8_t sec, const char* key, uint32_t key_len) +{ + SlSecParams_t secParams; + + secParams.Key = (_i8*)key; + secParams.KeyLen = ((key != NULL) ? key_len : 0); + secParams.Type = sec; + + if (0 == sl_WlanConnect((_i8*)ssid, ssid_len, (_u8*)bssid, &secParams, NULL)) { + + // Wait for WLAN Event + uint32_t waitForConnectionMs = 0; + while (!IS_CONNECTED(wlan_obj.status)) { + HAL_Delay (5); + if (++waitForConnectionMs >= MODWLAN_TIMEOUT_MS) { + return MODWLAN_ERROR_TIMEOUT; + } + } + + return MODWLAN_OK; + } + + return MODWLAN_ERROR_INVALID_PARAMS; +} + +STATIC void wlan_get_sl_mac (void) { + // Get the MAC address + uint8_t macAddrLen = SL_MAC_ADDR_LEN; + sl_NetCfgGet(SL_MAC_ADDRESS_GET,NULL, &macAddrLen, wlan_obj.macAddr); +} + +/// \method init(mode, ssid=myWlan, security=wlan.WPA_WPA2, key=myWlanKey) +/// +/// Initialise the UART bus with the given parameters: +/// +/// - `mode` can be ROLE_AP, ROLE_STA and ROLE_P2P. +/// - `ssid` is the network ssid in case of AP mode +/// - `security` is the security type for AP mode +/// - `key` is the key when in AP mode +/// - `channel` is the channel to use for the AP network +STATIC const mp_arg_t wlan_init_args[] = { + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = ROLE_STA} }, + { MP_QSTR_ssid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SL_SEC_TYPE_OPEN} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5} }, +}; + +STATIC mp_obj_t wlan_init_helper(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(wlan_init_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(wlan_init_args), wlan_init_args, args); + + // get the ssid + mp_uint_t ssid_len; + const char *ssid = mp_obj_str_get_data(args[1].u_obj, &ssid_len); + + // get the key + mp_uint_t key_len; + const char *key = mp_obj_str_get_data(args[3].u_obj, &key_len); + + if (key_len < 8) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, mpexception_value_invalid_arguments)); + } + + // Force the channel to be between 1-11 + uint8_t channel = args[4].u_int > 0 ? args[4].u_int % 12 : 1; + + if (MODWLAN_OK != wlan_sl_enable (args[0].u_int, ssid, ssid_len, args[2].u_int, key, key_len, channel)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); + } + + return mp_const_none; +} + + +/******************************************************************************/ +// Micro Python bindings; WLAN class + +/// \class WLAN - driver for the WLAN functionality of the SoC + +/// \classmethod \constructor() +/// Create a wlan obecjt and initialise the simplelink engine +// +STATIC mp_obj_t wlan_make_new (mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 0, MP_ARRAY_SIZE(wlan_init_args), true); + + if (n_args > 0) { + // Get the mode + SlWlanMode_t mode = mp_obj_get_int(args[0]); + + // Stop all other processes using the wlan engine + if ( (wlan_obj.servers_enabled = servers_are_enabled()) ) { + wlan_servers_stop(); + } + if (mode == ROLE_AP) { + // start the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + wlan_init_helper(n_args, args, &kw_args); + } + // TODO: Only STA mode supported for the moment. What if P2P? + else if (n_args == 1) { + if (MODWLAN_OK != wlan_sl_enable (mode, NULL, 0, 0, NULL, 0, 0)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); + } + } + else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, mpexception_num_type_invalid_arguments)); + } + + // Start the servers again + if (wlan_obj.servers_enabled) { + servers_enable (); + } + } else if (wlan_obj.mode < 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, mpexception_num_type_invalid_arguments)); + } + + wlan_obj.base.type = (mp_obj_type_t*)&mod_network_nic_type_wlan; + // register with the network module + mod_network_register_nic(&wlan_obj); + + return &wlan_obj; +} + +STATIC void wlan_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + wlan_obj_t *self = self_in; + print(env, "wlan(mode=%u, status=%u", self->mode, self->status); + print(env, ", mac=%02x:%02x:%02x:%02x:%02x:%02x", self->macAddr[0], self->macAddr[1], self->macAddr[2], + self->macAddr[3], self->macAddr[4], self->macAddr[5]); + + // Only print the ssid if in station or ap mode + if (self->mode == ROLE_STA || self->mode == ROLE_AP) { + print(env, ", ssid=%s", self->ssid_name); + + // Only print the bssid if in station mode + if (self->mode == ROLE_STA) { + print(env, ", bssid=%02x:%02x:%02x:%02x:%02x:%02x", self->bssid[0], self->bssid[1], self->bssid[2], + self->bssid[3], self->bssid[4], self->bssid[5]); + } + + char ip_str[IPV4_ADDR_STR_LEN_MAX]; + uint8_t *ip = (uint8_t *)&self->ip; + snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + print(env, ", ip=%s", ip_str); + ip = (uint8_t *)&self->gateway; + snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + print(env, ", gateway=%s", ip_str); + ip = (uint8_t *)&self->dns; + snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + print(env, ", dns=%s)", ip_str); + } + else { + print(env, ")"); + } +} + +/// \method mode() +/// Get the wlan mode: +/// +/// - Returns the current wlan mode. Possible values are: +/// ROLE_STA, ROLE_AP and ROLE_P2P +/// +STATIC mp_obj_t wlan_getmode(mp_obj_t self_in) { + wlan_obj_t* self = self_in; + return mp_obj_new_int(self->mode); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_getmode_obj, wlan_getmode); + +STATIC mp_obj_t wlan_setpm(mp_obj_t self_in, mp_obj_t pm_mode) { + mp_int_t mode = mp_obj_get_int(pm_mode); + if (mode < SL_NORMAL_POLICY || mode > SL_LONG_SLEEP_INTERVAL_POLICY) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + } + wlan_set_pm_policy((uint8_t)mode); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(wlan_setpm_obj, wlan_setpm); + +/// \method connect(ssid, key=None, *, security=OPEN, bssid=None) +STATIC mp_obj_t wlan_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + STATIC const mp_arg_t allowed_args[] = { + { MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = SL_SEC_TYPE_OPEN} }, + { MP_QSTR_bssid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get ssid + mp_uint_t ssid_len; + const char *ssid = mp_obj_str_get_data(args[0].u_obj, &ssid_len); + + // get key and sec + mp_uint_t key_len = 0; + const char *key = NULL; + mp_uint_t sec = SL_SEC_TYPE_OPEN; + if (args[1].u_obj != mp_const_none) { + key = mp_obj_str_get_data(args[1].u_obj, &key_len); + sec = args[2].u_int; + } + + // get bssid + const char *bssid = NULL; + if (args[3].u_obj != mp_const_none) { + bssid = mp_obj_str_get_str(args[3].u_obj); + } + + if (wlan_obj.mode != ROLE_STA) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_request_not_possible)); + } + else { + if (GET_STATUS_BIT(wlan_obj.status, STATUS_BIT_CONNECTION)) { + if (0 == sl_WlanDisconnect()) { + while (IS_CONNECTED(wlan_obj.status)) { + HAL_Delay (5); + } + } + } + // connect to the requested access point + modwlan_Status_t status; + status = wlan_do_connect (ssid, ssid_len, bssid, sec, key, key_len); + if (status != MODWLAN_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); + } + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wlan_connect_obj, 1, wlan_connect); + +/// \method wlan_disconnect() +/// Closes the current WLAN connection +/// +STATIC mp_obj_t wlan_disconnect(mp_obj_t self_in) { + sl_WlanDisconnect(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_disconnect_obj, wlan_disconnect); + +/// \method is_connected() +/// Returns true if connected to the AP and an IP address has been assigned. False otherwise. +/// +STATIC mp_obj_t wlan_isconnected(mp_obj_t self_in) { + if (GET_STATUS_BIT(wlan_obj.status, STATUS_BIT_CONNECTION) && + GET_STATUS_BIT(wlan_obj.status, STATUS_BIT_IP_ACQUIRED)) { + return mp_const_true; + } + return mp_const_false; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_isconnected_obj, wlan_isconnected); + +/// \method getip() +/// Get the IP +/// +/// - Returns the acquired IP address +/// +STATIC mp_obj_t wlan_getip(mp_obj_t self_in) { + return mod_network_format_ipv4_addr ((uint8_t *)&wlan_obj.ip); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_getip_obj, wlan_getip); + + +/// \method wlan_netlist() +/// Returns a list of tuples with all the acces points within range +STATIC mp_obj_t wlan_scan(mp_obj_t self_in) { + Sl_WlanNetworkEntry_t wlanEntry; + uint8_t _index = 0; + mp_obj_t nets = NULL; + + do { + if (sl_WlanGetNetworkList(_index++, 1, &wlanEntry) <= 0) { + break; + } + mp_obj_t tuple[4]; + + tuple[0] = mp_obj_new_str((const char *)wlanEntry.ssid, wlanEntry.ssid_len, false); + tuple[1] = mp_obj_new_str((const char *)wlanEntry.bssid, SL_BSSID_LENGTH, false); + // 'Normalize' the security type + if (wlanEntry.sec_type > 2) { + wlanEntry.sec_type = 2; + } + tuple[2] = mp_obj_new_int(wlanEntry.sec_type); + tuple[3] = mp_obj_new_int(wlanEntry.rssi); + + if (_index == 1) { + // Initialize the set + nets = mp_obj_new_set(0, NULL); + } + // Add the network found to the list if it's unique + mp_obj_set_store(nets, mp_obj_new_tuple(4, tuple)); + + } while (_index < MODWLAN_SL_MAX_NETWORKS); + + return (nets != NULL) ? nets : mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_scan_obj, wlan_scan); + +STATIC mp_obj_t wlan_serversstart(mp_obj_t self_in) { + servers_enable(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_serversstart_obj, wlan_serversstart); + +STATIC mp_obj_t wlan_serversstop(mp_obj_t self_in) { + wlan_servers_stop(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_serversstop_obj, wlan_serversstop); + +STATIC mp_obj_t wlan_areserversenabled(mp_obj_t self_in) { + return MP_BOOL(servers_are_enabled()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wlan_areserversenabled_obj, wlan_areserversenabled); + +STATIC mp_obj_t wlan_serversuserpass(mp_obj_t self_in, mp_obj_t user, mp_obj_t pass) { + const char *_user = mp_obj_str_get_str(user); + const char *_pass = mp_obj_str_get_str(pass); + servers_set_user_pass((char *)_user, (char *)_pass); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(wlan_serversuserpass_obj, wlan_serversuserpass); + +STATIC const mp_map_elem_t wlan_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&wlan_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_getmode), (mp_obj_t)&wlan_getmode_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setpm), (mp_obj_t)&wlan_setpm_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_scan), (mp_obj_t)&wlan_scan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&wlan_disconnect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&wlan_isconnected_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_getip), (mp_obj_t)&wlan_getip_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_serversstart), (mp_obj_t)&wlan_serversstart_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_serversstop), (mp_obj_t)&wlan_serversstop_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_areserversenabled), (mp_obj_t)&wlan_areserversenabled_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_serversuserpass), (mp_obj_t)&wlan_serversuserpass_obj }, + + // class constants + { MP_OBJ_NEW_QSTR(MP_QSTR_OPEN), MP_OBJ_NEW_SMALL_INT(SL_SEC_TYPE_OPEN) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WEP), MP_OBJ_NEW_SMALL_INT(SL_SEC_TYPE_WEP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WPA_WPA2), MP_OBJ_NEW_SMALL_INT(SL_SEC_TYPE_WPA_WPA2) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WPA_ENT), MP_OBJ_NEW_SMALL_INT(SL_SEC_TYPE_WPA_ENT) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WPS_PBC), MP_OBJ_NEW_SMALL_INT(SL_SEC_TYPE_WPS_PBC) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WPS_PIN), MP_OBJ_NEW_SMALL_INT(SL_SEC_TYPE_WPS_PIN) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_STA), MP_OBJ_NEW_SMALL_INT(ROLE_STA) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AP), MP_OBJ_NEW_SMALL_INT(ROLE_AP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_P2P), MP_OBJ_NEW_SMALL_INT(ROLE_P2P) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_NORMAL_PM), MP_OBJ_NEW_SMALL_INT(SL_NORMAL_POLICY) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LOW_LATENCY_PM), MP_OBJ_NEW_SMALL_INT(SL_LOW_LATENCY_POLICY) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LOW_POWER_PM), MP_OBJ_NEW_SMALL_INT(SL_LOW_POWER_POLICY) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ALWAYS_ON_PM), MP_OBJ_NEW_SMALL_INT(SL_ALWAYS_ON_POLICY) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LONG_SLEEP_PM), MP_OBJ_NEW_SMALL_INT(SL_LONG_SLEEP_INTERVAL_POLICY) }, +}; +STATIC MP_DEFINE_CONST_DICT(wlan_locals_dict, wlan_locals_dict_table); + + +/******************************************************************************/ +// Micro Python bindings; WLAN socket + +STATIC int wlan_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uint8_t *out_ip, uint8_t family) { + uint32_t ip; + int result = sl_NetAppDnsGetHostByName((_i8 *)name, (_u16)len, (_u32*)&ip, (_u8)family); + + out_ip[0] = ip >> 24; + out_ip[1] = ip >> 16; + out_ip[2] = ip >> 8; + out_ip[3] = ip; + + return result; +} + +STATIC int wlan_socket_socket(struct _mod_network_socket_obj_t *s, int *_errno) { + // open the socket + int16_t sd = sl_Socket(s->u_param.domain, s->u_param.type, s->u_param.proto); + if (s->sd < 0) { + *_errno = s->sd; + return -1; + } + + // mark the socket not closed + s->closed = false; + // save the socket descriptor + s->sd = sd; + + // make it blocking by default + int32_t optval = 0; + sl_SetSockOpt(sd, SOL_SOCKET, SO_NONBLOCKING, &optval, (SlSocklen_t)sizeof(optval)); + + return 0; +} + +STATIC void wlan_socket_close(mod_network_socket_obj_t *s) { + s->closed = true; + sl_Close(s->sd); +} + +STATIC int wlan_socket_bind(mod_network_socket_obj_t *s, byte *ip, mp_uint_t port, int *_errno) { + MAKE_SOCKADDR(addr, ip, port) + int ret = sl_Bind(s->sd, &addr, sizeof(addr)); + if (ret != 0) { + *_errno = ret; + return -1; + } + return 0; +} + +STATIC int wlan_socket_listen(mod_network_socket_obj_t *s, mp_int_t backlog, int *_errno) { + int ret = sl_Listen(s->sd, backlog); + if (ret != 0) { + *_errno = ret; + return -1; + } + return 0; +} + +STATIC int wlan_socket_accept(mod_network_socket_obj_t *s, mod_network_socket_obj_t *s2, byte *ip, mp_uint_t *port, int *_errno) { + // accept incoming connection + int16_t sd; + sockaddr addr; + socklen_t addr_len = sizeof(addr); + if ((sd = sl_Accept(s->sd, &addr, &addr_len)) < 0) { + *_errno = sd; + return -1; + } + + // Mark the socket not closed and save the new descriptor + s2->closed = false; + s2->sd = sd; + + // return ip and port + UNPACK_SOCKADDR(addr, ip, *port); + + return 0; +} + +STATIC int wlan_socket_connect(mod_network_socket_obj_t *s, byte *ip, mp_uint_t port, int *_errno) { + MAKE_SOCKADDR(addr, ip, port) + int ret = sl_Connect(s->sd, &addr, sizeof(addr)); + if (ret != 0) { + *_errno = ret; + return -1; + } + return 0; +} + +STATIC int wlan_socket_send(mod_network_socket_obj_t *s, const byte *buf, mp_uint_t len, int *_errno) { + if (s->closed) { + sl_Close (s->sd); + *_errno = EBADF; + return -1; + } + + mp_int_t bytes = 0; + if (len > 0) { + bytes = sl_Send(s->sd, (const void *)buf, len, 0); + } + if (bytes <= 0) { + *_errno = bytes; + return -1; + } + + return bytes; +} + +STATIC int wlan_socket_recv(mod_network_socket_obj_t *s, byte *buf, mp_uint_t len, int *_errno) { + // check if the socket is open + if (s->closed) { + // socket is closed, but the CC3200 may have some data remaining in its buffer, so check + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(s->sd, &rfds); + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 2; + int nfds = sl_Select(s->sd + 1, &rfds, NULL, NULL, &tv); + if (nfds == -1 || !FD_ISSET(s->sd, &rfds)) { + // no data waiting, so close socket and return 0 data + sl_Close(s->sd); + return 0; + } + } + + // cap length at WLAN_MAX_RX_SIZE + len = MIN(len, WLAN_MAX_RX_SIZE); + + // do the recv + int ret = sl_Recv(s->sd, buf, len, 0); + if (ret < 0) { + *_errno = ret; + return -1; + } + + return ret; +} + +STATIC int wlan_socket_sendto( mod_network_socket_obj_t *s, const byte *buf, mp_uint_t len, byte *ip, mp_uint_t port, int *_errno) { + MAKE_SOCKADDR(addr, ip, port) + int ret = sl_SendTo(s->sd, (byte*)buf, len, 0, (sockaddr*)&addr, sizeof(addr)); + if (ret < 0) { + *_errno = ret; + return -1; + } + return ret; +} + +STATIC int wlan_socket_recvfrom(mod_network_socket_obj_t *s, byte *buf, mp_uint_t len, byte *ip, mp_uint_t *port, int *_errno) { + sockaddr addr; + socklen_t addr_len = sizeof(addr); + mp_int_t ret = sl_RecvFrom(s->sd, buf, len, 0, &addr, &addr_len); + if (ret < 0) { + *_errno = ret; + return -1; + } + UNPACK_SOCKADDR(addr, ip, *port); + return ret; +} + +STATIC int wlan_socket_setsockopt(mod_network_socket_obj_t *socket, mp_uint_t level, mp_uint_t opt, const void *optval, mp_uint_t optlen, int *_errno) { + int ret = sl_SetSockOpt(socket->sd, level, opt, optval, optlen); + if (ret < 0) { + *_errno = ret; + return -1; + } + return 0; +} + +STATIC int wlan_socket_settimeout(mod_network_socket_obj_t *s, mp_uint_t timeout_ms, int *_errno) { + int ret; + if (timeout_ms == 0 || timeout_ms == -1) { + int optval; + if (timeout_ms == 0) { + // set non-blocking mode + optval = 1; + } else { + // set blocking mode + optval = 0; + } + ret = sl_SetSockOpt(s->sd, SOL_SOCKET, SO_NONBLOCKING, &optval, sizeof(optval)); + } else { + // set timeout + ret = sl_SetSockOpt(s->sd, SOL_SOCKET, SO_RCVTIMEO, &timeout_ms, sizeof(timeout_ms)); + } + + if (ret != 0) { + *_errno = ret; + return -1; + } + + return 0; +} + +STATIC int wlan_socket_ioctl (mod_network_socket_obj_t *s, mp_uint_t request, mp_uint_t arg, int *_errno) { + mp_int_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + int32_t sd = s->sd; + + // init fds + fd_set rfds, wfds, xfds; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + + // set fds if needed + if (flags & MP_IOCTL_POLL_RD) { + FD_SET(sd, &rfds); + + // A socked that just closed is available for reading. A call to + // recv() returns 0 which is consistent with BSD. + if (s->closed) { + ret |= MP_IOCTL_POLL_RD; + } + } + if (flags & MP_IOCTL_POLL_WR) { + FD_SET(sd, &wfds); + } + if (flags & MP_IOCTL_POLL_HUP) { + FD_SET(sd, &xfds); + } + + // call simplelink select with minimum timeout + SlTimeval_t tv; + tv.tv_sec = 0; + tv.tv_usec = 1; + int32_t nfds = sl_Select(sd + 1, &rfds, &wfds, &xfds, &tv); + + // check for error + if (nfds == -1) { + *_errno = nfds; + return -1; + } + + // check return of select + if (FD_ISSET(sd, &rfds)) { + ret |= MP_IOCTL_POLL_RD; + } + if (FD_ISSET(sd, &wfds)) { + ret |= MP_IOCTL_POLL_WR; + } + if (FD_ISSET(sd, &xfds)) { + ret |= MP_IOCTL_POLL_HUP; + } + } else { + *_errno = EINVAL; + ret = -1; + } + return ret; +} + +const mod_network_nic_type_t mod_network_nic_type_wlan = { + .base = { + { &mp_type_type }, + .name = MP_QSTR_WLAN, + .print = wlan_print, + .make_new = wlan_make_new, + .locals_dict = (mp_obj_t)&wlan_locals_dict, + }, + .gethostbyname = wlan_gethostbyname, + .socket = wlan_socket_socket, + .close = wlan_socket_close, + .bind = wlan_socket_bind, + .listen = wlan_socket_listen, + .accept = wlan_socket_accept, + .connect = wlan_socket_connect, + .send = wlan_socket_send, + .recv = wlan_socket_recv, + .sendto = wlan_socket_sendto, + .recvfrom = wlan_socket_recvfrom, + .setsockopt = wlan_socket_setsockopt, + .settimeout = wlan_socket_settimeout, + .ioctl = wlan_socket_ioctl, +}; diff --git a/cc3200/mods/modwlan.h b/cc3200/mods/modwlan.h new file mode 100644 index 000000000..7a84f2a51 --- /dev/null +++ b/cc3200/mods/modwlan.h @@ -0,0 +1,71 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SIMPLELINKTASK_H_ +#define SIMPLELINKTASK_H_ + +/****************************************************************************** + DEFINE CONSTANTS + ******************************************************************************/ +#define SIMPLELINK_SPAWN_TASK_PRIORITY 3 +#define SIMPLELINK_TASK_STACK_SIZE 2048 + +/****************************************************************************** + DEFINE TYPES + ******************************************************************************/ +typedef enum +{ + MODWLAN_OK = 0, + MODWLAN_ERROR_INVALID_PARAMS = -1, + MODWLAN_ERROR_TIMEOUT = -2, + MODWLAN_ERROR_UNKNOWN = -3 + +}modwlan_Status_t; + +/****************************************************************************** + DECLARE PUBLIC DATA + ******************************************************************************/ +#ifdef USE_FREERTOS +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +extern SemaphoreHandle_t xWlanSemaphore; +#endif + +/****************************************************************************** + DECLARE PUBLIC FUNCTIONS + ******************************************************************************/ +extern void wlan_init0 (void); +extern modwlan_Status_t wlan_sl_enable (SlWlanMode_t mode, const char *ssid, uint8_t ssid_len, uint8_t sec, + const char *key, uint8_t key_len, uint8_t channel); +extern void wlan_sl_disable (void); +extern SlWlanMode_t wlan_get_mode (void); +extern void wlan_get_mac (uint8_t *macAddress); +extern void wlan_get_ip (uint32_t *ip); +extern void wlan_set_pm_policy (uint8_t policy); +extern void wlan_servers_stop (void); + +#endif /* SIMPLELINKTASK_H_ */ diff --git a/cc3200/mods/pybextint.c b/cc3200/mods/pybextint.c new file mode 100644 index 000000000..abfec5572 --- /dev/null +++ b/cc3200/mods/pybextint.c @@ -0,0 +1,357 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <stddef.h> +#include <string.h> + +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "py/nlr.h" +#include "misc.h" +#include "qstr.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/pfenv.h" +#include "py/objlist.h" +#include "inc/hw_types.h" +#include "inc/hw_gpio.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "rom_map.h" +#include "pin.h" +#include "gpio.h" +#include "pybgpio.h" +#include "pybextint.h" +#include "mpexception.h" +#include "interrupt.h" +#include "mpstate.h" +#include "cc3200_asm.h" + +/// \moduleref pyb +/// \class ExtInt - configure I/O pins to interrupt on external events +/// +/// There are a maximum of 25 GPIO interrupt lines. +/// +/// Example callback: +/// +/// def callback(line): +/// print(line.pin()) +/// +/// Note: ExtInt will automatically configure the gpio line as an input. +/// +/// extint = pyb.ExtInt('GPIO10', pyb.ExtInt.IRQ_FALLING, pyb.GPIO.STD_PU, callback) +/// +/// Now every time a falling edge is seen on the gpio pin, the callback will be +/// called. Caution: mechanical pushbuttons have "bounce" and pushing or +/// releasing a switch will often generate multiple edges. +/// See: http://www.eng.utah.edu/~cs5780/debouncing.pdf for a detailed +/// explanation, along with various techniques for debouncing. +/// +/// All gpio objects go through the gpio mapper to come up with one of the +/// gpio pins. +/// +/// extint = pyb.ExtInt(gpio, mode, pull, callback) +/// +/// There is also a C API, so that drivers which require EXTI interrupt lines +/// can also use this code. See pybextint.h for the available functions. + +STATIC void ExecuteIntCallback (extint_obj_t *self); +STATIC void GPIOA0IntHandler (void); +STATIC void GPIOA1IntHandler (void); +STATIC void GPIOA2IntHandler (void); +STATIC void GPIOA3IntHandler (void); +STATIC void EXTI_Handler(uint port); + +STATIC extint_obj_t* extint_add (uint pin_num, uint port, uint bit) { + extint_obj_t *self = m_new_obj(extint_obj_t); + + self->port = port; + self->bit = bit; + self->callback = NULL; + self->pin_num = pin_num; + // add it to the list + mp_obj_list_append(&MP_STATE_PORT(pyb_extint_list), self); + + return self; +} + +STATIC extint_obj_t* extint_find (uint port, uint8_t bit) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(pyb_extint_list).len; i++) { + extint_obj_t *self = (extint_obj_t *)MP_STATE_PORT(pyb_extint_list).items[i]; + if (self->port == port && self->bit == bit) { + return self; + } + } + return NULL; +} + +/// \method line() +/// Return the pin number to which this external interrupt is mapped to. +STATIC mp_obj_t extint_obj_pin(mp_obj_t self_in) { + extint_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(self->pin_num); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_pin_obj, extint_obj_pin); + +/// \method enable() +/// Enable a disabled interrupt. +STATIC mp_obj_t extint_obj_enable(mp_obj_t self_in) { + extint_obj_t *self = self_in; + extint_enable(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_enable_obj, extint_obj_enable); + +/// \method disable() +/// Disable the interrupt associated with the ExtInt object. +/// This could be useful for debouncing. +STATIC mp_obj_t extint_obj_disable(mp_obj_t self_in) { + extint_obj_t *self = self_in; + extint_disable(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_disable_obj, extint_obj_disable); + +/// \method swint() +/// Trigger the callback from software. +STATIC mp_obj_t extint_obj_swint(mp_obj_t self_in) { + extint_obj_t *self = self_in; + extint_swint(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint); + +/// \classmethod \constructor(pin, mode, pull, callback) +/// Create an ExtInt object: +/// +/// - `gpio` is the gpio on which to enable the interrupt (can be a gpio object or any valid gpio name). +/// - `mode` can be one of: +/// - `ExtInt.IRQ_RISING` - trigger on a rising edge; +/// - `ExtInt.IRQ_FALLING` - trigger on a falling edge; +/// - `ExtInt.IRQ_RISING_FALLING` - trigger on a rising or falling edge. +/// - `pull` can be one of: +/// - `pyb.Pin.PULL_NONE` - no pull up or down resistors; +/// - `pyb.Pin.PULL_UP` - enable the pull-up resistor; +/// - `pyb.Pin.PULL_DOWN` - enable the pull-down resistor. +/// - `callback` is the function to call when the interrupt triggers. The +/// callback function must accept exactly 1 argument, which is the line that +/// triggered the interrupt. +STATIC const mp_arg_t pyb_extint_make_new_args[] = { + { MP_QSTR_gpio, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_pull, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_callback, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, +}; +#define PYB_EXTINT_MAKE_NEW_NUM_ARGS MP_ARRAY_SIZE(pyb_extint_make_new_args) + +STATIC mp_obj_t extint_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + // parse args + mp_arg_val_t vals[PYB_EXTINT_MAKE_NEW_NUM_ARGS]; + mp_arg_parse_all_kw_array(n_args, n_kw, args, PYB_EXTINT_MAKE_NEW_NUM_ARGS, pyb_extint_make_new_args, vals); + + extint_obj_t *self = extint_register(vals[0].u_obj, vals[1].u_int, vals[2].u_int, vals[3].u_obj); + self->base.type = type_in; + + return self; +} + +STATIC void extint_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + extint_obj_t *self = self_in; + print(env, "<ExtInt pin=%u>", self->pin_num); +} + +STATIC const mp_map_elem_t extint_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_pin), (mp_obj_t)&extint_obj_pin_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_enable), (mp_obj_t)&extint_obj_enable_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disable), (mp_obj_t)&extint_obj_disable_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_swint), (mp_obj_t)&extint_obj_swint_obj }, + + // class constants + /// \constant IRQ_RISING - interrupt on a rising edge + /// \constant IRQ_FALLING - interrupt on a falling edge + /// \constant IRQ_RISING_FALLING - interrupt on a rising or falling edge + /// \constant IRQ_LOW_LEVEL - interrupt on a low level + /// \constant IRQ_HIGH_LEVEL - interrupt on a high level + { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_FALLING), MP_OBJ_NEW_SMALL_INT(GPIO_FALLING_EDGE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_RISING), MP_OBJ_NEW_SMALL_INT(GPIO_RISING_EDGE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_RISING_FALLING), MP_OBJ_NEW_SMALL_INT(GPIO_BOTH_EDGES) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_LOW_LEVEL), MP_OBJ_NEW_SMALL_INT(GPIO_LOW_LEVEL) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_HIGH_LEVEL), MP_OBJ_NEW_SMALL_INT(GPIO_HIGH_LEVEL) }, +}; + +STATIC MP_DEFINE_CONST_DICT(extint_locals_dict, extint_locals_dict_table); + +STATIC void ExecuteIntCallback (extint_obj_t *self) { + if (self->callback != mp_const_none) { + // disable interrupts to avoid nesting + uint primsk = disable_irq(); + // when executing code within a handler we must lock the GC to prevent + // any memory allocations. We must also catch any exceptions. + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_1(self->callback, self); + nlr_pop(); + } else { + // uncaught exception; disable the callback so it doesn't run again + self->callback = mp_const_none; + extint_disable(self); + // printing an exception here will cause a stack overflow that ends up in a + // hard fault so, is better to signal the uncaught (probably non-recoverable) + // exception by blinkg the BLD + // TODO: Blink the BLD + } + gc_unlock(); + enable_irq(primsk); + } +} + +STATIC void GPIOA0IntHandler (void) { + EXTI_Handler(GPIOA0_BASE); +} + +STATIC void GPIOA1IntHandler (void) { + EXTI_Handler(GPIOA1_BASE); +} + +STATIC void GPIOA2IntHandler (void) { + EXTI_Handler(GPIOA2_BASE); +} + +STATIC void GPIOA3IntHandler (void) { + EXTI_Handler(GPIOA3_BASE); +} + +// common interrupt handler +STATIC void EXTI_Handler(uint port) { + extint_obj_t *self; + uint32_t bit = MAP_GPIOIntStatus(port, true); + + MAP_GPIOIntClear(port, bit); + if (NULL != (self = extint_find(port, bit))) { + ExecuteIntCallback(self); + } +} + +const mp_obj_type_t extint_type = { + { &mp_type_type }, + .name = MP_QSTR_ExtInt, + .print = extint_obj_print, + .make_new = extint_make_new, + .locals_dict = (mp_obj_t)&extint_locals_dict, +}; + +void extint_init0(void) { + mp_obj_list_init(&MP_STATE_PORT(pyb_extint_list), 0); +} + +extint_obj_t* extint_register(mp_obj_t pin_obj, uint32_t intmode, uint32_t pull, mp_obj_t callback) { + const gpio_obj_t *gpio = NULL; + extint_obj_t* self; + void *handler; + uint32_t intnum; + + gpio = gpio_find(pin_obj); + + if (intmode != GPIO_FALLING_EDGE && + intmode != GPIO_RISING_EDGE && + intmode != GPIO_BOTH_EDGES && + intmode != GPIO_LOW_LEVEL && + intmode != GPIO_HIGH_LEVEL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_value_invalid_arguments)); + } + if (pull != PIN_TYPE_STD && + pull != PIN_TYPE_STD_PU && + pull != PIN_TYPE_STD_PD && + pull != PIN_TYPE_OD && + pull != PIN_TYPE_OD_PU && + pull != PIN_TYPE_OD_PD) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_value_invalid_arguments)); + } + + if (NULL == (self = extint_find(gpio->port, gpio->bit))) { + self = extint_add(gpio->pin_num, gpio->port, gpio->bit); + } + else { + // we need to update the callback atomically, so we disable the line + // before we update anything. + extint_disable(self); + } + + // invalidate the callback + self->callback = NULL; + + // before enabling the interrupt, configure the gpio pin + gpio_config(gpio, PIN_MODE_0, GPIO_DIR_MODE_IN, pull, PIN_STRENGTH_4MA); + + MAP_GPIOIntTypeSet(gpio->port, gpio->bit, intmode); + switch (gpio->port) { + case GPIOA0_BASE: + handler = GPIOA0IntHandler; + intnum = INT_GPIOA0; + break; + case GPIOA1_BASE: + handler = GPIOA1IntHandler; + intnum = INT_GPIOA1; + break; + case GPIOA2_BASE: + handler = GPIOA2IntHandler; + intnum = INT_GPIOA2; + break; + case GPIOA3_BASE: + default: + handler = GPIOA3IntHandler; + intnum = INT_GPIOA3; + break; + } + + MAP_GPIOIntRegister(gpio->port, handler); + // set the interrupt to the lowest priority, to make sure that no ther + // isr will be preemted by this one + MAP_IntPrioritySet(intnum, INT_PRIORITY_LVL_7); + + // set the new callback + self->callback = callback; + // enable the interrupt just before leaving + extint_enable(self); + + return self; +} + +void extint_enable(extint_obj_t *self) { + MAP_GPIOIntClear(self->port, self->bit); + MAP_GPIOIntEnable(self->port, self->bit); +} + +void extint_disable(extint_obj_t *self) { + MAP_GPIOIntDisable(self->port, self->bit); +} + +void extint_swint(extint_obj_t *self) { + ExecuteIntCallback(self); +} diff --git a/cc3200/mods/pybextint.h b/cc3200/mods/pybextint.h new file mode 100644 index 000000000..b479403b2 --- /dev/null +++ b/cc3200/mods/pybextint.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef PYBEXTINT_H_ +#define PYBEXTINT_H_ + +typedef struct { + mp_obj_base_t base; + mp_obj_t callback; + uint32_t port; + uint8_t pin_num; + uint8_t bit; +} extint_obj_t; + +extern const mp_obj_type_t extint_type; + +void extint_init0(void); +extint_obj_t* extint_register(mp_obj_t pin_obj, uint32_t intmode, uint32_t pull, mp_obj_t callback); +void extint_enable(extint_obj_t *self); +void extint_disable(extint_obj_t *self); +void extint_swint(extint_obj_t *self); + +#endif /* PYBEXTINT_H_ */ diff --git a/cc3200/mods/pybgpio.c b/cc3200/mods/pybgpio.c new file mode 100644 index 000000000..cf52b501d --- /dev/null +++ b/cc3200/mods/pybgpio.c @@ -0,0 +1,529 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "py/mpstate.h" +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" +#include "inc/hw_types.h" +#include "inc/hw_gpio.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "rom_map.h" +#include "pin.h" +#include "prcm.h" +#include "gpio.h" +#include "pybgpio.h" +#include "mpexception.h" + + +/// \moduleref pyb +/// \class GPIO - control I/O pins +/// +/// A pin is the basic object to control I/O pins. It has methods to set +/// the mode of the pin (input or output) and methods to get and set the +/// digital logic level. For analog control of a pin, see the ADC class. +/// +/// Usage Model: +/// +/// All CPU Pins are predefined as pyb.GPIO.cpu.Name +/// +/// GPIO9_pin = pyb.GPIO.cpu.GPIO9 +/// +/// g = pyb.GPIO(pyb.GPIO.cpu.GPIO9, 0, pyb.GPIO.IN) +/// +/// CPU pins which correspond to the board pins are available +/// as `pyb.cpu.Name`. +/// +/// You can also use strings: +/// +/// g = pyb.GPIO('GPIO9', 0) +/// +/// Users can add their own names: +/// +/// MyMapperDict = { 'LeftMotorDir' : pyb.GPIO.cpu.GPIO11 } +/// pyb.GPIO.dict(MyMapperDict) +/// g = pyb.GPIO("LeftMotorDir", 0) +/// +/// and can query mappings +/// +/// g = pyb.GPIO("LeftMotorDir") +/// +/// Users can also add their own mapping function: +/// +/// def MyMapper(gpio_name): +/// if gpio_name == "LeftMotorDir": +/// return pyb.GPIO.cpu.GPIO11 +/// +/// pyb.GPIO.mapper(MyMapper) +/// +/// So, if you were to call: `pyb.GPIO("LeftMotorDir", pyb.GPIO.OUT)` +/// then `"LeftMotorDir"` is passed directly to the mapper function. +/// +/// To summarise, the following order determines how things get mapped into +/// an ordinal pin number: +/// +/// 1. Directly specify a GPIO object +/// 2. User supplied mapping function +/// 3. User supplied mapping (object must be usable as a dictionary key) +/// 4. Supply a string which matches a CPU port/pin + +void gpio_init0(void) { + MP_STATE_PORT(gpio_class_mapper) = mp_const_none; + MP_STATE_PORT(gpio_class_map_dict) = mp_const_none; +} + +// C API used to convert a user-supplied pin name into an ordinal pin number. +const gpio_obj_t *gpio_find(mp_obj_t user_obj) { + const gpio_obj_t *gpio_obj; + + // If a pin was provided, then use it + if (MP_OBJ_IS_TYPE(user_obj, &gpio_type)) { + gpio_obj = user_obj; + return gpio_obj; + } + + if (MP_STATE_PORT(gpio_class_mapper) != mp_const_none) { + gpio_obj = mp_call_function_1(MP_STATE_PORT(gpio_class_mapper), user_obj); + if (gpio_obj != mp_const_none) { + if (!MP_OBJ_IS_TYPE(gpio_obj, &gpio_type)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_resource_not_avaliable)); + } + return gpio_obj; + } + // The pin mapping function returned mp_const_none, fall through to + // other lookup methods. + } + + if (MP_STATE_PORT(gpio_class_map_dict) != mp_const_none) { + mp_map_t *gpio_map_map = mp_obj_dict_get_map(MP_STATE_PORT(gpio_class_map_dict)); + mp_map_elem_t *elem = mp_map_lookup(gpio_map_map, user_obj, MP_MAP_LOOKUP); + if (elem != NULL && elem->value != NULL) { + gpio_obj = elem->value; + return gpio_obj; + } + } + + // See if the pin name matches a cpu pin + gpio_obj = gpio_find_named_pin(&gpio_cpu_pins_locals_dict, user_obj); + if (gpio_obj) { + return gpio_obj; + } + + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); +} + +void gpio_config(const gpio_obj_t *self, uint af, uint mode, uint type, uint strength) { + // PIN_MODE_0 means it stays as a GPIO, else, another peripheral will take control of it + if (af == PIN_MODE_0) { + // enable the peripheral clock for the GPIO port of this pin + switch (self->port) { + case PORT_A0: + MAP_PRCMPeripheralClkEnable(PRCM_GPIOA0, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); + break; + case PORT_A1: + MAP_PRCMPeripheralClkEnable(PRCM_GPIOA1, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); + break; + case PORT_A2: + MAP_PRCMPeripheralClkEnable(PRCM_GPIOA2, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); + break; + case PORT_A3: + MAP_PRCMPeripheralClkEnable(PRCM_GPIOA3, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); + break; + default: + break; + } + // configure the direction + MAP_GPIODirModeSet(self->port, self->bit, mode); + } + + // now set the alternate function, strenght and type + MAP_PinModeSet (self->pin_num, af); + MAP_PinConfigSet(self->pin_num, strength, type); +} + +/// \method print() +/// Return a string describing the pin object. +STATIC void gpio_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + gpio_obj_t *self = self_in; + uint32_t af = MAP_PinModeGet(self->pin_num); + uint32_t type = gpio_get_type(self); + uint32_t strength = gpio_get_strenght(self); + + // pin name + print(env, "GPIO(GPIO.cpu.%s, af=%u", qstr_str(self->name), af); + + if (af == PIN_MODE_0) { + // IO mode + qstr mode_qst; + uint32_t mode = gpio_get_mode(self); + if (mode == GPIO_DIR_MODE_IN) { + mode_qst = MP_QSTR_IN; + } else { + mode_qst = MP_QSTR_OUT; + } + print(env, ", mode=GPIO.%s", qstr_str(mode_qst)); // safe because mode_qst has no formatting chars + } + + // pin type + qstr type_qst; + if (type == PIN_TYPE_STD) { + type_qst = MP_QSTR_STD; + } else if (type == PIN_TYPE_STD_PU) { + type_qst = MP_QSTR_STD_PU; + } else if (type == PIN_TYPE_STD_PD) { + type_qst = MP_QSTR_STD_PD; + } else if (type == PIN_TYPE_OD) { + type_qst = MP_QSTR_OD; + } else if (type == PIN_TYPE_OD_PU) { + type_qst = MP_QSTR_OD_PU; + } else { + type_qst = MP_QSTR_OD_PD; + } + print(env, ", pull=GPIO.%s", qstr_str(type_qst)); + + // Strength + qstr str_qst; + if (strength == PIN_STRENGTH_2MA) { + str_qst = MP_QSTR_S2MA; + } else if (strength == PIN_STRENGTH_4MA) { + str_qst = MP_QSTR_S4MA; + } else { + str_qst = MP_QSTR_S6MA; + } + print(env, ", strength=GPIO.%s)", qstr_str(str_qst)); +} + +STATIC mp_obj_t gpio_obj_init_helper(const gpio_obj_t *pin, mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args); + +/// \classmethod \constructor(id, ...) +/// Create a new GPIO object associated with the id. If additional arguments are given, +/// they are used to initialise the pin. See `init`. +STATIC mp_obj_t gpio_make_new(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // Run an argument through the mapper and return the result. + const gpio_obj_t *pin = gpio_find(args[0]); + + if (n_args > 1) { + // pin af given, so configure it + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + gpio_obj_init_helper(pin, n_args - 1, args + 1, &kw_args); + } + + return (mp_obj_t)pin; +} + +/// \classmethod mapper([fun]) +/// Get or set the pin mapper function. +STATIC mp_obj_t gpio_mapper(mp_uint_t n_args, const mp_obj_t *args) { + if (n_args > 1) { + MP_STATE_PORT(gpio_class_mapper) = args[1]; + return mp_const_none; + } + return MP_STATE_PORT(gpio_class_mapper); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gpio_mapper_fun_obj, 1, 2, gpio_mapper); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(gpio_mapper_obj, (mp_obj_t)&gpio_mapper_fun_obj); + +/// \classmethod dict([dict]) +/// Get or set the pin mapper dictionary. +STATIC mp_obj_t gpio_map_dict(mp_uint_t n_args, const mp_obj_t *args) { + if (n_args > 1) { + MP_STATE_PORT(gpio_class_map_dict) = args[1]; + return mp_const_none; + } + return MP_STATE_PORT(gpio_class_map_dict); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gpio_map_dict_fun_obj, 1, 2, gpio_map_dict); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(gpio_map_dict_obj, (mp_obj_t)&gpio_map_dict_fun_obj); + +/// \method init(mode, pull=Pin.PULL_NONE, af=-1) +/// Initialise the pin: +/// +/// - `af` can be in range 0-15, please check the CC3200 datasheet +/// for the details on the AFs availables on each pin (af=0, keeps it as a gpio pin). +/// - `mode` can be one of: +/// - `Pin.IN` - configure the pin for input; +/// - `Pin.OUT` - configure the pin for output; +/// - `type` can be one of: +/// - `Pin.STD` - standard without pull-up or pull-down; +/// - `Pin.STD_PU` - standard with pull-up resistor; +/// - `Pin.STD_PD` - standard with pull-down resistor. +/// - `Pin.OD` - standard without pull up or pull down; +/// - `Pin.OD_PU` - open drain with pull-up resistor; +/// - `Pin.OD_PD` - open drain with pull-down resistor. +/// - `Pin.ANALOG` - configured in analog (adc) mode +/// - `strength` can be one of: +/// - `Pin.2MA` - 2ma drive strength; +/// - `Pin.4MA` - 4ma drive strength; +/// - `Pin.6MA` - 6ma drive strength; +/// +/// Returns: `None`. +STATIC const mp_arg_t gpio_init_args[] = { + { MP_QSTR_af, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_mode, MP_ARG_INT, {.u_int = GPIO_DIR_MODE_OUT} }, + { MP_QSTR_type, MP_ARG_INT, {.u_int = PIN_TYPE_STD} }, + { MP_QSTR_str, MP_ARG_INT, {.u_int = PIN_STRENGTH_4MA} }, +}; +#define gpio_INIT_NUM_ARGS MP_ARRAY_SIZE(gpio_init_args) + +STATIC mp_obj_t gpio_obj_init_helper(const gpio_obj_t *self, mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + // parse args + mp_arg_val_t vals[gpio_INIT_NUM_ARGS]; + mp_arg_parse_all(n_args, args, kw_args, gpio_INIT_NUM_ARGS, gpio_init_args, vals); + + // get the af + uint af = vals[0].u_int; + if (af < PIN_MODE_0 || af > PIN_MODE_15) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + } + // get the io mode + uint mode = vals[1].u_int; + if (mode != GPIO_DIR_MODE_IN && mode != GPIO_DIR_MODE_OUT) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + } + // get the type + uint type = vals[2].u_int; + if (type != PIN_TYPE_STD && type != PIN_TYPE_STD_PU && type != PIN_TYPE_STD_PD && + type != PIN_TYPE_OD && type != PIN_TYPE_OD_PU && type != PIN_TYPE_OD_PD) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + } + // get the strenght + uint strength = vals[3].u_int; + if (strength != PIN_STRENGTH_2MA && strength != PIN_STRENGTH_4MA && strength != PIN_STRENGTH_6MA) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + } + + // configure the pin as requested + gpio_config (self, af, mode, type, strength); + + return mp_const_none; +} + +STATIC mp_obj_t gpio_obj_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return gpio_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(gpio_init_obj, 1, gpio_obj_init); + +/// \method value([value]) +/// Get or set the digital logic level of the pin: +/// +/// - With no argument, return 0 or 1 depending on the logic level of the pin. +/// - With `value` given, set the logic level of the pin. `value` can be +/// anything that converts to a boolean. If it converts to `True`, the pin +/// is set high, otherwise it is set low. +STATIC mp_obj_t gpio_value(mp_uint_t n_args, const mp_obj_t *args) { + gpio_obj_t *self = args[0]; + if (n_args == 1) { + // get the pin value + return MP_OBJ_NEW_SMALL_INT(MAP_GPIOPinRead(self->port, self->bit) ? 1 : 0); + } else { + // set the pin value + if (mp_obj_is_true(args[1])) { + MAP_GPIOPinWrite(self->port, self->bit, self->bit); + } else { + MAP_GPIOPinWrite(self->port, self->bit, 0); + } + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gpio_value_obj, 1, 2, gpio_value); + +/// \method low() +/// Set the pin to a low logic level. +STATIC mp_obj_t gpio_low(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + MAP_GPIOPinWrite(self->port, self->bit, 0); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_low_obj, gpio_low); + +/// \method high() +/// Set the pin to a high logic level. +STATIC mp_obj_t gpio_high(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + MAP_GPIOPinWrite(self->port, self->bit, self->bit); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_high_obj, gpio_high); + +/// \method toggle() +/// Toggles the value of the pin +STATIC mp_obj_t gpio_toggle(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + MAP_GPIOPinWrite(self->port, self->bit, ~MAP_GPIOPinRead(self->port, self->bit)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_toggle_obj, gpio_toggle); + +/// \method name() +/// Get the pin name. +STATIC mp_obj_t gpio_name(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + return MP_OBJ_NEW_QSTR(self->name); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_name_obj, gpio_name); + +/// \method name() +/// Returns the cpu name for this pin. +STATIC mp_obj_t gpio_cpu_name(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + mp_obj_t result = mp_obj_new_list(0, NULL); + mp_obj_list_append(result, MP_OBJ_NEW_QSTR(self->name)); + + mp_map_t *map = mp_obj_dict_get_map((mp_obj_t)&gpio_cpu_pins_locals_dict); + mp_map_elem_t *elem = map->table; + + for (mp_uint_t i = 0; i < map->used; i++, elem++) { + if (elem->value == self) { + mp_obj_list_append(result, elem->key); + } + } + return result; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_cpu_name_obj, gpio_cpu_name); + +/// \method port() +/// Get the pin port. +STATIC mp_obj_t gpio_port(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + return mp_obj_new_int(self->port); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_port_obj, gpio_port); + +/// \method pin() +/// Get the pin number. +STATIC mp_obj_t gpio_pin(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(self->pin_num); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_pin_obj, gpio_pin); + +/// \method mode() +/// Returns the currently configured mode of the gpio pin. The integer returned +/// will match one of the allowed constants for the mode argument to the init +/// function. +STATIC mp_obj_t gpio_mode(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(gpio_get_mode(self_in)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_mode_obj, gpio_mode); + +/// \method type() +/// Returns the currently configured type of the pin. The integer returned +/// will match one of the allowed constants for the type argument to the init +/// function. +STATIC mp_obj_t gpio_type_get(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(gpio_get_type(self_in)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_type_obj, gpio_type_get); + +/// \method strength() +/// Returns the currently configured drive strength of the pin. The integer returned +/// will match one of the allowed constants for the strength argument to the init +/// function. +STATIC mp_obj_t gpio_strength(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(gpio_get_strenght(self_in)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_strenght_obj, gpio_strength); + +/// \method af() +/// Returns the currently configured alternate function of the gpio pin. The integer returned +/// will match one of the allowed constants for the af argument to the init function. +STATIC mp_obj_t gpio_af(mp_obj_t self_in) { + gpio_obj_t *self = self_in; + return MP_OBJ_NEW_SMALL_INT(MAP_PinModeGet(self->pin_num)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(gpio_af_obj, gpio_af); + +STATIC const mp_map_elem_t gpio_locals_dict_table[] = { + // instance methods + { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&gpio_init_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_value), (mp_obj_t)&gpio_value_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_low), (mp_obj_t)&gpio_low_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_high), (mp_obj_t)&gpio_high_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_toggle), (mp_obj_t)&gpio_toggle_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_name), (mp_obj_t)&gpio_name_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_names), (mp_obj_t)&gpio_cpu_name_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_port), (mp_obj_t)&gpio_port_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin), (mp_obj_t)&gpio_pin_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_mode), (mp_obj_t)&gpio_mode_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_type), (mp_obj_t)&gpio_type_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_strength), (mp_obj_t)&gpio_strenght_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_af), (mp_obj_t)&gpio_af_obj }, + + // class methods + { MP_OBJ_NEW_QSTR(MP_QSTR_mapper), (mp_obj_t)&gpio_mapper_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_dict), (mp_obj_t)&gpio_map_dict_obj }, + + // class attributes + { MP_OBJ_NEW_QSTR(MP_QSTR_cpu), (mp_obj_t)&gpio_cpu_pins_obj_type }, + + // class constants + /// \constant IN - set the pin to input mode + /// \constant OUT - set the pin to output mode + /// \constant STD - set the pin to standard mode without pull-up or pull-down + /// \constant STD_PU - set the pin to standard mode with pull-up + /// \constant STD_PD - set the pin to standard mode with pull-down + /// \constant OD - set the pin to open drain mode without pull-up or pull-down + /// \constant OD_PU - set the pin to open drain mode with pull-up + /// \constant OD_PD - set the pin to open drain mode with pull-down + /// \constant 2MA - set the drive strength to 2ma + /// \constant 4MA - set the drive strength to 4ma + /// \constant 6MA - set the drive strength to 6ma + { MP_OBJ_NEW_QSTR(MP_QSTR_IN), MP_OBJ_NEW_SMALL_INT(GPIO_DIR_MODE_IN) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OUT), MP_OBJ_NEW_SMALL_INT(GPIO_DIR_MODE_OUT) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_STD), MP_OBJ_NEW_SMALL_INT(PIN_TYPE_STD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_STD_PU), MP_OBJ_NEW_SMALL_INT(PIN_TYPE_STD_PU) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_STD_PD), MP_OBJ_NEW_SMALL_INT(PIN_TYPE_STD_PD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OD), MP_OBJ_NEW_SMALL_INT(PIN_TYPE_OD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OD_PU), MP_OBJ_NEW_SMALL_INT(PIN_TYPE_OD_PU) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_OD_PD), MP_OBJ_NEW_SMALL_INT(PIN_TYPE_OD_PD) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_S2MA), MP_OBJ_NEW_SMALL_INT(PIN_STRENGTH_2MA) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_S4MA), MP_OBJ_NEW_SMALL_INT(PIN_STRENGTH_4MA) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_S6MA), MP_OBJ_NEW_SMALL_INT(PIN_STRENGTH_6MA) }, +}; + +STATIC MP_DEFINE_CONST_DICT(gpio_locals_dict, gpio_locals_dict_table); + +const mp_obj_type_t gpio_type = { + { &mp_type_type }, + .name = MP_QSTR_GPIO, + .print = gpio_print, + .make_new = gpio_make_new, + .locals_dict = (mp_obj_t)&gpio_locals_dict, +}; diff --git a/cc3200/mods/pybgpio.h b/cc3200/mods/pybgpio.h new file mode 100644 index 000000000..08a495803 --- /dev/null +++ b/cc3200/mods/pybgpio.h @@ -0,0 +1,68 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// This file requires pin_defs_xxx.h (which has port specific enums and +// defines, so we include it here. It should never be included directly + +#include MICROPY_PIN_DEFS_PORT_H + +typedef struct { + mp_obj_base_t base; + qstr name; + uint32_t port; + uint32_t bit : 8; + uint32_t pin_num : 7; +} gpio_obj_t; + +extern const mp_obj_type_t gpio_type; + +typedef struct { + const char *name; + const gpio_obj_t *gpio; +} gpio_named_pin_t; + +extern const gpio_named_pin_t gpio_cpu_pins[]; + +typedef struct { + mp_obj_base_t base; + qstr name; + const gpio_named_pin_t *named_pins; +} gpio_named_pins_obj_t; + +extern const mp_obj_type_t gpio_cpu_pins_obj_type; +extern const mp_obj_dict_t gpio_cpu_pins_locals_dict; + +MP_DECLARE_CONST_FUN_OBJ(gpio_init_obj); + +void gpio_init0(void); +void gpio_config(const gpio_obj_t *self, uint af, uint mode, uint type, uint strength); +const gpio_obj_t *gpio_find(mp_obj_t user_obj); +const gpio_obj_t *gpio_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t name); +uint32_t gpio_get_mode(const gpio_obj_t *self); +uint32_t gpio_get_type(const gpio_obj_t *self); +uint32_t gpio_get_strenght(const gpio_obj_t *self); + diff --git a/cc3200/mods/pybrtc.c b/cc3200/mods/pybrtc.c new file mode 100644 index 000000000..8d4383068 --- /dev/null +++ b/cc3200/mods/pybrtc.c @@ -0,0 +1,158 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> + +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" +#include "modutime.h" +#include "inc/hw_types.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "rom_map.h" +#include "prcm.h" +#include "pybrtc.h" + +/// \moduleref pyb +/// \class RTC - real time clock +/// +/// The RTC is and independent clock that keeps track of the date +/// and time. +/// +/// Example usage: +/// +/// rtc = pyb.RTC() +/// rtc.datetime((2014, 5, 1, 4, 13, 0, 0, 0)) +/// print(rtc.datetime()) + +void pybrtc_init(void) { + // if RTC was previously set leave it alone + if (MAP_PRCMSysResetCauseGet() == PRCM_POWER_ON) { + // fresh reset; configure RTC Calendar + // set the date to 1st Jan 2015 + // set the time to 00:00:00 + uint32_t seconds = mod_time_seconds_since_2000(2015, 1, 1, 0, 0, 0); + + MAP_PRCMRTCSet(seconds, 0); + + // Mark that the RTC is in use + MAP_PRCMRTCInUseSet(); + } +} + +/******************************************************************************/ +// Micro Python bindings + +typedef struct _pyb_rtc_obj_t { + mp_obj_base_t base; +} pyb_rtc_obj_t; + +STATIC const pyb_rtc_obj_t pyb_rtc_obj = {{&pyb_rtc_type}}; + +/// \classmethod \constructor() +/// Create an RTC object. +STATIC mp_obj_t pyb_rtc_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // return constant object + return (mp_obj_t)&pyb_rtc_obj; +} + +/// \method datetime([datetimetuple]) +/// Get or set the date and time of the RTC. +/// +/// With no arguments, this method returns an 8-tuple with the current +/// date and time. With 1 argument (being an 8-tuple) it sets the date +/// and time. +/// +/// The 8-tuple has the following format: +/// +/// (year, month, day, weekday, hours, minutes, seconds, milliseconds) +/// +/// `weekday` is 1-7 for Monday through Sunday. +/// +mp_obj_t pyb_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) { + mod_struct_time tm; + uint32_t seconds; + uint16_t mseconds; + + if (n_args == 1) { + // get the seconds and the milliseconds from the RTC + MAP_PRCMRTCGet(&seconds, &mseconds); + mseconds = RTC_CYCLES_U16MS(mseconds); + mod_time_seconds_since_2000_to_struct_time(seconds, &tm); + + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(mseconds) + }; + return mp_obj_new_tuple(8, tuple); + } else { + // set date and time + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 8, &items); + + tm.tm_year = mp_obj_get_int(items[0]); + tm.tm_mon = mp_obj_get_int(items[1]); + tm.tm_mday = mp_obj_get_int(items[2]); + // Skip the weekday + tm.tm_hour = mp_obj_get_int(items[4]); + tm.tm_min = mp_obj_get_int(items[5]); + tm.tm_sec = mp_obj_get_int(items[6]); + mseconds = mp_obj_get_int(items[7]); + + seconds = mod_time_seconds_since_2000(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + mseconds = RTC_U16MS_CYCLES(mseconds); + MAP_PRCMRTCSet(seconds, mseconds); + + return mp_const_none; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_datetime_obj, 1, 2, pyb_rtc_datetime); + +STATIC const mp_map_elem_t pyb_rtc_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_datetime), (mp_obj_t)&pyb_rtc_datetime_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(pyb_rtc_locals_dict, pyb_rtc_locals_dict_table); + +const mp_obj_type_t pyb_rtc_type = { + { &mp_type_type }, + .name = MP_QSTR_RTC, + .make_new = pyb_rtc_make_new, + .locals_dict = (mp_obj_t)&pyb_rtc_locals_dict, +}; diff --git a/cc3200/mods/pybrtc.h b/cc3200/mods/pybrtc.h new file mode 100644 index 000000000..abe38cc38 --- /dev/null +++ b/cc3200/mods/pybrtc.h @@ -0,0 +1,33 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define RTC_U16MS_CYCLES(msec) ((msec * 1024) / 1000) +#define RTC_CYCLES_U16MS(cycles) ((cycles * 1000) / 1024) + +extern const mp_obj_type_t pyb_rtc_type; + +void pybrtc_init(void); diff --git a/cc3200/mods/pybstdio.c b/cc3200/mods/pybstdio.c new file mode 100644 index 000000000..2d6a1c07b --- /dev/null +++ b/cc3200/mods/pybstdio.c @@ -0,0 +1,174 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include "py/mpstate.h" +#include "mpconfig.h" +#include "misc.h" +#include "qstr.h" +#include "misc.h" +#include "obj.h" +#include "stream.h" +#include MICROPY_HAL_H +#include "pybuart.h" +#include "telnet.h" +#include "pybstdio.h" + +// TODO make stdin, stdout and stderr writable objects so they can +// be changed by Python code. This requires some changes, as these +// objects are in a read-only module (py/modsys.c). + +void stdout_tx_str(const char *str) { + stdout_tx_strn(str, strlen(str)); +} + +void stdout_tx_strn(const char *str, mp_uint_t len) { + if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { + uart_tx_strn(MP_STATE_PORT(pyb_stdio_uart), str, len); + } + // and also to telnet + if (telnet_is_active()) { + telnet_tx_strn(str, len); + } +} + +void stdout_tx_strn_cooked(const char *str, mp_uint_t len) { + // send stdout to UART + if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { + uart_tx_strn_cooked(MP_STATE_PORT(pyb_stdio_uart), str, len); + } + // and also to telnet + if (telnet_is_active()) { + telnet_tx_strn_cooked(str, len); + } +} + +int stdin_rx_chr(void) { + for ( ;; ) { + if (telnet_rx_any()) { + return telnet_rx_char(); + } + else if (MP_STATE_PORT(pyb_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) { + return uart_rx_char(MP_STATE_PORT(pyb_stdio_uart)); + } + HAL_Delay(1); + } +} + +/******************************************************************************/ +// Micro Python bindings + +#define STDIO_FD_IN (0) +#define STDIO_FD_OUT (1) +#define STDIO_FD_ERR (2) + +typedef struct _pyb_stdio_obj_t { + mp_obj_base_t base; + int fd; +} pyb_stdio_obj_t; + +void stdio_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + pyb_stdio_obj_t *self = self_in; + print(env, "<io.FileIO %d>", self->fd); +} + +STATIC mp_uint_t stdio_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { + pyb_stdio_obj_t *self = self_in; + if (self->fd == STDIO_FD_IN) { + for (uint i = 0; i < size; i++) { + int c = stdin_rx_chr(); + if (c == '\r') { + c = '\n'; + } + ((byte*)buf)[i] = c; + } + return size; + } else { + *errcode = EPERM; + return MP_STREAM_ERROR; + } +} + +STATIC mp_uint_t stdio_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { + pyb_stdio_obj_t *self = self_in; + if (self->fd == STDIO_FD_OUT || self->fd == STDIO_FD_ERR) { + stdout_tx_strn_cooked(buf, size); + return size; + } else { + *errcode = EPERM; + return MP_STREAM_ERROR; + } +} + +mp_obj_t stdio_obj___exit__(mp_uint_t n_args, const mp_obj_t *args) { + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stdio_obj___exit___obj, 4, 4, stdio_obj___exit__); + +// TODO gc hook to close the file if not already closed + +STATIC const mp_map_elem_t stdio_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&mp_identity_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&mp_identity_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR___enter__), (mp_obj_t)&mp_identity_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR___exit__), (mp_obj_t)&stdio_obj___exit___obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(stdio_locals_dict, stdio_locals_dict_table); + +STATIC const mp_stream_p_t stdio_obj_stream_p = { + .read = stdio_read, + .write = stdio_write, + .is_text = true, +}; + +STATIC const mp_obj_type_t stdio_obj_type = { + { &mp_type_type }, + .name = MP_QSTR_FileIO, + // TODO .make_new? + .print = stdio_obj_print, + .getiter = mp_identity, + .iternext = mp_stream_unbuffered_iter, + .stream_p = &stdio_obj_stream_p, + .locals_dict = (mp_obj_t)&stdio_locals_dict, +}; + +/// \moduleref sys +/// \constant stdin - standard input (connected to UART0, or to telnet, configurable) +/// \constant stdout - standard output (connected to UART0, and optionally to telnet) +/// \constant stderr - standard error (connected to UART0, and optionally to telnet) +const pyb_stdio_obj_t mp_sys_stdin_obj = {{&stdio_obj_type}, .fd = STDIO_FD_IN}; +const pyb_stdio_obj_t mp_sys_stdout_obj = {{&stdio_obj_type}, .fd = STDIO_FD_OUT}; +const pyb_stdio_obj_t mp_sys_stderr_obj = {{&stdio_obj_type}, .fd = STDIO_FD_ERR}; diff --git a/cc3200/mods/pybstdio.h b/cc3200/mods/pybstdio.h new file mode 100644 index 000000000..f987e8bc4 --- /dev/null +++ b/cc3200/mods/pybstdio.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +void stdout_tx_str(const char *str); +void stdout_tx_strn(const char *str, mp_uint_t len); +void stdout_tx_strn_cooked(const char *str, mp_uint_t len); +int stdin_rx_chr(void); diff --git a/cc3200/mods/pybsystick.c b/cc3200/mods/pybsystick.c new file mode 100644 index 000000000..e934ab7dd --- /dev/null +++ b/cc3200/mods/pybsystick.c @@ -0,0 +1,93 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "misc.h" +#include "nlr.h" +#include "qstr.h" +#include "obj.h" +#include "irq.h" +#include "pybsystick.h" +#include "systick.h" +#include "inc/hw_types.h" +#include "inc/hw_nvic.h" + +#ifdef USE_FREERTOS +#include "FreeRTOS.h" +#include "task.h" +#endif + + +bool sys_tick_has_passed(uint32_t start_tick, uint32_t delay_ms) { + return HAL_GetTick() - start_tick >= delay_ms; +} + +// waits until at least delay_ms milliseconds have passed from the sampling of +// startTick. Handles overflow properly. Assumes stc was taken from +// HAL_GetTick() some time before calling this function. +void sys_tick_wait_at_least(uint32_t start_tick, uint32_t delay_ms) { +#ifdef USE_FREERTOS + vTaskDelay (delay_ms / portTICK_PERIOD_MS); +#else + while (!sys_tick_has_passed(start_tick, delay_ms)) { + __WFI(); // enter sleep mode, waiting for interrupt + } +#endif +} + +// The SysTick timer counts down at HAL_FCPU_HZ, so we can use that knowledge +// to grab a microsecond counter. +// +// We assume that HAL_GetTick returns milliseconds. +uint32_t sys_tick_get_microseconds(void) { + mp_uint_t irq_state = disable_irq(); + uint32_t counter = SysTickValueGet(); + uint32_t milliseconds = HAL_GetTick(); + uint32_t status = (HWREG(NVIC_ST_CTRL)); + enable_irq(irq_state); + + // It's still possible for the countflag bit to get set if the counter was + // reloaded between reading VAL and reading CTRL. With interrupts disabled + // it definitely takes less than 50 HCLK cycles between reading VAL and + // reading CTRL, so the test (counter > 50) is to cover the case where VAL + // is +ve and very close to zero, and the COUNTFLAG bit is also set. + if ((status & NVIC_ST_CTRL_COUNT) && counter > 50) { + // This means that the HW reloaded VAL between the time we read VAL and the + // time we read CTRL, which implies that there is an interrupt pending + // to increment the tick counter. + milliseconds++; + } + uint32_t load = (HWREG(NVIC_ST_RELOAD)); + counter = load - counter; // Convert from decrementing to incrementing + + // ((load + 1) / 1000) is the number of counts per microsecond. + // + // counter / ((load + 1) / 1000) scales from the systick clock to microseconds + // and is the same thing as (counter * 1000) / (load + 1) + return milliseconds * 1000 + (counter * 1000) / (load + 1); +} diff --git a/cc3200/mods/pybsystick.h b/cc3200/mods/pybsystick.h new file mode 100644 index 000000000..abc61939b --- /dev/null +++ b/cc3200/mods/pybsystick.h @@ -0,0 +1,30 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +void sys_tick_wait_at_least(uint32_t stc, uint32_t delay_ms); +bool sys_tick_has_passed(uint32_t stc, uint32_t delay_ms); +uint32_t sys_tick_get_microseconds(void); diff --git a/cc3200/mods/pybuart.c b/cc3200/mods/pybuart.c new file mode 100644 index 000000000..8fad7eaa4 --- /dev/null +++ b/cc3200/mods/pybuart.c @@ -0,0 +1,682 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <errno.h> +#include <stdint.h> + +#include "py/mpstate.h" +#include "mpconfig.h" +#include MICROPY_HAL_H +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "runtime.h" +#include "stream.h" +#include "inc/hw_types.h" +#include "inc/hw_gpio.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_uart.h" +#include "rom_map.h" +#include "interrupt.h" +#include "prcm.h" +#include "gpio.h" +#include "uart.h" +#include "pin.h" +#include "pybuart.h" +#include "pybgpio.h" +#include "pybioctl.h" +#include "pybstdio.h" +#include "mpexception.h" +#include "osi.h" + + +/// \moduleref pyb +/// \class UART - duplex serial communication bus +/// +/// UART implements the standard UART/USART duplex serial communications protocol. At +/// the physical level it consists of 2 lines: RX and TX. +/// +/// UART objects can be created and initialised using: +/// +/// from pyb import UART +/// +/// uart = UART(1, 9600) # init with given baudrate +/// uart.init(9600, bits=8, stop=1, parity=None) # init with given parameters +/// +/// Bits can be 5, 6, 7, 8, parity can be None, 0 (even), 1 (odd). Stop can be 1 or 2. +/// +/// A UART object acts like a stream object and reading and writing is done +/// using the standard stream methods: +/// +/// uart.read(10) # read 10 characters, returns a bytes object +/// uart.readall() # read all available characters +/// uart.readline() # read a line +/// uart.readinto(buf) # read and store into the given buffer +/// uart.write('abc') # write the 3 characters +/// +/// Individual characters can be read/written using: +/// +/// uart.readchar() # read 1 character and returns it as an integer +/// uart.writechar(42) # write 1 character +/// +/// To check if there is anything to be read, use: +/// +/// uart.any() # returns True if any characters waiting + + + +/****************************************************************************** + DECLARE PRIVATE FUNCTIONS + ******************************************************************************/ +STATIC void UARTGenericIntHandler(uint32_t uart_id); +STATIC void UART0IntHandler(void); +STATIC void UART1IntHandler(void); +STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in); + +/****************************************************************************** + DEFINE PRIVATE TYPES + ******************************************************************************/ + +struct _pyb_uart_obj_t { + mp_obj_base_t base; + pyb_uart_t uart_id; + uint reg; + uint baudrate; + uint config; + uint flowcontrol; + byte *read_buf; // read buffer pointer + uint16_t timeout; // timeout waiting for first char + uint16_t timeout_char; // timeout waiting between chars + uint16_t read_buf_len; // len in chars; buf can hold len-1 chars + volatile uint16_t read_buf_head; // indexes first empty slot + uint16_t read_buf_tail; // indexes first full slot (not full if equals head) + bool enabled; +}; + +#define PYBUART_TX_WAIT_MS 1 +#define PYBUART_TX_MAX_TIMEOUT_MS 5 + +/****************************************************************************** + DEFINE PUBLIC FUNCTIONS + ******************************************************************************/ +void uart_init0 (void) { + for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_uart_obj_all)); i++) { + MP_STATE_PORT(pyb_uart_obj_all)[i] = NULL; + } +} + +// unregister all interrupt sources +void uart_deinit(void) { + for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_uart_obj_all)); i++) { + pyb_uart_obj_t *self = MP_STATE_PORT(pyb_uart_obj_all)[i]; + if (self != NULL) { + pyb_uart_deinit(self); + } + } +} + +// assumes Init parameters have been set up correctly +bool uart_init2(pyb_uart_obj_t *self) { + uint uartPerh; + + switch (self->uart_id) { + case PYB_UART_1: + self->reg = UARTA0_BASE; + uartPerh = PRCM_UARTA0; + MAP_PinTypeUART(PIN_55, PIN_MODE_3); + MAP_PinTypeUART(PIN_57, PIN_MODE_3); + MAP_UARTIntRegister(UARTA0_BASE, UART0IntHandler); + MAP_IntPrioritySet(INT_UARTA0, INT_PRIORITY_LVL_7); + break; + case PYB_UART_2: + self->reg = UARTA1_BASE; + uartPerh = PRCM_UARTA1; + MAP_PinTypeUART(PIN_58, PIN_MODE_6); + MAP_PinTypeUART(PIN_59, PIN_MODE_6); + MAP_UARTIntRegister(UARTA1_BASE, UART1IntHandler); + MAP_IntPrioritySet(INT_UARTA1, INT_PRIORITY_LVL_7); + break; + default: + return false; + } + + // Enable the peripheral clock + MAP_PRCMPeripheralClkEnable(uartPerh, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); + + // Reset the uart + MAP_PRCMPeripheralReset(uartPerh); + + // Initialize the UART + MAP_UARTConfigSetExpClk(self->reg, MAP_PRCMPeripheralClockGet(uartPerh), + self->baudrate, self->config); + + // Enbale the FIFO + MAP_UARTFIFOEnable(self->reg); + + // Configure the FIFO interrupt levels + MAP_UARTFIFOLevelSet(self->reg, UART_FIFO_TX4_8, UART_FIFO_RX4_8); + + // Configure the flow control mode + UARTFlowControlSet(self->reg, self->flowcontrol); + + // Enable the RX and RX timeout interrupts + MAP_UARTIntEnable(self->reg, UART_INT_RX | UART_INT_RT); + + self->enabled = true; + + return true; +} + +bool uart_init(pyb_uart_obj_t *self, uint baudrate) { + self->baudrate = baudrate; + self->config = UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE; + self->flowcontrol = UART_FLOWCONTROL_NONE; + return uart_init2(self); +} + +bool uart_rx_any(pyb_uart_obj_t *self) { + return (self->read_buf_tail != self->read_buf_head || MAP_UARTCharsAvail(self->reg)); +} + +// Waits at most timeout milliseconds for at least 1 char to become ready for +// reading (from buf or for direct reading). +// Returns true if something available, false if not. +STATIC bool uart_rx_wait(pyb_uart_obj_t *self, uint32_t timeout) { + for (;;) { + if (uart_rx_any(self)) { + return true; // have at least 1 char ready for reading + } + if (timeout > 0) { + HAL_Delay (1); + timeout--; + } + else { + return false; + } + } +} + +int uart_rx_char(pyb_uart_obj_t *self) { + if (self->read_buf_tail != self->read_buf_head) { + // buffering via IRQ + int data = self->read_buf[self->read_buf_tail]; + self->read_buf_tail = (self->read_buf_tail + 1) % self->read_buf_len; + return data; + } else { + // no buffering + return MAP_UARTCharGetNonBlocking(self->reg); + } +} + +bool uart_tx_char(pyb_uart_obj_t *self, int c) { + uint32_t timeout = 0; + + while (!MAP_UARTCharPutNonBlocking(self->reg, c)) { + if (timeout++ > (PYBUART_TX_MAX_TIMEOUT_MS / PYBUART_TX_WAIT_MS)) { + return false; + } + HAL_Delay (PYBUART_TX_WAIT_MS); + } + return true; +} + +bool uart_tx_strn(pyb_uart_obj_t *self, const char *str, uint len) { + for (const char *top = str + len; str < top; str++) { + if (!uart_tx_char(self, *str)) { + return false; + } + } + return true; +} + +void uart_tx_strn_cooked(pyb_uart_obj_t *self, const char *str, uint len) { + for (const char *top = str + len; str < top; str++) { + if (*str == '\n') { + uart_tx_char(self, '\r'); + } + uart_tx_char(self, *str); + } +} + +/****************************************************************************** + DEFINE PRIVATE FUNCTIONS + ******************************************************************************/ + +STATIC void UARTGenericIntHandler(uint32_t uart_id) { + pyb_uart_obj_t *self = MP_STATE_PORT(pyb_uart_obj_all)[uart_id]; + uint32_t status; + + if (self == NULL) { + // UART object has not been set, so we can't do anything, not + // even disable the IRQ. This should never happen. + return; + } + + status = MAP_UARTIntStatus(self->reg, true); + + // Receive interrupt + if (status & (UART_INT_RX | UART_INT_RT)) { + MAP_UARTIntClear(self->reg, UART_INT_RX | UART_INT_RT); + while (UARTCharsAvail(self->reg)) { + int data = MAP_UARTCharGetNonBlocking(self->reg); + if (MICROPY_STDIO_UART == self->uart_id && data == user_interrupt_char) { + // raise exception when interrupts are finished + mpexception_keyboard_nlr_jump(); + } + else if (self->read_buf_len != 0) { + uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len; + if (next_head != self->read_buf_tail) { + // only store data if room in buf + self->read_buf[self->read_buf_head] = data; + self->read_buf_head = next_head; + } + } + } + } +} + +STATIC void UART0IntHandler(void) { + UARTGenericIntHandler(0); +} + +STATIC void UART1IntHandler(void) { + UARTGenericIntHandler(1); +} + +/******************************************************************************/ +/* Micro Python bindings */ + +STATIC void pyb_uart_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { + pyb_uart_obj_t *self = self_in; + if (!self->enabled) { + print(env, "UART(%u)", self->uart_id); + } else { + print(env, "UART(%u, baudrate=%u, bits=", self->uart_id, self->baudrate); + switch (self->config & UART_CONFIG_WLEN_MASK) { + case UART_CONFIG_WLEN_5: + print(env, "5"); + break; + case UART_CONFIG_WLEN_6: + print(env, "6"); + break; + case UART_CONFIG_WLEN_7: + print(env, "7"); + break; + case UART_CONFIG_WLEN_8: + print(env, "8"); + break; + default: + break; + } + if ((self->config & UART_CONFIG_PAR_MASK) == UART_CONFIG_PAR_NONE) { + print(env, ", parity=None"); + } else { + print(env, ", parity=%u", (self->config & UART_CONFIG_PAR_MASK) == UART_CONFIG_PAR_EVEN ? 0 : 1); + } + print(env, ", stop=%u, timeout=%u, timeout_char=%u, read_buf_len=%u)", + (self->config & UART_CONFIG_STOP_MASK) == UART_CONFIG_STOP_ONE ? 1 : 2, + self->timeout, self->timeout_char, self->read_buf_len); + } +} + +/// \method init(baudrate, bits=8, parity=None, stop=1, *, timeout=1000, timeout_char=0, read_buf_len=64) +/// +/// Initialise the UART bus with the given parameters: +/// +/// - `baudrate` is the clock rate. +/// - `bits` is the number of bits per byte, 7, 8 or 9. +/// - `parity` is the parity, `None`, 0 (even) or 1 (odd). +/// - `stop` is the number of stop bits, 1 or 2. +/// - `flowcontrol` is the flow control mode, `None`, `UART.FLOW_TX`, +/// `UART.FLOW_RX', 'UART.FLOW_TXRX`. +/// - `timeout` is the timeout in milliseconds to wait for the first character. +/// - `timeout_char` is the timeout in milliseconds to wait between characters. +/// - `read_buf_len` is the character length of the read buffer (0 to disable). +STATIC const mp_arg_t pyb_uart_init_args[] = { + { MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_parity, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_stop, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = UART_FLOWCONTROL_NONE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_read_buf_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, +}; + +STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + bool success; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(pyb_uart_init_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(pyb_uart_init_args), pyb_uart_init_args, args); + + // set the UART configuration values + if (n_args > 1) { + self->baudrate = args[0].u_int; + switch (args[1].u_int) { + case 5: + self->config = UART_CONFIG_WLEN_5; + break; + case 6: + self->config = UART_CONFIG_WLEN_6; + break; + case 7: + self->config = UART_CONFIG_WLEN_7; + break; + case 8: + self->config = UART_CONFIG_WLEN_8; + break; + default: + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + break; + } + // Parity + if (args[2].u_obj == mp_const_none) { + self->config |= UART_CONFIG_PAR_NONE; + } else { + self->config |= ((mp_obj_get_int(args[2].u_obj) & 1) ? UART_CONFIG_PAR_ODD : UART_CONFIG_PAR_EVEN); + } + // Stop bits + self->config |= (args[3].u_int == 1 ? UART_CONFIG_STOP_ONE : UART_CONFIG_STOP_TWO); + + // Flow control + self->flowcontrol = args[4].u_int; + success = uart_init2(self); + } else { + success = uart_init(self, args[0].u_int); + } + + // init UART (if it fails, something weird happened) + if (!success) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); + } + + // set timeouts + self->timeout = args[5].u_int; + self->timeout_char = args[6].u_int; + + // setup the read buffer + m_del(byte, self->read_buf, self->read_buf_len); + self->read_buf_head = 0; + self->read_buf_tail = 0; + + if (args[7].u_int <= 0) { + // no read buffer + self->read_buf_len = 0; + self->read_buf = NULL; + MAP_UARTIntDisable(self->reg, UART_INT_RX | UART_INT_RT); + } else { + // read buffer using interrupts + self->read_buf_len = args[7].u_int; + self->read_buf = m_new(byte, args[7].u_int); + } + + return mp_const_none; +} + +/// \classmethod \constructor(bus, ...) +/// +/// Construct a UART object on the given bus. `bus` can be 1-2 +/// With no additional parameters, the UART object is created but not +/// initialised (it has the settings from the last initialisation of +/// the bus, if any). +/// When only the baud rate is given the UART object is created and +/// initialized with the default configuration of: 8 bit transfers, +/// 1 stop bit, no parity and flow control disabled. +/// See `init` for parameters of initialisation. +/// If extra arguments are given, the bus is initialised with these arguments +/// See `init` for parameters of initialisation. +/// +STATIC mp_obj_t pyb_uart_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 1, MP_ARRAY_SIZE(pyb_uart_init_args), true); + + // work out port + pyb_uart_t uart_id = mp_obj_get_int(args[0]); + + if (uart_id < PYB_UART_1 || uart_id > MP_ARRAY_SIZE(MP_STATE_PORT(pyb_uart_obj_all))) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_resource_not_avaliable)); + } + + // create object + pyb_uart_obj_t *self; + if (MP_STATE_PORT(pyb_uart_obj_all)[uart_id - 1] == NULL) { + // create new UART object + self = m_new0(pyb_uart_obj_t, 1); + self->base.type = &pyb_uart_type; + self->uart_id = uart_id; + self->enabled = false; + MP_STATE_PORT(pyb_uart_obj_all)[uart_id - 1] = self; + } else { + // reference existing UART object + self = MP_STATE_PORT(pyb_uart_obj_all)[uart_id - 1]; + } + + if (n_args > 1 || n_kw > 0) { + // start the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pyb_uart_init_helper(self, n_args - 1, args + 1, &kw_args); + } else if (!self->enabled) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, mpexception_num_type_invalid_arguments)); + } + + return self; +} + +STATIC mp_obj_t pyb_uart_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return pyb_uart_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_init_obj, 1, pyb_uart_init); + +/// \method deinit() +/// Turn off the UART bus. +STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) { + pyb_uart_obj_t *self = self_in; + uint uartPerh; + + switch (self->uart_id) { + + case PYB_UART_1: + uartPerh = PRCM_UARTA0; + break; + + case PYB_UART_2: + uartPerh = PRCM_UARTA1; + break; + + default: + return mp_const_none; + } + + self->enabled = false; + MAP_UARTIntDisable(self->reg, UART_INT_RX | UART_INT_RT); + MAP_UARTIntClear(self->reg, UART_INT_RX | UART_INT_RT); + MAP_UARTIntUnregister(self->reg); + MAP_UARTDisable(self->reg); + MAP_PRCMPeripheralClkDisable(uartPerh, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_deinit_obj, pyb_uart_deinit); + +/// \method any() +/// Return `True` if any characters waiting, else `False`. +STATIC mp_obj_t pyb_uart_any(mp_obj_t self_in) { + pyb_uart_obj_t *self = self_in; + if (uart_rx_any(self)) { + return mp_const_true; + } else { + return mp_const_false; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_any_obj, pyb_uart_any); + +/// \method writechar(char) +/// Write a single character on the bus. `char` is an integer to write. +/// Return value: `None`. +STATIC mp_obj_t pyb_uart_writechar(mp_obj_t self_in, mp_obj_t char_in) { + pyb_uart_obj_t *self = self_in; + + // get the character to write + uint8_t data = mp_obj_get_int(char_in); + + // send the character + if (!uart_tx_char(self, data)) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ETIMEDOUT))); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_uart_writechar_obj, pyb_uart_writechar); + +/// \method readchar() +/// Receive a single character on the bus. +/// Return value: The character read, as an integer. Returns -1 on timeout. +STATIC mp_obj_t pyb_uart_readchar(mp_obj_t self_in) { + pyb_uart_obj_t *self = self_in; + if (uart_rx_wait(self, self->timeout)) { + return mp_obj_new_int(uart_rx_char(self)); + } else { + // return -1 on timeout + return MP_OBJ_NEW_SMALL_INT(-1); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_readchar_obj, pyb_uart_readchar); + +STATIC const mp_map_elem_t pyb_uart_locals_dict_table[] = { + // instance methods + + { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_uart_init_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_uart_deinit_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&pyb_uart_any_obj }, + + /// \method read([nbytes]) + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, + /// \method readall() + { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, + /// \method readline() + { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, + /// \method readinto(buf[, nbytes]) + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, + /// \method write(buf) + { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_writechar), (mp_obj_t)&pyb_uart_writechar_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readchar), (mp_obj_t)&pyb_uart_readchar_obj }, + + // class constants + { MP_OBJ_NEW_QSTR(MP_QSTR_UART1), MP_OBJ_NEW_SMALL_INT(PYB_UART_1) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_UART2), MP_OBJ_NEW_SMALL_INT(PYB_UART_2) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_FLOW_NONE), MP_OBJ_NEW_SMALL_INT(UART_FLOWCONTROL_NONE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_FLOW_TX), MP_OBJ_NEW_SMALL_INT(UART_FLOWCONTROL_TX) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_FLOW_RX), MP_OBJ_NEW_SMALL_INT(UART_FLOWCONTROL_RX) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_FLOW_TXRX), MP_OBJ_NEW_SMALL_INT(UART_FLOWCONTROL_TX | UART_FLOWCONTROL_RX) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pyb_uart_locals_dict, pyb_uart_locals_dict_table); + +STATIC mp_uint_t pyb_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + pyb_uart_obj_t *self = self_in; + byte *buf = buf_in; + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + // wait for first char to become available + if (!uart_rx_wait(self, self->timeout)) { + // we can either return 0 to indicate EOF (then read() method returns b'') + // or return EAGAIN error to indicate non-blocking (then read() method returns None) + return 0; + } + + // read the data + byte *orig_buf = buf; + for (;;) { + *buf++ = uart_rx_char(self); + if (--size == 0 || !uart_rx_wait(self, self->timeout_char)) { + // return number of bytes read + return buf - orig_buf; + } + } +} + +STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + pyb_uart_obj_t *self = self_in; + const char *buf = buf_in; + + // write the data + if (!uart_tx_strn(self, buf, size)) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ETIMEDOUT))); + } + return size; +} + +STATIC mp_uint_t pyb_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + pyb_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_IOCTL_POLL) { + mp_uint_t flags = arg; + ret = 0; + if ((flags & MP_IOCTL_POLL_RD) && uart_rx_any(self)) { + ret |= MP_IOCTL_POLL_RD; + } + if ((flags & MP_IOCTL_POLL_WR) && MAP_UARTSpaceAvail(self->reg)) { + ret |= MP_IOCTL_POLL_WR; + } + } else { + *errcode = EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_stream_p_t uart_stream_p = { + .read = pyb_uart_read, + .write = pyb_uart_write, + .ioctl = pyb_uart_ioctl, + .is_text = false, +}; + +const mp_obj_type_t pyb_uart_type = { + { &mp_type_type }, + .name = MP_QSTR_UART, + .print = pyb_uart_print, + .make_new = pyb_uart_make_new, + .getiter = mp_identity, + .iternext = mp_stream_unbuffered_iter, + .stream_p = &uart_stream_p, + .locals_dict = (mp_obj_t)&pyb_uart_locals_dict, +}; diff --git a/cc3200/mods/pybuart.h b/cc3200/mods/pybuart.h new file mode 100644 index 000000000..74298a83f --- /dev/null +++ b/cc3200/mods/pybuart.h @@ -0,0 +1,45 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +typedef enum { + PYB_UART_NONE = 0, + PYB_UART_1 = 1, + PYB_UART_2 = 2 +} pyb_uart_t; + +typedef struct _pyb_uart_obj_t pyb_uart_obj_t; +extern const mp_obj_type_t pyb_uart_type; + +void uart_init0(void); +bool uart_init(pyb_uart_obj_t *uart_obj, uint baudrate); +void uart_deinit (void); +bool uart_rx_any(pyb_uart_obj_t *uart_obj); +int uart_rx_char(pyb_uart_obj_t *uart_obj); +bool uart_tx_char(pyb_uart_obj_t *self, int c); +bool uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len); +void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len); + |
