mirror of
https://github.com/micropython/micropython.git
synced 2025-12-24 22:00:12 +01:00
Compare commits
403 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b0a150bd6 | ||
|
|
f8fb4470a0 | ||
|
|
7063210014 | ||
|
|
1cc81ed449 | ||
|
|
c734de490a | ||
|
|
358e5d8bad | ||
|
|
657aef66ff | ||
|
|
6e87aeb841 | ||
|
|
b69f798c92 | ||
|
|
558fd5d228 | ||
|
|
6c3db26ab7 | ||
|
|
d6236e85c2 | ||
|
|
6837dba6b8 | ||
|
|
0c97e4c414 | ||
|
|
a45e280c58 | ||
|
|
f38e8f5217 | ||
|
|
5b1c221785 | ||
|
|
397b705647 | ||
|
|
7e9182f3aa | ||
|
|
b32b0d38fe | ||
|
|
20d9bc2d76 | ||
|
|
05ba2433f6 | ||
|
|
5e919b7ef8 | ||
|
|
9c04299da1 | ||
|
|
5e7fa7c80c | ||
|
|
8a8e775035 | ||
|
|
04d5e644fc | ||
|
|
2c915e1ae6 | ||
|
|
ce8b4e8749 | ||
|
|
2c407bcf20 | ||
|
|
d85439fd19 | ||
|
|
1a0a323ca8 | ||
|
|
4e51a3038c | ||
|
|
96eca22322 | ||
|
|
e6a4d4e23c | ||
|
|
e4cb7c6158 | ||
|
|
2c8356c482 | ||
|
|
f81ea6307c | ||
|
|
de12502d89 | ||
|
|
81fd5685fc | ||
|
|
1b811b946e | ||
|
|
069654f2be | ||
|
|
cee888255b | ||
|
|
28d4b94dce | ||
|
|
69b702276b | ||
|
|
e24674d44e | ||
|
|
69256ac0b1 | ||
|
|
debbaac1bd | ||
|
|
919b70b7ec | ||
|
|
8d2bcaf3cd | ||
|
|
9698a60591 | ||
|
|
fcd6862597 | ||
|
|
ef0c5db2ed | ||
|
|
254a5646c1 | ||
|
|
333a63efaa | ||
|
|
4f2d59e82f | ||
|
|
ec5f8db49d | ||
|
|
c07a03a36d | ||
|
|
aa3fb7b387 | ||
|
|
fd2b71f972 | ||
|
|
a5d07c3aba | ||
|
|
a5d2af7949 | ||
|
|
e5fa163a4c | ||
|
|
a0cb4eda9a | ||
|
|
cef073877b | ||
|
|
cf4b72bf13 | ||
|
|
b475327ffa | ||
|
|
6a051a8e0b | ||
|
|
fb6cc96951 | ||
|
|
fc4c43a72e | ||
|
|
785cf9a61f | ||
|
|
777232c9a5 | ||
|
|
3d4a535208 | ||
|
|
5531437941 | ||
|
|
4b597a1c1a | ||
|
|
98af891610 | ||
|
|
61fa7c8152 | ||
|
|
2e75a17bab | ||
|
|
b1dfdaf6cb | ||
|
|
f50d9477c1 | ||
|
|
d7019d0628 | ||
|
|
9475cc59e6 | ||
|
|
2599672384 | ||
|
|
e1e7657277 | ||
|
|
03b8bb7ec9 | ||
|
|
d88250c06e | ||
|
|
c4506ed869 | ||
|
|
402a743821 | ||
|
|
8fc5e56a6a | ||
|
|
c961889e34 | ||
|
|
6ca17c1922 | ||
|
|
b4070ee8a4 | ||
|
|
2f02302e22 | ||
|
|
193795398d | ||
|
|
71d40f132d | ||
|
|
9edd736ee6 | ||
|
|
e8e116e7fc | ||
|
|
8b08a0d9ed | ||
|
|
61230e007d | ||
|
|
b01a373adb | ||
|
|
cd6194aefc | ||
|
|
374654f2b8 | ||
|
|
fe9bc0c573 | ||
|
|
bbc65d4eda | ||
|
|
fd86bf5917 | ||
|
|
53302f1616 | ||
|
|
651a188299 | ||
|
|
13394a632d | ||
|
|
53ad5edc01 | ||
|
|
87c783b454 | ||
|
|
679fe0abae | ||
|
|
eda8746324 | ||
|
|
935e021250 | ||
|
|
c33a76059f | ||
|
|
3962766be0 | ||
|
|
b62beadae0 | ||
|
|
f71c0699a5 | ||
|
|
9c7e3353e5 | ||
|
|
ba640bde55 | ||
|
|
632d8efa05 | ||
|
|
82b95f625e | ||
|
|
91031a75a1 | ||
|
|
4332d72fd8 | ||
|
|
de0c84ebf1 | ||
|
|
5e75f335e6 | ||
|
|
0cb10b5220 | ||
|
|
159f1aaca8 | ||
|
|
e589cddcd4 | ||
|
|
6e6488530e | ||
|
|
4b5606bc09 | ||
|
|
ee6fcc6f91 | ||
|
|
96a644076d | ||
|
|
8f1854ad2d | ||
|
|
ac671546d1 | ||
|
|
88f60de914 | ||
|
|
2c81b9be28 | ||
|
|
063e6e7d0a | ||
|
|
5d93dfbc2c | ||
|
|
3dbd2ee926 | ||
|
|
4a02a8f74d | ||
|
|
24342dd65e | ||
|
|
d4c8e626f2 | ||
|
|
a5d48b1162 | ||
|
|
78d0dde562 | ||
|
|
0cdbd356fd | ||
|
|
5b9f361824 | ||
|
|
667d64b430 | ||
|
|
dd32f02cc3 | ||
|
|
7059c8c23c | ||
|
|
a1d072df81 | ||
|
|
df1f6783f2 | ||
|
|
494aea3e86 | ||
|
|
5bc9398d9d | ||
|
|
8000d51b68 | ||
|
|
1a0adf49df | ||
|
|
e673714cfe | ||
|
|
58e2ad42ae | ||
|
|
b894551772 | ||
|
|
da161fd9f0 | ||
|
|
12154b1774 | ||
|
|
8d4d6731f5 | ||
|
|
2b302dad51 | ||
|
|
6d5a549067 | ||
|
|
b8133c4c0f | ||
|
|
4a6cac4643 | ||
|
|
99146ea444 | ||
|
|
556a1df6fd | ||
|
|
9d7b871f58 | ||
|
|
2466cb67f8 | ||
|
|
3acaa28b52 | ||
|
|
52e062ef33 | ||
|
|
664f03f466 | ||
|
|
9a58316de2 | ||
|
|
be989be861 | ||
|
|
1ded19d4b3 | ||
|
|
157056ecdf | ||
|
|
ab69ed7dac | ||
|
|
9996adc37d | ||
|
|
cea6cf8a5e | ||
|
|
0d1f8868b6 | ||
|
|
77f85db41e | ||
|
|
06b398489e | ||
|
|
0be6359f39 | ||
|
|
2a1cca20b1 | ||
|
|
e7cd1699df | ||
|
|
dddb98db8b | ||
|
|
99fc0d120a | ||
|
|
00f921ce02 | ||
|
|
031fadd10e | ||
|
|
e5c39a3a9e | ||
|
|
0779409d0d | ||
|
|
ba8f7d5171 | ||
|
|
09ed5bcbbb | ||
|
|
21b74604f9 | ||
|
|
85d3b6165a | ||
|
|
70f32f0f73 | ||
|
|
3d0e3a3d3e | ||
|
|
932f07ccf5 | ||
|
|
4c2cb7e384 | ||
|
|
6e5c31c947 | ||
|
|
a099bfe89c | ||
|
|
e5b047369b | ||
|
|
54fc247f9b | ||
|
|
97375f4576 | ||
|
|
7261f17b9e | ||
|
|
97c2628900 | ||
|
|
9ae51257bd | ||
|
|
5239a8a82b | ||
|
|
a49c16069c | ||
|
|
d5a12a6608 | ||
|
|
1c43a0fbf8 | ||
|
|
7378c50b2f | ||
|
|
9e8396accb | ||
|
|
9b5e05a7c7 | ||
|
|
ad725a6661 | ||
|
|
853fb08d0d | ||
|
|
07554486ee | ||
|
|
6b80ebe32e | ||
|
|
42ef5a1567 | ||
|
|
5b74bba3a3 | ||
|
|
4b6077b3fe | ||
|
|
43d497592f | ||
|
|
fda874e406 | ||
|
|
7379be3673 | ||
|
|
4f64f6bfd3 | ||
|
|
6d2e9e70b3 | ||
|
|
6185dc5f3d | ||
|
|
f1919b7c98 | ||
|
|
c7fb87caff | ||
|
|
f7be80398e | ||
|
|
809fbeefb7 | ||
|
|
dcdf8f2d14 | ||
|
|
bb293e6bcf | ||
|
|
2c72ae5c29 | ||
|
|
f0e2d13fd2 | ||
|
|
ed593780bf | ||
|
|
a0cd118b14 | ||
|
|
f8d42da104 | ||
|
|
3fbbbecec9 | ||
|
|
04a9ac7f38 | ||
|
|
d684f872bd | ||
|
|
05dda0ee9e | ||
|
|
88b0490945 | ||
|
|
4f0080346b | ||
|
|
484a471f9b | ||
|
|
367c084c4b | ||
|
|
0edfb7a115 | ||
|
|
d964873e56 | ||
|
|
ce3beb1672 | ||
|
|
b86c20676e | ||
|
|
db984b73f3 | ||
|
|
1d5d4f49d9 | ||
|
|
419bb26ddc | ||
|
|
d973c1bc12 | ||
|
|
50e0a7b9d4 | ||
|
|
fbb3c190f9 | ||
|
|
14848ffa12 | ||
|
|
52d7685d9a | ||
|
|
d9d4a72679 | ||
|
|
d3a4d39687 | ||
|
|
077448328a | ||
|
|
26f0616e8f | ||
|
|
eb247eacd8 | ||
|
|
342d903a13 | ||
|
|
cdad2b6f4d | ||
|
|
8ab16b6af0 | ||
|
|
c70637bc00 | ||
|
|
98b727c931 | ||
|
|
f22a4f8e0a | ||
|
|
f39bcb304b | ||
|
|
7193086c03 | ||
|
|
4284b3811f | ||
|
|
5d7c408ba8 | ||
|
|
6abafca1aa | ||
|
|
a4c8ef9d16 | ||
|
|
57884996b9 | ||
|
|
b41a14a4b9 | ||
|
|
f70873db23 | ||
|
|
7480ee5892 | ||
|
|
70fb9ee99b | ||
|
|
2382d30318 | ||
|
|
259f1344ca | ||
|
|
ff69a1d27d | ||
|
|
65405247a0 | ||
|
|
3aa0f2eed3 | ||
|
|
453a2a3d7c | ||
|
|
701c4152c1 | ||
|
|
02ea74d8f5 | ||
|
|
1febaf3ac3 | ||
|
|
d083d7d610 | ||
|
|
6f4357c28e | ||
|
|
6d0629bddc | ||
|
|
ecd1272d16 | ||
|
|
3d1d92acfc | ||
|
|
6bb9d3ea3e | ||
|
|
dc320164d8 | ||
|
|
4f72aa86bf | ||
|
|
2740dd85f2 | ||
|
|
e0821830b0 | ||
|
|
09e363316f | ||
|
|
9fb36af9af | ||
|
|
08fed6992f | ||
|
|
6ef65e70af | ||
|
|
eaa96a7610 | ||
|
|
bbe832a0b2 | ||
|
|
6f469209e9 | ||
|
|
57425b648f | ||
|
|
19749db7bf | ||
|
|
9c081b740b | ||
|
|
cd6d189f48 | ||
|
|
8a18084571 | ||
|
|
ac23662550 | ||
|
|
476c15290d | ||
|
|
56f76b873a | ||
|
|
ea23520403 | ||
|
|
57b96a7be2 | ||
|
|
8e1fdf2eb3 | ||
|
|
28adab36c7 | ||
|
|
e9d1a94bf0 | ||
|
|
d6c558c0aa | ||
|
|
add930c4b5 | ||
|
|
12547ce737 | ||
|
|
cc7a4d7db2 | ||
|
|
5148860332 | ||
|
|
495e7cfebc | ||
|
|
f5248a087a | ||
|
|
0d210a0be8 | ||
|
|
fe9620a2bd | ||
|
|
73c9f85b4c | ||
|
|
562bcffd3a | ||
|
|
ed8db2e371 | ||
|
|
53fec1ef48 | ||
|
|
40d8430ee3 | ||
|
|
69d9e7d27d | ||
|
|
dfc35afba1 | ||
|
|
6cee869feb | ||
|
|
ec37239e53 | ||
|
|
4b2938a4b0 | ||
|
|
9598f36a84 | ||
|
|
94e4bd456f | ||
|
|
46fc7a3d75 | ||
|
|
4cd45f48b1 | ||
|
|
46a0ac02c5 | ||
|
|
6b0c88256b | ||
|
|
8cb78e0e53 | ||
|
|
72085a669b | ||
|
|
9fdac9144d | ||
|
|
9d0525182d | ||
|
|
9e0478a902 | ||
|
|
0ee1d0f407 | ||
|
|
e3c66a5a67 | ||
|
|
baf47c84c4 | ||
|
|
1bb15ca427 | ||
|
|
8a43a41b3a | ||
|
|
e9be6a378c | ||
|
|
dc3eb55e6a | ||
|
|
e5cff5b223 | ||
|
|
c38809e26b | ||
|
|
609a9c6b71 | ||
|
|
bf904b238d | ||
|
|
d4315a6caf | ||
|
|
2850e7cd97 | ||
|
|
dc587a3623 | ||
|
|
a2e39a756c | ||
|
|
32b7e93535 | ||
|
|
f791e14750 | ||
|
|
38ac23c942 | ||
|
|
7bb501ef9f | ||
|
|
e372e83b30 | ||
|
|
b33a770596 | ||
|
|
34023eb673 | ||
|
|
0e0ce47e65 | ||
|
|
3770cd2e70 | ||
|
|
f7e5e677df | ||
|
|
3a042fb921 | ||
|
|
a888d5ab91 | ||
|
|
13a4c120ce | ||
|
|
c33ad60a67 | ||
|
|
3846fd56c1 | ||
|
|
5b85a86ce3 | ||
|
|
a2e5e4c3d8 | ||
|
|
3e02b1d19a | ||
|
|
9e78ab4b86 | ||
|
|
814b1ae3a9 | ||
|
|
81407729a5 | ||
|
|
96688de601 | ||
|
|
ab0e36b3da | ||
|
|
350ab0f570 | ||
|
|
103fbaaf27 | ||
|
|
5bf6eba845 | ||
|
|
a63a4761cd | ||
|
|
71206f02c3 | ||
|
|
97a0846af9 | ||
|
|
ff1a96ce2c | ||
|
|
2e2e15cec2 | ||
|
|
5f3e005b67 | ||
|
|
086d98cbde | ||
|
|
87e07ea943 | ||
|
|
e3a29de1dc | ||
|
|
2bd758fe96 | ||
|
|
67e8108345 | ||
|
|
93bb7dffd2 | ||
|
|
9e677114e4 |
@@ -11,7 +11,7 @@ before_script:
|
||||
# - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo add-apt-repository -y ppa:terry.guo/gcc-arm-embedded
|
||||
- sudo dpkg --add-architecture i386
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get update -qq || true
|
||||
- sudo apt-get install -y python3 gcc-multilib gcc-arm-none-eabi pkg-config libffi-dev libffi-dev:i386 qemu-system mingw32
|
||||
# For teensy build
|
||||
- sudo apt-get install realpath
|
||||
|
||||
13
README.md
13
README.md
@@ -16,6 +16,7 @@ The MicroPython project
|
||||
|
||||
This is the MicroPython project, which aims to put an implementation
|
||||
of Python 3.x on microcontrollers and small embedded systems.
|
||||
You can find the official website at [micropython.org](http://www.micropython.org).
|
||||
|
||||
WARNING: this project is in beta stage and is subject to changes of the
|
||||
code-base, including project-wide name changes and API changes.
|
||||
@@ -129,7 +130,7 @@ The STM version
|
||||
|
||||
The "stmhal" port requires an ARM compiler, arm-none-eabi-gcc, and associated
|
||||
bin-utils. For those using Arch Linux, you need arm-none-eabi-binutils and
|
||||
arm-none-eabi-gcc packages from the AUR. Otherwise, try here:
|
||||
arm-none-eabi-gcc packages. Otherwise, try here:
|
||||
https://launchpad.net/gcc-arm-embedded
|
||||
|
||||
To build:
|
||||
@@ -145,9 +146,7 @@ Then to flash the code via USB DFU to your device:
|
||||
|
||||
$ make deploy
|
||||
|
||||
You will need the dfu-util program, on Arch Linux it's dfu-util-git in the
|
||||
AUR. If the above does not work it may be because you don't have the
|
||||
correct permissions. Try then:
|
||||
|
||||
$ sudo dfu-util -a 0 -d 0483:df11 -D build-PYBV10/firmware.dfu
|
||||
|
||||
This will use the included `tools/pydfu.py` script. If flashing the firmware
|
||||
does not work it may be because you don't have the correct permissions, and
|
||||
need to use `sudo make deploy`.
|
||||
See the README.md file in the stmhal/ directory for further details.
|
||||
|
||||
@@ -17,7 +17,7 @@ include ../py/mkenv.mk
|
||||
CROSS_COMPILE ?= arm-none-eabi-
|
||||
|
||||
CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -march=armv7e-m -mabi=aapcs -mcpu=cortex-m4 -msoft-float -mfloat-abi=soft -fsingle-precision-constant -Wdouble-promotion
|
||||
CFLAGS = -Wall -Wpointer-arith -Werror -ansi -std=gnu99 -nostdlib $(CFLAGS_CORTEX_M4)
|
||||
CFLAGS = -Wall -Wpointer-arith -Werror -ansi -std=gnu99 -nostdlib $(CFLAGS_CORTEX_M4) -Os
|
||||
CFLAGS += -g -ffunction-sections -fdata-sections -fno-common -fsigned-char -mno-unaligned-access
|
||||
CFLAGS += -Iboards/$(BOARD)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
__stack_size__ = 3K; /* interrupts are handled within this stack */
|
||||
__stack_size__ = 2K; /* interrupts are handled within this stack */
|
||||
__min_heap_size__ = 8K;
|
||||
|
||||
MEMORY
|
||||
|
||||
@@ -157,7 +157,7 @@ APP_LIB_SRC_C = $(addprefix lib/,\
|
||||
|
||||
APP_STM_SRC_C = $(addprefix stmhal/,\
|
||||
bufhelper.c \
|
||||
file.c \
|
||||
builtin_open.c \
|
||||
import.c \
|
||||
input.c \
|
||||
irq.c \
|
||||
@@ -183,31 +183,10 @@ $(BUILD)/drivers/cc3100/src/driver.o: CFLAGS += -fno-strict-aliasing
|
||||
|
||||
# Check if we would like to debug the port code
|
||||
ifeq ($(BTYPE), release)
|
||||
# Optimize everything and define the NDEBUG flag
|
||||
CFLAGS += -Os -DNDEBUG
|
||||
CFLAGS += -DNDEBUG
|
||||
else
|
||||
ifeq ($(BTYPE), debug)
|
||||
# Define the DEBUG flag
|
||||
CFLAGS += -DDEBUG=DEBUG
|
||||
# Optimize the stable sources only
|
||||
$(BUILD)/extmod/%.o: CFLAGS += -Os
|
||||
$(BUILD)/lib/%.o: CFLAGS += -Os
|
||||
$(BUILD)/fatfs/src/%.o: CFLAGS += -Os
|
||||
$(BUILD)/FreeRTOS/Source/%.o: CFLAGS += -Os
|
||||
$(BUILD)/ftp/%.o: CFLAGS += -Os
|
||||
$(BUILD)/hal/%.o: CFLAGS += -Os
|
||||
$(BUILD)/misc/%.o: CFLAGS += -Os
|
||||
$(BUILD)/mods/%.o: CFLAGS += -Os
|
||||
$(BUILD)/py/%.o: CFLAGS += -Os
|
||||
$(BUILD)/simplelink/%.o: CFLAGS += -Os
|
||||
$(BUILD)/drivers/cc3100/%.o: CFLAGS += -Os
|
||||
$(BUILD)/stmhal/%.o: CFLAGS += -Os
|
||||
$(BUILD)/telnet/%.o: CFLAGS += -Os
|
||||
$(BUILD)/util/%.o: CFLAGS += -Os
|
||||
$(BUILD)/pins.o: CFLAGS += -Os
|
||||
$(BUILD)/main.o: CFLAGS += -Os
|
||||
$(BUILD)/mptask.o: CFLAGS += -Os
|
||||
$(BUILD)/servertask.o: CFLAGS += -Os
|
||||
CFLAGS += -DNDEBUG
|
||||
else
|
||||
$(error Invalid BTYPE specified)
|
||||
endif
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Pin,Name,Default,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF14,AF15,ADC
|
||||
1,GP10,GP10,GP10,I2C0_SCL,,TIM3_PWM0,,,SD0_CLK,UART1_TX,,,,,TIM0_CC1,,,,
|
||||
2,GP11,GP11,GP11,I2C0_SDA,,TIM3_PWM1,pXCLK(XVCLK),,SD0_CMD,UART1_RX,,,,,TIM1_CC0,I2S0_FS,,,
|
||||
3,GP12,GP12,GP12,,,I2S0_CLK,pVS(VSYNC),I2C0_SCL,,UART0_TX,,,,,TIM1_CC1,,,,
|
||||
4,GP13,GP13,GP13,,,,pHS(HSYNC),I2C0_SDA,,UART0_RX,,,,,TIM2_CC0,,,,
|
||||
5,GP14,GP14,GP14,,,,pDATA8(CAM_D4),I2C0_SCL,,SPI0_CLK,,,,,TIM2_CC1,,,,
|
||||
6,GP15,GP15,GP15,,,,pDATA9(CAM_D5),I2C0_SDA,,SPI0_MISO,SD0_DAT0,,,,,TIM3_CC0,,,
|
||||
7,GP16,GP16,GP16,,,,pDATA10(CAM_D6),UART1_TX,,SPI0_MOSI,SD0_CLK,,,,,TIM3_CC1,,,
|
||||
1,GP10,GP10,GP10,I2C0_SCL,,TIM3_PWM,,,SD0_CLK,UART1_TX,,,,,TIM0_CC,,,,
|
||||
2,GP11,GP11,GP11,I2C0_SDA,,TIM3_PWM,pXCLK(XVCLK),,SD0_CMD,UART1_RX,,,,,TIM1_CC,I2S0_FS,,,
|
||||
3,GP12,GP12,GP12,,,I2S0_CLK,pVS(VSYNC),I2C0_SCL,,UART0_TX,,,,,TIM1_CC,,,,
|
||||
4,GP13,GP13,GP13,,,,pHS(HSYNC),I2C0_SDA,,UART0_RX,,,,,TIM2_CC,,,,
|
||||
5,GP14,GP14,GP14,,,,pDATA8(CAM_D4),I2C0_SCL,,SPI0_CLK,,,,,TIM2_CC,,,,
|
||||
6,GP15,GP15,GP15,,,,pDATA9(CAM_D5),I2C0_SDA,,SPI0_MISO,SD0_DAT0,,,,,TIM3_CC,,,
|
||||
7,GP16,GP16,GP16,,,,pDATA10(CAM_D6),UART1_TX,,SPI0_MOSI,SD0_CLK,,,,,TIM3_CC,,,
|
||||
8,GP17,GP17,GP17,,,,pDATA11(CAM_D7),UART1_RX,,SPI0_CS0,SD0_CMD,,,,,,,,
|
||||
9,VDD_DIG1,VDD_DIG1,VDD_DIG1,,,,,,,,,,,,,,,,
|
||||
10,VIN_IO1,VIN_IO1,VIN_IO1,,,,,,,,,,,,,,,,
|
||||
@@ -13,13 +13,13 @@ Pin,Name,Default,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF1
|
||||
12,FLASH_SPI_DOUT,FLASH_SPI_DOUT,FLASH_SPI_DOUT,,,,,,,,,,,,,,,,
|
||||
13,FLASH_SPI_DIN,FLASH_SPI_DIN,FLASH_SPI_DIN,,,,,,,,,,,,,,,,
|
||||
14,FLASH_SPI_CS,FLASH_SPI_CS,FLASH_SPI_CS,,,,,,,,,,,,,,,,
|
||||
15,GP22,GP22,GP22,,,,,TIM2_CC0,,I2S0_FS,,,,,,,,,
|
||||
15,GP22,GP22,GP22,,,,,TIM2_CC,,I2S0_FS,,,,,,,,,
|
||||
16,GP23,TDI,GP23,TDI,UART1_TX,,,,,,,I2C0_SCL,,,,,,,
|
||||
17,GP24,TDO,GP24,TDO,UART1_RX,,TIM3_CC0,TIM0_PWM0,I2S0_FS,,,I2C0_SDA,,,,,,,
|
||||
17,GP24,TDO,GP24,TDO,UART1_RX,,TIM3_CC,TIM0_PWM,I2S0_FS,,,I2C0_SDA,,,,,,,
|
||||
18,GP28,GP28,GP28,,,,,,,,,,,,,,,,
|
||||
19,TCK,TCK,,TCK,,,,,,,TIM1_PWM2,,,,,,,,
|
||||
19,TCK,TCK,,TCK,,,,,,,TIM1_PWM,,,,,,,,
|
||||
20,GP29,TMS,GP29,TMS,,,,,,,,,,,,,,,
|
||||
21,GP25,SOP2,GP25,,I2S0_FS,,,,,,,TIM1_PWM0,,,,,,,
|
||||
21,GP25,SOP2,GP25,,I2S0_FS,,,,,,,TIM1_PWM,,,,,,,
|
||||
22,WLAN_XTAL_N,WLAN_XTAL_N,WLAN_XTAL_N,,,,,,,,,,,,,,,,
|
||||
23,WLAN_XTAL_P,WLAN_XTAL_P,WLAN_XTAL_P,,,,,,,,,,,,,,,,
|
||||
24,VDD_PLL,VDD_PLL,VDD_PLL,,,,,,,,,,,,,,,,
|
||||
@@ -48,19 +48,19 @@ Pin,Name,Default,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF1
|
||||
47,VDD_ANA2,VDD_ANA2,VDD_ANA2,,,,,,,,,,,,,,,,
|
||||
48,VDD_ANA1,VDD_ANA1,VDD_ANA1,,,,,,,,,,,,,,,,
|
||||
49,VDD_RAM,VDD_RAM,VDD_RAM,,,,,,,,,,,,,,,,
|
||||
50,GP0,GP0,GP0,,,UART0_RTS,I2S0_DAT0,,I2S0_DAT1,TIM0_CC0,,SPI0_CS0,UART1_RTS,,UART0_CTS,,,,
|
||||
50,GP0,GP0,GP0,,,UART0_RTS,I2S0_DAT0,,I2S0_DAT1,TIM0_CC,,SPI0_CS0,UART1_RTS,,UART0_CTS,,,,
|
||||
51,RTC_XTAL_P,RTC_XTAL_P,RTC_XTAL_P,,,,,,,,,,,,,,,,
|
||||
52,RTC_XTAL_N,RTC_XTAL_N,GP32,,I2S0_CLK,,I2S0_DAT0,,UART0_RTS,,SPI0_MOSI,,,,,,,,
|
||||
53,GP30,GP30,GP30,,I2S0_CLK,I2S0_FS,TIM2_CC1,,,SPI0_MISO,,UART0_TX,,,,,,,
|
||||
53,GP30,GP30,GP30,,I2S0_CLK,I2S0_FS,TIM2_CC,,,SPI0_MISO,,UART0_TX,,,,,,,
|
||||
54,VIN_IO2,VIN_IO2,VIN_IO2,,,,,,,,,,,,,,,,
|
||||
55,GP1,GP1,GP1,,,UART0_TX,pCLK (PIXCLK),,UART1_TX,TIM0_CC1,,,,,,,,,
|
||||
55,GP1,GP1,GP1,,,UART0_TX,pCLK (PIXCLK),,UART1_TX,TIM0_CC,,,,,,,,,
|
||||
56,VDD_DIG2,VDD_DIG2,VDD_DIG2,,,,,,,,,,,,,,,,
|
||||
57,GP2,GP2,GP2,,,UART0_RX,,,UART1_RX,TIM1_CC0,,,,,,,,,ADC0_CH0
|
||||
57,GP2,GP2,GP2,,,UART0_RX,,,UART1_RX,TIM1_CC,,,,,,,,,ADC0_CH0
|
||||
58,GP3,GP3,GP3,,,,pDATA7(CAM_D3),,UART1_TX,,,,,,,,,,ADC0_CH1
|
||||
59,GP4,GP4,GP4,,,,pDATA6(CAM_D2),,UART1_RX,,,,,,,,,,ADC0_CH2
|
||||
60,GP5,GP5,GP5,,,,pDATA5(CAM_D1),,I2S0_DAT1,TIM2_CC1,,,,,,,,,ADC0_CH3
|
||||
61,GP6,GP6,GP6,,,UART1_CTS,pDATA4(CAM_D0),UART0_RTS,UART0_CTS,TIM3_CC0,,,,,,,,,
|
||||
60,GP5,GP5,GP5,,,,pDATA5(CAM_D1),,I2S0_DAT1,TIM2_CC,,,,,,,,,ADC0_CH3
|
||||
61,GP6,GP6,GP6,,,UART1_CTS,pDATA4(CAM_D0),UART0_RTS,UART0_CTS,TIM3_CC,,,,,,,,,
|
||||
62,GP7,GP7,GP7,,,UART1_RTS,,,,,,,UART0_RTS,UART0_TX,,I2S0_CLK,,,
|
||||
63,GP8,GP8,GP8,,,,,,SD0_IRQ,I2S0_FS,,,,,TIM3_CC0,,,,
|
||||
64,GP9,GP9,GP9,,,TIM2_PWM1,,,SD0_DAT0,I2S0_DAT0,,,,,TIM0_CC0,,,,
|
||||
63,GP8,GP8,GP8,,,,,,SD0_IRQ,I2S0_FS,,,,,TIM3_CC,,,,
|
||||
64,GP9,GP9,GP9,,,TIM2_PWM,,,SD0_DAT0,I2S0_DAT0,,,,,TIM0_CC,,,,
|
||||
65,GND_TAB,GND_TAB,GND_TAB,,,,,,,,,,,,,,,,
|
||||
|
||||
|
@@ -12,7 +12,7 @@ SUPPORTED_AFS = { 'UART': ('TX', 'RX', 'RTS', 'CTS'),
|
||||
'SPI': ('CLK', 'MOSI', 'MISO', 'CS0'),
|
||||
#'I2S': ('CLK', 'FS', 'DAT0', 'DAT1'),
|
||||
'I2C': ('SDA', 'SCL'),
|
||||
'TIM': ('PWM0', 'PWM1', 'CC0', 'CC1'),
|
||||
'TIM': ('PWM'),
|
||||
'SD': ('CLK', 'CMD', 'DAT0'),
|
||||
'ADC': ('CH0', 'CH1', 'CH2', 'CH3')
|
||||
}
|
||||
@@ -44,6 +44,7 @@ class AF:
|
||||
def print(self):
|
||||
print (' AF({:16s}, {:4d}, {:8s}, {:4d}, {:8s}), // {}'.format(self.name, self.idx, self.fn, self.unit, self.type, self.name))
|
||||
|
||||
|
||||
class Pin:
|
||||
"""Holds the information associated with a pin."""
|
||||
def __init__(self, name, port, gpio_bit, pin_num):
|
||||
|
||||
@@ -196,6 +196,10 @@ void nlr_jump_fail(void *val) {
|
||||
|
||||
void mperror_enable_heartbeat (bool enable) {
|
||||
if (enable) {
|
||||
#ifndef BOOTLOADER
|
||||
// configure the led again
|
||||
pin_config ((pin_obj_t *)&MICROPY_SYS_LED_GPIO, PIN_MODE_0, GPIO_DIR_MODE_OUT, PIN_TYPE_STD, 0, PIN_STRENGTH_6MA);
|
||||
#endif
|
||||
mperror_heart_beat.enabled = true;
|
||||
mperror_heart_beat.do_disable = false;
|
||||
mperror_heartbeat_switch_off();
|
||||
|
||||
@@ -153,7 +153,7 @@ STATIC const mp_map_elem_t mp_module_network_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_WLAN), (mp_obj_t)&mod_network_nic_type_wlan },
|
||||
|
||||
#if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP)
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_server), (mp_obj_t)&network_server_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_Server), (mp_obj_t)&network_server_type },
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -177,7 +177,7 @@ STATIC MP_DEFINE_CONST_DICT(network_server_locals_dict, network_server_locals_di
|
||||
|
||||
STATIC const mp_obj_type_t network_server_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_server,
|
||||
.name = MP_QSTR_Server,
|
||||
.make_new = network_server_make_new,
|
||||
.locals_dict = (mp_obj_t)&network_server_locals_dict,
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "moduos.h"
|
||||
#include "diskio.h"
|
||||
#include "sflash_diskio.h"
|
||||
#include "file.h"
|
||||
#include "extmod/vfs_fat_file.h"
|
||||
#include "random.h"
|
||||
#include "mpexception.h"
|
||||
#include "version.h"
|
||||
|
||||
@@ -106,8 +106,10 @@ void modusocket_enter_sleep (void) {
|
||||
}
|
||||
}
|
||||
|
||||
// wait for any of the sockets to become ready...
|
||||
sl_Select(maxfd + 1, &socketset, NULL, NULL, NULL);
|
||||
if (maxfd > 0) {
|
||||
// wait for any of the sockets to become ready...
|
||||
sl_Select(maxfd + 1, &socketset, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void modusocket_close_all_user_sockets (void) {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "py/obj.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/mphal.h"
|
||||
#include "inc/hw_types.h"
|
||||
#include "inc/hw_ints.h"
|
||||
@@ -149,8 +150,8 @@ STATIC wlan_obj_t wlan_obj = {
|
||||
.ssid = MICROPY_PORT_WLAN_AP_SSID,
|
||||
.key = MICROPY_PORT_WLAN_AP_KEY,
|
||||
.mac = {0},
|
||||
.ssid_o = {0},
|
||||
.bssid = {0},
|
||||
//.ssid_o = {0},
|
||||
//.bssid = {0},
|
||||
#if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP)
|
||||
.servers_enabled = false,
|
||||
#endif
|
||||
@@ -210,11 +211,11 @@ void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent) {
|
||||
{
|
||||
case SL_WLAN_CONNECT_EVENT:
|
||||
{
|
||||
slWlanConnectAsyncResponse_t *pEventData = &pWlanEvent->EventData.STAandP2PModeWlanConnected;
|
||||
//slWlanConnectAsyncResponse_t *pEventData = &pWlanEvent->EventData.STAandP2PModeWlanConnected;
|
||||
// copy the new connection data
|
||||
memcpy(wlan_obj.bssid, pEventData->bssid, SL_BSSID_LENGTH);
|
||||
memcpy(wlan_obj.ssid_o, pEventData->ssid_name, pEventData->ssid_len);
|
||||
wlan_obj.ssid_o[pEventData->ssid_len] = '\0';
|
||||
//memcpy(wlan_obj.bssid, pEventData->bssid, SL_BSSID_LENGTH);
|
||||
//memcpy(wlan_obj.ssid_o, pEventData->ssid_name, pEventData->ssid_len);
|
||||
//wlan_obj.ssid_o[pEventData->ssid_len] = '\0';
|
||||
SET_STATUS_BIT(wlan_obj.status, STATUS_BIT_CONNECTION);
|
||||
#if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP)
|
||||
// we must reset the servers in case that the last connection
|
||||
@@ -228,15 +229,16 @@ void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent) {
|
||||
CLR_STATUS_BIT(wlan_obj.status, STATUS_BIT_IP_ACQUIRED);
|
||||
#if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP)
|
||||
servers_reset();
|
||||
servers_wlan_cycle_power();
|
||||
#endif
|
||||
break;
|
||||
case SL_WLAN_STA_CONNECTED_EVENT:
|
||||
{
|
||||
slPeerInfoAsyncResponse_t *pEventData = &pWlanEvent->EventData.APModeStaConnected;
|
||||
//slPeerInfoAsyncResponse_t *pEventData = &pWlanEvent->EventData.APModeStaConnected;
|
||||
// get the mac address and name of the connected device
|
||||
memcpy(wlan_obj.bssid, pEventData->mac, SL_BSSID_LENGTH);
|
||||
memcpy(wlan_obj.ssid_o, pEventData->go_peer_device_name, pEventData->go_peer_device_name_len);
|
||||
wlan_obj.ssid_o[pEventData->go_peer_device_name_len] = '\0';
|
||||
//memcpy(wlan_obj.bssid, pEventData->mac, SL_BSSID_LENGTH);
|
||||
//memcpy(wlan_obj.ssid_o, pEventData->go_peer_device_name, pEventData->go_peer_device_name_len);
|
||||
//wlan_obj.ssid_o[pEventData->go_peer_device_name_len] = '\0';
|
||||
SET_STATUS_BIT(wlan_obj.status, STATUS_BIT_CONNECTION);
|
||||
#if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP)
|
||||
// we must reset the servers in case that the last connection
|
||||
@@ -249,6 +251,7 @@ void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent) {
|
||||
CLR_STATUS_BIT(wlan_obj.status, STATUS_BIT_CONNECTION);
|
||||
#if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP)
|
||||
servers_reset();
|
||||
servers_wlan_cycle_power();
|
||||
#endif
|
||||
break;
|
||||
case SL_WLAN_P2P_DEV_FOUND_EVENT:
|
||||
@@ -547,6 +550,12 @@ void wlan_set_current_time (uint32_t seconds_since_2000) {
|
||||
sl_DevSet(SL_DEVICE_GENERAL_CONFIGURATION, SL_DEVICE_GENERAL_CONFIGURATION_DATE_TIME, sizeof(SlDateTime_t), (_u8 *)(&sl_datetime));
|
||||
}
|
||||
|
||||
void wlan_off_on (void) {
|
||||
// no need to lock the WLAN object on every API call since the servers and the MicroPtyhon
|
||||
// task have the same priority
|
||||
wlan_reenable(wlan_obj.mode);
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// DEFINE STATIC FUNCTIONS
|
||||
//*****************************************************************************
|
||||
@@ -554,8 +563,8 @@ void wlan_set_current_time (uint32_t seconds_since_2000) {
|
||||
STATIC void wlan_clear_data (void) {
|
||||
CLR_STATUS_BIT_ALL(wlan_obj.status);
|
||||
wlan_obj.ip = 0;
|
||||
memset(wlan_obj.ssid_o, 0, sizeof(wlan_obj.ssid));
|
||||
memset(wlan_obj.bssid, 0, sizeof(wlan_obj.bssid));
|
||||
//memset(wlan_obj.ssid_o, 0, sizeof(wlan_obj.ssid));
|
||||
//memset(wlan_obj.bssid, 0, sizeof(wlan_obj.bssid));
|
||||
}
|
||||
|
||||
STATIC void wlan_reenable (SlWlanMode_t mode) {
|
||||
|
||||
@@ -95,6 +95,7 @@ extern void wlan_get_mac (uint8_t *macAddress);
|
||||
extern void wlan_get_ip (uint32_t *ip);
|
||||
extern bool wlan_is_connected (void);
|
||||
extern void wlan_set_current_time (uint32_t seconds_since_2000);
|
||||
extern void wlan_off_on (void);
|
||||
|
||||
extern int wlan_gethostbyname(const char *name, mp_uint_t len, uint8_t *out_ip, uint8_t family);
|
||||
extern int wlan_socket_socket(mod_network_socket_obj_t *s, int *_errno);
|
||||
|
||||
@@ -63,7 +63,7 @@ typedef struct _pyb_i2c_obj_t {
|
||||
#define PYBI2C_MIN_BAUD_RATE_HZ (50000)
|
||||
#define PYBI2C_MAX_BAUD_RATE_HZ (400000)
|
||||
|
||||
#define PYBI2C_TRANSC_TIMEOUT_MS (10)
|
||||
#define PYBI2C_TRANSC_TIMEOUT_MS (20)
|
||||
#define PYBI2C_TRANSAC_WAIT_DELAY_US (10)
|
||||
|
||||
#define PYBI2C_TIMEOUT_TO_COUNT(to_us, baud) (((baud) * to_us) / 16000000)
|
||||
@@ -78,9 +78,13 @@ typedef struct _pyb_i2c_obj_t {
|
||||
DECLARE PRIVATE DATA
|
||||
******************************************************************************/
|
||||
STATIC pyb_i2c_obj_t pyb_i2c_obj = {.baudrate = 0};
|
||||
|
||||
STATIC const mp_obj_t pyb_i2c_def_pin[2] = {&pin_GP13, &pin_GP23};
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE PRIVATE FUNCTIONS
|
||||
******************************************************************************/
|
||||
STATIC bool pyb_i2c_write(byte addr, byte *data, uint len, bool stop);
|
||||
|
||||
/******************************************************************************
|
||||
DEFINE PRIVATE FUNCTIONS
|
||||
******************************************************************************/
|
||||
@@ -107,13 +111,13 @@ STATIC bool pyb_i2c_transaction(uint cmd) {
|
||||
// Wait until the current byte has been transferred.
|
||||
// Poll on the raw interrupt status.
|
||||
while ((MAP_I2CMasterIntStatusEx(I2CA0_BASE, false) & (I2C_MASTER_INT_DATA | I2C_MASTER_INT_TIMEOUT)) == 0) {
|
||||
// wait for a few microseconds
|
||||
UtilsDelay(UTILS_DELAY_US_TO_COUNT(PYBI2C_TRANSAC_WAIT_DELAY_US));
|
||||
timeout -= PYBI2C_TRANSAC_WAIT_DELAY_US;
|
||||
if (timeout < 0) {
|
||||
// the peripheral is not responding, so stop
|
||||
return false;
|
||||
}
|
||||
// wait for a few microseconds
|
||||
UtilsDelay(UTILS_DELAY_US_TO_COUNT(PYBI2C_TRANSAC_WAIT_DELAY_US));
|
||||
timeout -= PYBI2C_TRANSAC_WAIT_DELAY_US;
|
||||
}
|
||||
|
||||
// Check for any errors in the transfer
|
||||
@@ -145,13 +149,22 @@ STATIC void pyb_i2c_check_init(pyb_i2c_obj_t *self) {
|
||||
}
|
||||
|
||||
STATIC bool pyb_i2c_scan_device(byte devAddr) {
|
||||
// Set I2C codec slave address
|
||||
bool ret = false;
|
||||
// Set the I2C slave address
|
||||
MAP_I2CMasterSlaveAddrSet(I2CA0_BASE, devAddr, true);
|
||||
// Initiate the transfer.
|
||||
RET_IF_ERR(pyb_i2c_transaction(I2C_MASTER_CMD_SINGLE_RECEIVE));
|
||||
// Since this is a hack, send the stop bit anyway
|
||||
if (pyb_i2c_transaction(I2C_MASTER_CMD_SINGLE_RECEIVE)) {
|
||||
ret = true;
|
||||
}
|
||||
// Send the stop bit to cancel the read transaction
|
||||
MAP_I2CMasterControl(I2CA0_BASE, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP);
|
||||
return true;
|
||||
if (!ret) {
|
||||
uint8_t data = 0;
|
||||
if (pyb_i2c_write(devAddr, &data, sizeof(data), true)) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
STATIC bool pyb_i2c_mem_addr_write (byte addr, byte *mem_addr, uint mem_addr_len) {
|
||||
@@ -365,7 +378,7 @@ STATIC mp_obj_t pyb_i2c_scan(mp_obj_t self_in) {
|
||||
pyb_i2c_check_init(&pyb_i2c_obj);
|
||||
mp_obj_t list = mp_obj_new_list(0, NULL);
|
||||
for (uint addr = 1; addr <= 127; addr++) {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (pyb_i2c_scan_device(addr)) {
|
||||
mp_obj_list_append(list, mp_obj_new_int(addr));
|
||||
break;
|
||||
|
||||
@@ -60,7 +60,6 @@ DECLARE PRIVATE FUNCTIONS
|
||||
STATIC pin_obj_t *pin_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t name);
|
||||
STATIC pin_obj_t *pin_find_pin_by_port_bit (const mp_obj_dict_t *named_pins, uint port, uint bit);
|
||||
STATIC int8_t pin_obj_find_af (const pin_obj_t* pin, uint8_t fn, uint8_t unit, uint8_t type);
|
||||
STATIC int8_t pin_find_af_index (const pin_obj_t* pin, uint8_t fn, uint8_t unit, uint8_t type);
|
||||
STATIC void pin_free_af_from_pins (uint8_t fn, uint8_t unit, uint8_t type);
|
||||
STATIC void pin_deassign (pin_obj_t* pin);
|
||||
STATIC void pin_obj_configure (const pin_obj_t *self);
|
||||
@@ -199,6 +198,14 @@ uint8_t pin_find_peripheral_type (const mp_obj_t pin, uint8_t fn, uint8_t unit)
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||
}
|
||||
|
||||
int8_t pin_find_af_index (const pin_obj_t* pin, uint8_t fn, uint8_t unit, uint8_t type) {
|
||||
int8_t af = pin_obj_find_af(pin, fn, unit, type);
|
||||
if (af < 0) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||
}
|
||||
return af;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
DEFINE PRIVATE FUNCTIONS
|
||||
******************************************************************************/
|
||||
@@ -231,14 +238,6 @@ STATIC int8_t pin_obj_find_af (const pin_obj_t* pin, uint8_t fn, uint8_t unit, u
|
||||
return -1;
|
||||
}
|
||||
|
||||
STATIC int8_t pin_find_af_index (const pin_obj_t* pin, uint8_t fn, uint8_t unit, uint8_t type) {
|
||||
int8_t af = pin_obj_find_af(pin, fn, unit, type);
|
||||
if (af < 0) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||
}
|
||||
return af;
|
||||
}
|
||||
|
||||
STATIC void pin_free_af_from_pins (uint8_t fn, uint8_t unit, uint8_t type) {
|
||||
mp_map_t *named_map = mp_obj_dict_get_map((mp_obj_t)&pin_board_pins_locals_dict);
|
||||
for (uint i = 0; i < named_map->used - 1; i++) {
|
||||
@@ -248,7 +247,7 @@ STATIC void pin_free_af_from_pins (uint8_t fn, uint8_t unit, uint8_t type) {
|
||||
// check if the pin supports the target af
|
||||
int af = pin_obj_find_af(pin, fn, unit, type);
|
||||
if (af > 0 && af == pin->af) {
|
||||
// the pin is assigned to the target af, de-assign it
|
||||
// the pin supports the target af, de-assign it
|
||||
pin_deassign (pin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,10 +72,7 @@ enum {
|
||||
};
|
||||
|
||||
enum {
|
||||
PIN_TYPE_TIM_PWM0 = 0,
|
||||
PIN_TYPE_TIM_PWM1,
|
||||
PIN_TYPE_TIM_CC0,
|
||||
PIN_TYPE_TIM_CC1,
|
||||
PIN_TYPE_TIM_PWM = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -139,5 +136,6 @@ pin_obj_t *pin_find(mp_obj_t user_obj);
|
||||
void pin_assign_pins_af (mp_obj_t *pins, uint32_t n_pins, uint32_t pull, uint32_t fn, uint32_t unit);
|
||||
uint8_t pin_find_peripheral_unit (const mp_obj_t pin, uint8_t fn, uint8_t type);
|
||||
uint8_t pin_find_peripheral_type (const mp_obj_t pin, uint8_t fn, uint8_t unit);
|
||||
int8_t pin_find_af_index (const pin_obj_t* pin, uint8_t fn, uint8_t unit, uint8_t type);;
|
||||
|
||||
#endif // PYBPIN_H_
|
||||
|
||||
@@ -43,7 +43,10 @@
|
||||
#include "interrupt.h"
|
||||
#include "prcm.h"
|
||||
#include "timer.h"
|
||||
#include "pin.h"
|
||||
#include "pybtimer.h"
|
||||
#include "pybpin.h"
|
||||
#include "pins.h"
|
||||
#include "mpirq.h"
|
||||
#include "pybsleep.h"
|
||||
#include "mpexception.h"
|
||||
@@ -55,30 +58,8 @@
|
||||
/// Each timer consists of a counter that counts up at a certain rate. The rate
|
||||
/// at which it counts is the peripheral clock frequency (in Hz) divided by the
|
||||
/// timer prescaler. When the counter reaches the timer period it triggers an
|
||||
/// event, and the counter resets back to zero. By using the callback method,
|
||||
/// event, and the counter resets back to zero. By using the irq method,
|
||||
/// the timer event can call a Python function.
|
||||
///
|
||||
/// Example usage to toggle an LED at a fixed frequency:
|
||||
///
|
||||
/// tim = pyb.Timer(4) # create a timer object using timer 4
|
||||
/// tim.init(mode=Timer.PERIODIC) # initialize it in periodic mode
|
||||
/// tim_ch = tim.channel(Timer.A, freq=2) # configure channel A at a frequency of 2Hz
|
||||
/// tim_ch.callback(handler=lambda t:led.toggle()) # toggle a LED on every cycle of the timer
|
||||
///
|
||||
/// Further examples:
|
||||
///
|
||||
/// tim1 = pyb.Timer(2, mode=Timer.EVENT_COUNT) # initialize it capture mode
|
||||
/// tim2 = pyb.Timer(1, mode=Timer.PWM) # initialize it in PWM mode
|
||||
/// tim_ch = tim1.channel(Timer.A, freq=1, polarity=Timer.POSITIVE) # start the event counter with a frequency of 1Hz and triggered by positive edges
|
||||
/// tim_ch = tim2.channel(Timer.B, freq=10000, duty_cycle=50) # start the PWM on channel B with a 50% duty cycle
|
||||
/// tim_ch.time() # get the current time in usec (can also be set)
|
||||
/// tim_ch.freq(20) # set the frequency (can also get)
|
||||
/// tim_ch.duty_cycle(30) # set the duty cycle to 30% (can also get)
|
||||
/// tim_ch.duty_cycle(30, Timer.NEGATIVE) # set the duty cycle to 30% and change the polarity to negative
|
||||
/// tim_ch.event_count() # get the number of captured events
|
||||
/// tim_ch.event_time() # get the the time of the last captured event
|
||||
/// tim_ch.period(2000000) # change the period to 2 seconds
|
||||
///
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE PRIVATE CONSTANTS
|
||||
@@ -87,6 +68,9 @@
|
||||
#define PYBTIMER_POLARITY_POS (0x01)
|
||||
#define PYBTIMER_POLARITY_NEG (0x02)
|
||||
|
||||
#define PYBTIMER_TIMEOUT_TRIGGER (0x01)
|
||||
#define PYBTIMER_MATCH_TRIGGER (0x02)
|
||||
|
||||
#define PYBTIMER_SRC_FREQ_HZ HAL_FCPU_HZ
|
||||
|
||||
/******************************************************************************
|
||||
@@ -108,8 +92,8 @@ typedef struct _pyb_timer_channel_obj_t {
|
||||
uint32_t frequency;
|
||||
uint32_t period;
|
||||
uint16_t channel;
|
||||
uint16_t duty_cycle;
|
||||
uint8_t polarity;
|
||||
uint8_t duty_cycle;
|
||||
} pyb_timer_channel_obj_t;
|
||||
|
||||
/******************************************************************************
|
||||
@@ -121,12 +105,14 @@ STATIC pyb_timer_obj_t pyb_timer_obj[PYBTIMER_NUM_TIMERS] = {{.timer = TIMERA0_B
|
||||
{.timer = TIMERA2_BASE, .peripheral = PRCM_TIMERA2},
|
||||
{.timer = TIMERA3_BASE, .peripheral = PRCM_TIMERA3}};
|
||||
STATIC const mp_obj_type_t pyb_timer_channel_type;
|
||||
STATIC const mp_obj_t pyb_timer_pwm_pin[8] = {&pin_GP24, MP_OBJ_NULL, &pin_GP25, MP_OBJ_NULL, MP_OBJ_NULL, &pin_GP9, &pin_GP10, &pin_GP11};
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE PRIVATE FUNCTIONS
|
||||
******************************************************************************/
|
||||
STATIC mp_obj_t pyb_timer_channel_irq (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
|
||||
STATIC void timer_disable (pyb_timer_obj_t *tim);
|
||||
STATIC void timer_channel_init (pyb_timer_channel_obj_t *ch);
|
||||
STATIC void TIMER0AIntHandler(void);
|
||||
STATIC void TIMER0BIntHandler(void);
|
||||
STATIC void TIMER1AIntHandler(void);
|
||||
@@ -177,6 +163,8 @@ STATIC void pyb_timer_channel_remove (pyb_timer_channel_obj_t *ch) {
|
||||
pyb_timer_channel_obj_t *channel;
|
||||
if ((channel = pyb_timer_channel_find(ch->timer->timer, ch->channel))) {
|
||||
mp_obj_list_remove(&MP_STATE_PORT(pyb_timer_channel_obj_list), channel);
|
||||
// unregister it with the sleep module
|
||||
pyb_sleep_remove((const mp_obj_t)channel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +172,8 @@ STATIC void pyb_timer_channel_add (pyb_timer_channel_obj_t *ch) {
|
||||
// remove it in case it already exists
|
||||
pyb_timer_channel_remove(ch);
|
||||
mp_obj_list_append(&MP_STATE_PORT(pyb_timer_channel_obj_list), ch);
|
||||
// register it with the sleep module
|
||||
pyb_sleep_add((const mp_obj_t)ch, (WakeUpCB_t)timer_channel_init);
|
||||
}
|
||||
|
||||
STATIC void timer_disable (pyb_timer_obj_t *tim) {
|
||||
@@ -191,8 +181,15 @@ STATIC void timer_disable (pyb_timer_obj_t *tim) {
|
||||
MAP_TimerDisable(tim->timer, TIMER_A | TIMER_B);
|
||||
MAP_TimerIntDisable(tim->timer, tim->irq_trigger);
|
||||
MAP_TimerIntClear(tim->timer, tim->irq_trigger);
|
||||
pyb_timer_channel_obj_t *ch;
|
||||
// disable its channels
|
||||
if ((ch = pyb_timer_channel_find (tim->timer, TIMER_A))) {
|
||||
pyb_sleep_remove(ch);
|
||||
}
|
||||
if ((ch = pyb_timer_channel_find (tim->timer, TIMER_B))) {
|
||||
pyb_sleep_remove(ch);
|
||||
}
|
||||
MAP_PRCMPeripheralClkDisable(tim->peripheral, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK);
|
||||
memset(&pyb_timer_obj[tim->id], 0, sizeof(pyb_timer_obj_t));
|
||||
}
|
||||
|
||||
// computes prescaler period and match value so timer triggers at freq-Hz
|
||||
@@ -205,20 +202,23 @@ STATIC uint32_t compute_prescaler_period_and_match_value(pyb_timer_channel_obj_t
|
||||
if (period_c == 0) {
|
||||
goto error;
|
||||
}
|
||||
prescaler = period_c >> 16;
|
||||
|
||||
prescaler = period_c >> 16; // The prescaler is an extension of the timer counter
|
||||
*period_out = period_c;
|
||||
|
||||
if (prescaler > 0xFF && maxcount == 0xFFFF) {
|
||||
goto error;
|
||||
}
|
||||
// check limit values for the duty cycle
|
||||
if (ch->duty_cycle == 0) {
|
||||
*match_out = period_c - 1;
|
||||
}
|
||||
else {
|
||||
*match_out = period_c - ((period_c * ch->duty_cycle) / 100);
|
||||
}
|
||||
if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM && (*match_out > 0xFFFF)) {
|
||||
goto error;
|
||||
} else {
|
||||
if (period_c > 0xFFFF) {
|
||||
uint32_t match = (period_c * 100) / 10000;
|
||||
*match_out = period_c - ((match * ch->duty_cycle) / 100);
|
||||
} else {
|
||||
*match_out = period_c - ((period_c * ch->duty_cycle) / 10000);
|
||||
}
|
||||
}
|
||||
return prescaler;
|
||||
|
||||
@@ -250,17 +250,7 @@ STATIC void timer_channel_init (pyb_timer_channel_obj_t *ch) {
|
||||
MAP_TimerControlLevel(ch->timer->timer, ch->channel, (ch->polarity == PYBTIMER_POLARITY_NEG) ? true : false);
|
||||
// set the match value (which is simply the duty cycle translated to ticks)
|
||||
MAP_TimerMatchSet(ch->timer->timer, ch->channel, match);
|
||||
}
|
||||
// configure the event edge type if we are in such mode
|
||||
else if ((ch->timer->config & 0x0F) == TIMER_CFG_A_CAP_COUNT || (ch->timer->config & 0x0F) == TIMER_CFG_A_CAP_TIME) {
|
||||
uint32_t polarity = TIMER_EVENT_BOTH_EDGES;
|
||||
if (ch->polarity == PYBTIMER_POLARITY_POS) {
|
||||
polarity = TIMER_EVENT_POS_EDGE;
|
||||
}
|
||||
else if (ch->polarity == PYBTIMER_POLARITY_NEG) {
|
||||
polarity = TIMER_EVENT_NEG_EDGE;
|
||||
}
|
||||
MAP_TimerControlEvent(ch->timer->timer, ch->channel, polarity);
|
||||
MAP_TimerPrescaleMatchSet(ch->timer->timer, ch->channel, match >> 16);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -282,37 +272,18 @@ STATIC void pyb_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_
|
||||
// timer mode
|
||||
qstr mode_qst = MP_QSTR_PWM;
|
||||
switch(mode) {
|
||||
case TIMER_CFG_A_ONE_SHOT:
|
||||
case TIMER_CFG_A_ONE_SHOT_UP:
|
||||
mode_qst = MP_QSTR_ONE_SHOT;
|
||||
break;
|
||||
case TIMER_CFG_A_PERIODIC:
|
||||
case TIMER_CFG_A_PERIODIC_UP:
|
||||
mode_qst = MP_QSTR_PERIODIC;
|
||||
break;
|
||||
case TIMER_CFG_A_CAP_COUNT:
|
||||
mode_qst = MP_QSTR_EDGE_COUNT;
|
||||
break;
|
||||
case TIMER_CFG_A_CAP_TIME:
|
||||
mode_qst = MP_QSTR_EDGE_TIME;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mp_printf(print, "<Timer%u, mode=Timer.%q>", (tim->id + 1), mode_qst);
|
||||
mp_printf(print, "Timer(%u, mode=Timer.%q)", tim->id, mode_qst);
|
||||
}
|
||||
|
||||
/// \method init(mode, *, width)
|
||||
/// Initialise the timer. Initialisation must give the desired mode
|
||||
/// and an optional timer width
|
||||
///
|
||||
/// tim.init(mode=Timer.ONE_SHOT, width=32) # one shot mode
|
||||
/// tim.init(mode=Timer.PERIODIC) # configure in free running periodic mode
|
||||
/// split into two 16-bit independent timers
|
||||
///
|
||||
/// Keyword arguments:
|
||||
///
|
||||
/// - `width` - specifies the width of the timer. Default is 32 bit mode. When in 16 bit mode
|
||||
/// the timer is splitted into 2 independent channels.
|
||||
///
|
||||
STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *tim, 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_mode, MP_ARG_REQUIRED | MP_ARG_INT, },
|
||||
@@ -325,8 +296,7 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *tim, mp_uint_t n_args, co
|
||||
|
||||
// check the mode
|
||||
uint32_t _mode = args[0].u_int;
|
||||
if (_mode != TIMER_CFG_A_ONE_SHOT && _mode != TIMER_CFG_A_PERIODIC && _mode != TIMER_CFG_A_CAP_COUNT &&
|
||||
_mode != TIMER_CFG_A_CAP_TIME && _mode != TIMER_CFG_A_PWM) {
|
||||
if (_mode != TIMER_CFG_A_ONE_SHOT_UP && _mode != TIMER_CFG_A_PERIODIC_UP && _mode != TIMER_CFG_A_PWM) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -336,7 +306,7 @@ STATIC mp_obj_t pyb_timer_init_helper(pyb_timer_obj_t *tim, mp_uint_t n_args, co
|
||||
}
|
||||
bool is16bit = (args[1].u_int == 16);
|
||||
|
||||
if (!is16bit && (_mode != TIMER_CFG_A_ONE_SHOT && _mode != TIMER_CFG_A_PERIODIC)) {
|
||||
if (!is16bit && _mode == TIMER_CFG_A_PWM) {
|
||||
// 32-bit mode is only available when in free running modes
|
||||
goto error;
|
||||
}
|
||||
@@ -352,16 +322,12 @@ error:
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||
}
|
||||
|
||||
/// \classmethod \constructor(id, ...)
|
||||
/// Construct a new timer object of the given id. If additional
|
||||
/// arguments are given, then the timer is initialised by `init(...)`.
|
||||
/// `id` can be 1 to 4
|
||||
STATIC mp_obj_t pyb_timer_make_new(const mp_obj_type_t *type, 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_OBJ_FUN_ARGS_MAX, true);
|
||||
|
||||
// create a new Timer object
|
||||
int32_t timer_idx = mp_obj_get_int(args[0]) - 1;
|
||||
int32_t timer_idx = mp_obj_get_int(args[0]);
|
||||
if (timer_idx < 0 || timer_idx > (PYBTIMER_NUM_TIMERS - 1)) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_resource_not_avaliable));
|
||||
}
|
||||
@@ -379,15 +345,11 @@ STATIC mp_obj_t pyb_timer_make_new(const mp_obj_type_t *type, mp_uint_t n_args,
|
||||
return (mp_obj_t)tim;
|
||||
}
|
||||
|
||||
// \method init()
|
||||
/// initializes the timer
|
||||
STATIC mp_obj_t pyb_timer_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
return pyb_timer_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_timer_init_obj, 1, pyb_timer_init);
|
||||
|
||||
// \method deinit()
|
||||
/// disables the timer
|
||||
STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in) {
|
||||
pyb_timer_obj_t *self = self_in;
|
||||
timer_disable(self);
|
||||
@@ -395,24 +357,6 @@ STATIC mp_obj_t pyb_timer_deinit(mp_obj_t self_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_deinit_obj, pyb_timer_deinit);
|
||||
|
||||
/// \method channel(channel, *, freq, period, polarity, duty_cycle)
|
||||
/// Initialise the timer channel. Initialization requires at least a frequency param. With no
|
||||
/// extra params given besides the channel id, the channel is returned with the previous configuration
|
||||
/// os 'None', if it hasn't been initialized before.
|
||||
///
|
||||
/// tim1.channel(Timer.A, freq=1000) # set channel A frequency to 1KHz
|
||||
/// tim2.channel(Timer.AB, freq=10) # both channels (because it's a 32 bit timer) combined to create a 10Hz timer
|
||||
///
|
||||
/// when initialiazing the channel of a 32-bit timer, channel ID MUST be = Timer.AB
|
||||
///
|
||||
/// Keyword arguments:
|
||||
///
|
||||
/// - `freq` - specifies the frequency in Hz.
|
||||
/// - `period` - specifies the period in microseconds.
|
||||
/// - `polarity` - in PWM specifies the polarity of the pulse. In capture mode specifies the edge to capture.
|
||||
/// in order to capture on both negative and positive edges, make it = Timer.POSITIVE | Timer.NEGATIVE.
|
||||
/// - `duty_cycle` - sets the duty cycle value
|
||||
///
|
||||
STATIC mp_obj_t pyb_timer_channel(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_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||
@@ -474,12 +418,21 @@ STATIC mp_obj_t pyb_timer_channel(mp_uint_t n_args, const mp_obj_t *pos_args, mp
|
||||
ch->frequency = args[0].u_int;
|
||||
ch->period = args[1].u_int;
|
||||
ch->polarity = args[2].u_int;
|
||||
ch->duty_cycle = MIN(100, MAX(0, args[3].u_int));
|
||||
ch->duty_cycle = MIN(10000, MAX(0, args[3].u_int));
|
||||
|
||||
timer_channel_init(ch);
|
||||
|
||||
// register it with the sleep module
|
||||
pyb_sleep_add ((const mp_obj_t)ch, (WakeUpCB_t)timer_channel_init);
|
||||
// assign the pin
|
||||
if ((ch->timer->config & 0x0F) == TIMER_CFG_A_PWM) {
|
||||
uint32_t ch_idx = (ch->channel == TIMER_A) ? 0 : 1;
|
||||
// use the default pin if available
|
||||
mp_obj_t pin_o = (mp_obj_t)pyb_timer_pwm_pin[(ch->timer->id * 2) + ch_idx];
|
||||
if (pin_o != MP_OBJ_NULL) {
|
||||
pin_obj_t *pin = pin_find(pin_o);
|
||||
pin_config (pin, pin_find_af_index(pin, PIN_FN_TIM, ch->timer->id, PIN_TYPE_TIM_PWM),
|
||||
0, PIN_TYPE_STD, -1, PIN_STRENGTH_4MA);
|
||||
}
|
||||
}
|
||||
|
||||
// add the timer to the list
|
||||
pyb_timer_channel_add(ch);
|
||||
@@ -500,13 +453,13 @@ STATIC const mp_map_elem_t pyb_timer_locals_dict_table[] = {
|
||||
// class constants
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_A), MP_OBJ_NEW_SMALL_INT(TIMER_A) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_B), MP_OBJ_NEW_SMALL_INT(TIMER_B) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ONE_SHOT), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_ONE_SHOT) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_PERIODIC), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_PERIODIC) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_EDGE_COUNT), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_CAP_COUNT) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_EDGE_TIME), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_CAP_TIME) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ONE_SHOT), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_ONE_SHOT_UP) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_PERIODIC), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_PERIODIC_UP) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_PWM), MP_OBJ_NEW_SMALL_INT(TIMER_CFG_A_PWM) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_POSITIVE), MP_OBJ_NEW_SMALL_INT(PYBTIMER_POLARITY_POS) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_NEGATIVE), MP_OBJ_NEW_SMALL_INT(PYBTIMER_POLARITY_NEG) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_TIMEOUT), MP_OBJ_NEW_SMALL_INT(PYBTIMER_TIMEOUT_TRIGGER) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MATCH), MP_OBJ_NEW_SMALL_INT(PYBTIMER_MATCH_TRIGGER) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(pyb_timer_locals_dict, pyb_timer_locals_dict_table);
|
||||
|
||||
@@ -528,7 +481,6 @@ STATIC const mp_irq_methods_t pyb_timer_channel_irq_methods = {
|
||||
STATIC void TIMERGenericIntHandler(uint32_t timer, uint16_t channel) {
|
||||
pyb_timer_channel_obj_t *self;
|
||||
uint32_t status;
|
||||
|
||||
if ((self = pyb_timer_channel_find(timer, channel))) {
|
||||
status = MAP_TimerIntStatus(self->timer->timer, true) & self->channel;
|
||||
MAP_TimerIntClear(self->timer->timer, status);
|
||||
@@ -574,16 +526,14 @@ STATIC void pyb_timer_channel_print(const mp_print_t *print, mp_obj_t self_in, m
|
||||
// timer channel
|
||||
if (ch->channel == TIMER_A) {
|
||||
ch_id = "A";
|
||||
}
|
||||
else if (ch->channel == TIMER_B) {
|
||||
} else if (ch->channel == TIMER_B) {
|
||||
ch_id = "B";
|
||||
}
|
||||
|
||||
mp_printf(print, "<%q %s, timer=%u, %q=%u", MP_QSTR_TimerChannel,
|
||||
ch_id, (ch->timer->id + 1), MP_QSTR_freq, ch->frequency);
|
||||
mp_printf(print, "timer.channel(Timer.%s, %q=%u", ch_id, MP_QSTR_freq, ch->frequency);
|
||||
|
||||
uint32_t mode = ch->timer->config & 0xFF;
|
||||
if (mode == TIMER_CFG_A_CAP_COUNT || mode == TIMER_CFG_A_CAP_TIME || mode == TIMER_CFG_A_PWM) {
|
||||
if (mode == TIMER_CFG_A_PWM) {
|
||||
mp_printf(print, ", %q=Timer.", MP_QSTR_polarity);
|
||||
switch (ch->polarity) {
|
||||
case PYBTIMER_POLARITY_POS:
|
||||
@@ -596,15 +546,11 @@ STATIC void pyb_timer_channel_print(const mp_print_t *print, mp_obj_t self_in, m
|
||||
mp_printf(print, "BOTH");
|
||||
break;
|
||||
}
|
||||
if (mode == TIMER_CFG_A_PWM) {
|
||||
mp_printf(print, ", %q=%u", MP_QSTR_duty_cycle, ch->duty_cycle);
|
||||
}
|
||||
mp_printf(print, ", %q=%u.%02u", MP_QSTR_duty_cycle, ch->duty_cycle / 100, ch->duty_cycle % 100);
|
||||
}
|
||||
mp_printf(print, ">");
|
||||
mp_printf(print, ")");
|
||||
}
|
||||
|
||||
/// \method freq([value])
|
||||
/// get or set the frequency of the timer channel
|
||||
STATIC mp_obj_t pyb_timer_channel_freq(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_channel_obj_t *ch = args[0];
|
||||
if (n_args == 1) {
|
||||
@@ -624,8 +570,6 @@ STATIC mp_obj_t pyb_timer_channel_freq(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_freq_obj, 1, 2, pyb_timer_channel_freq);
|
||||
|
||||
/// \method period([value])
|
||||
/// get or set the period of the timer channel in microseconds
|
||||
STATIC mp_obj_t pyb_timer_channel_period(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_channel_obj_t *ch = args[0];
|
||||
if (n_args == 1) {
|
||||
@@ -645,74 +589,17 @@ STATIC mp_obj_t pyb_timer_channel_period(mp_uint_t n_args, const mp_obj_t *args)
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_period_obj, 1, 2, pyb_timer_channel_period);
|
||||
|
||||
/// \method time([value])
|
||||
/// get or set the value of the timer channel in microseconds
|
||||
STATIC mp_obj_t pyb_timer_channel_time(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_channel_obj_t *ch = args[0];
|
||||
uint32_t value;
|
||||
// calculate the period, the prescaler and the match value
|
||||
uint32_t period_c;
|
||||
uint32_t match;
|
||||
(void)compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
value = (ch->channel == TIMER_B) ? HWREG(ch->timer->timer + TIMER_O_TBV) : HWREG(ch->timer->timer + TIMER_O_TAV);
|
||||
// return the current timer value in microseconds
|
||||
// substract value to period since we are always operating in count-down mode
|
||||
uint32_t time_t = (1000 * (period_c - value)) / period_c;
|
||||
return mp_obj_new_int((time_t * 1000) / ch->frequency);
|
||||
}
|
||||
else {
|
||||
// set
|
||||
value = (mp_obj_get_int(args[1]) * ((ch->frequency * period_c) / 1000)) / 1000;
|
||||
if ((value > 0xFFFF) && (ch->timer->config & TIMER_CFG_SPLIT_PAIR)) {
|
||||
// this exceeds the maximum value of a 16-bit timer
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments));
|
||||
}
|
||||
// write period minus value since we are always operating in count-down mode
|
||||
TimerValueSet (ch->timer->timer, ch->channel, (period_c - value));
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_time_obj, 1, 2, pyb_timer_channel_time);
|
||||
|
||||
/// \method event_count()
|
||||
/// get the number of events triggered by the configured edge
|
||||
STATIC mp_obj_t pyb_timer_channel_event_count(mp_obj_t self_in) {
|
||||
pyb_timer_channel_obj_t *ch = self_in;
|
||||
return mp_obj_new_int(MAP_TimerValueGet(ch->timer->timer, ch->channel == (TIMER_A | TIMER_B) ? TIMER_A : ch->channel));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_channel_event_count_obj, pyb_timer_channel_event_count);
|
||||
|
||||
/// \method event_time()
|
||||
/// get the time at which the last event was triggered
|
||||
STATIC mp_obj_t pyb_timer_channel_event_time(mp_obj_t self_in) {
|
||||
pyb_timer_channel_obj_t *ch = self_in;
|
||||
// calculate the period, the prescaler and the match value
|
||||
uint32_t period_c;
|
||||
uint32_t match;
|
||||
(void)compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||
uint32_t value = MAP_TimerValueGet(ch->timer->timer, ch->channel == (TIMER_A | TIMER_B) ? TIMER_A : ch->channel);
|
||||
// substract value to period since we are always operating in count-down mode
|
||||
uint32_t time_t = (1000 * (period_c - value)) / period_c;
|
||||
return mp_obj_new_int((time_t * 1000) / ch->frequency);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_timer_channel_event_time_obj, pyb_timer_channel_event_time);
|
||||
|
||||
/// \method duty_cycle()
|
||||
/// get or set the duty cycle when in PWM mode
|
||||
STATIC mp_obj_t pyb_timer_channel_duty_cycle(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_timer_channel_obj_t *ch = args[0];
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
return mp_obj_new_int(ch->duty_cycle);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// duty cycle must be converted from percentage to ticks
|
||||
// calculate the period, the prescaler and the match value
|
||||
uint32_t period_c;
|
||||
uint32_t match;
|
||||
ch->duty_cycle = MIN(100, MAX(0, mp_obj_get_int(args[1])));
|
||||
ch->duty_cycle = MIN(10000, MAX(0, mp_obj_get_int(args[1])));
|
||||
compute_prescaler_period_and_match_value(ch, &period_c, &match);
|
||||
if (n_args == 3) {
|
||||
// set the new polarity if requested
|
||||
@@ -720,13 +607,12 @@ STATIC mp_obj_t pyb_timer_channel_duty_cycle(mp_uint_t n_args, const mp_obj_t *a
|
||||
MAP_TimerControlLevel(ch->timer->timer, ch->channel, (ch->polarity == PYBTIMER_POLARITY_NEG) ? true : false);
|
||||
}
|
||||
MAP_TimerMatchSet(ch->timer->timer, ch->channel, match);
|
||||
MAP_TimerPrescaleMatchSet(ch->timer->timer, ch->channel, match >> 16);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_timer_channel_duty_cycle_obj, 1, 3, pyb_timer_channel_duty_cycle);
|
||||
|
||||
/// \method irq(trigger, priority, handler, wake)
|
||||
/// FIXME triggers!!
|
||||
STATIC mp_obj_t pyb_timer_channel_irq (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
mp_arg_val_t args[mp_irq_INIT_NUM_ARGS];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, mp_irq_INIT_NUM_ARGS, mp_irq_init_args, args);
|
||||
@@ -741,25 +627,28 @@ STATIC mp_obj_t pyb_timer_channel_irq (mp_uint_t n_args, const mp_obj_t *pos_arg
|
||||
goto invalid_args;
|
||||
}
|
||||
|
||||
// get the trigger
|
||||
uint trigger = mp_obj_get_int(args[0].u_obj);
|
||||
|
||||
// disable the callback first
|
||||
pyb_timer_channel_irq_disable(ch);
|
||||
|
||||
uint8_t shift = (ch->channel == TIMER_B) ? 8 : 0;
|
||||
uint32_t _config = (ch->channel == TIMER_B) ? ((ch->timer->config & TIMER_B) >> 8) : (ch->timer->config & TIMER_A);
|
||||
switch (_config) {
|
||||
case TIMER_CFG_A_ONE_SHOT:
|
||||
case TIMER_CFG_A_PERIODIC:
|
||||
case TIMER_CFG_A_ONE_SHOT_UP:
|
||||
case TIMER_CFG_A_PERIODIC_UP:
|
||||
ch->timer->irq_trigger |= TIMER_TIMA_TIMEOUT << shift;
|
||||
break;
|
||||
case TIMER_CFG_A_CAP_COUNT:
|
||||
ch->timer->irq_trigger |= TIMER_CAPA_MATCH << shift;
|
||||
break;
|
||||
case TIMER_CFG_A_CAP_TIME:
|
||||
ch->timer->irq_trigger |= TIMER_CAPA_EVENT << shift;
|
||||
if (trigger != PYBTIMER_TIMEOUT_TRIGGER) {
|
||||
goto invalid_args;
|
||||
}
|
||||
break;
|
||||
case TIMER_CFG_A_PWM:
|
||||
// special case for the PWM match interrupt
|
||||
ch->timer->irq_trigger |= ((ch->channel & TIMER_A) == TIMER_A) ? TIMER_TIMA_MATCH : TIMER_TIMB_MATCH;
|
||||
if (trigger != PYBTIMER_MATCH_TRIGGER) {
|
||||
goto invalid_args;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -831,9 +720,6 @@ STATIC const mp_map_elem_t pyb_timer_channel_locals_dict_table[] = {
|
||||
// instance methods
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_timer_channel_freq_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_period), (mp_obj_t)&pyb_timer_channel_period_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&pyb_timer_channel_time_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_event_count), (mp_obj_t)&pyb_timer_channel_event_count_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_event_time), (mp_obj_t)&pyb_timer_channel_event_time_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_duty_cycle), (mp_obj_t)&pyb_timer_channel_duty_cycle_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_irq), (mp_obj_t)&pyb_timer_channel_irq_obj },
|
||||
};
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#define MICROPY_FATFS_REENTRANT (1)
|
||||
#define MICROPY_FATFS_TIMEOUT (2500)
|
||||
#define MICROPY_FATFS_SYNC_T SemaphoreHandle_t
|
||||
#define MICROPY_FSUSERMOUNT_ADHOC (1)
|
||||
|
||||
#define MICROPY_STREAMS_NON_BLOCK (1)
|
||||
#define MICROPY_MODULE_WEAK_LINKS (1)
|
||||
|
||||
@@ -279,7 +279,7 @@ Q(CERT_REQUIRED)
|
||||
|
||||
// for network class
|
||||
Q(network)
|
||||
Q(server)
|
||||
Q(Server)
|
||||
Q(init)
|
||||
Q(deinit)
|
||||
Q(login)
|
||||
@@ -365,18 +365,15 @@ Q(width)
|
||||
Q(channel)
|
||||
Q(polarity)
|
||||
Q(duty_cycle)
|
||||
Q(time)
|
||||
Q(event_count)
|
||||
Q(event_time)
|
||||
Q(A)
|
||||
Q(B)
|
||||
Q(ONE_SHOT)
|
||||
Q(PERIODIC)
|
||||
Q(EDGE_COUNT)
|
||||
Q(EDGE_TIME)
|
||||
Q(PWM)
|
||||
Q(POSITIVE)
|
||||
Q(NEGATIVE)
|
||||
Q(TIMEOUT)
|
||||
Q(MATCH)
|
||||
|
||||
// for uhashlib module
|
||||
//Q(uhashlib)
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
#include "pybwdt.h"
|
||||
#include "modusocket.h"
|
||||
#include "mpexception.h"
|
||||
|
||||
#include "modnetwork.h"
|
||||
#include "modwlan.h"
|
||||
|
||||
/******************************************************************************
|
||||
DEFINE PRIVATE TYPES
|
||||
@@ -50,13 +51,13 @@ typedef struct {
|
||||
bool do_disable;
|
||||
bool do_enable;
|
||||
bool do_reset;
|
||||
bool do_wlan_cycle_power;
|
||||
} servers_data_t;
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE PRIVATE DATA
|
||||
******************************************************************************/
|
||||
static servers_data_t servers_data = {.timeout = SERVERS_DEF_TIMEOUT_MS, .enabled = false, .do_disable = false,
|
||||
.do_enable = false, .do_reset = false};
|
||||
static servers_data_t servers_data = {.timeout = SERVERS_DEF_TIMEOUT_MS};
|
||||
static volatile bool sleep_sockets = false;
|
||||
|
||||
/******************************************************************************
|
||||
@@ -120,10 +121,16 @@ void TASK_Servers (void *pvParameters) {
|
||||
}
|
||||
|
||||
if (sleep_sockets) {
|
||||
sleep_sockets = false;
|
||||
pybwdt_srv_sleeping(true);
|
||||
modusocket_enter_sleep();
|
||||
pybwdt_srv_sleeping(false);
|
||||
mp_hal_delay_ms(SERVERS_CYCLE_TIME_MS * 2);
|
||||
if (servers_data.do_wlan_cycle_power) {
|
||||
servers_data.do_wlan_cycle_power = false;
|
||||
wlan_off_on();
|
||||
}
|
||||
sleep_sockets = false;
|
||||
|
||||
}
|
||||
|
||||
// set the alive flag for the wdt
|
||||
@@ -152,6 +159,10 @@ void servers_reset (void) {
|
||||
servers_data.do_reset = true;
|
||||
}
|
||||
|
||||
void servers_wlan_cycle_power (void) {
|
||||
servers_data.do_wlan_cycle_power = true;
|
||||
}
|
||||
|
||||
bool servers_are_enabled (void) {
|
||||
return servers_data.enabled;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ extern void TASK_Servers (void *pvParameters);
|
||||
extern void servers_start (void);
|
||||
extern void servers_stop (void);
|
||||
extern void servers_reset (void);
|
||||
extern void servers_wlan_cycle_power (void);
|
||||
extern bool servers_are_enabled (void);
|
||||
extern void servers_close_socket (int16_t *sd);
|
||||
extern void servers_set_login (char *user, char *pass);
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
#ifndef VERSION_H_
|
||||
#define VERSION_H_
|
||||
|
||||
#define WIPY_SW_VERSION_NUMBER "1.1.1"
|
||||
#define WIPY_SW_VERSION_NUMBER "1.2.0"
|
||||
|
||||
#endif /* VERSION_H_ */
|
||||
|
||||
33
docs/conf.py
33
docs/conf.py
@@ -26,18 +26,29 @@ from collections import OrderedDict
|
||||
micropy_port = os.getenv('MICROPY_PORT') or 'pyboard'
|
||||
tags.add('port_' + micropy_port)
|
||||
ports = OrderedDict((
|
||||
("unix", "unix"),
|
||||
("pyboard", "the pyboard"),
|
||||
("wipy", "the WiPy"),
|
||||
("esp8266", "esp8266"),
|
||||
('unix', ('unix', 'unix')),
|
||||
('pyboard', ('pyboard', 'the pyboard')),
|
||||
('wipy', ('WiPy', 'the WiPy')),
|
||||
('esp8266', ('ESP8266', 'the ESP8266')),
|
||||
))
|
||||
|
||||
# The members of the html_context dict are available inside topindex.html
|
||||
url_prefix = os.getenv('MICROPY_URL_PREFIX') or '/'
|
||||
micropy_version = os.getenv('MICROPY_VERSION') or 'latest'
|
||||
micropy_all_versions = (os.getenv('MICROPY_ALL_VERSIONS') or 'latest').split(',')
|
||||
url_pattern = '%s/en/%%s/%%s' % (os.getenv('MICROPY_URL_PREFIX') or '/',)
|
||||
html_context = {
|
||||
'port':micropy_port,
|
||||
'port_name':ports[micropy_port],
|
||||
'all_ports':[(n, url_prefix + p) for p, n in ports.items()],
|
||||
'port_short_name':ports[micropy_port][0],
|
||||
'port_name':ports[micropy_port][1],
|
||||
'port_version':micropy_version,
|
||||
'all_ports':[
|
||||
(port_name[0], url_pattern % (micropy_version, port_id))
|
||||
for port_id, port_name in ports.items()
|
||||
],
|
||||
'all_versions':[
|
||||
(ver, url_pattern % (ver, micropy_port))
|
||||
for ver in micropy_all_versions
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -74,16 +85,16 @@ source_suffix = '.rst'
|
||||
|
||||
# General information about the project.
|
||||
project = 'MicroPython'
|
||||
copyright = '2014, Damien P. George'
|
||||
copyright = '2014-2016, Damien P. George and contributors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.6'
|
||||
version = '1.7'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.6'
|
||||
release = '1.7'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -306,5 +317,3 @@ exclude_patterns.extend([port + '*' for port in ports if port != micropy_port])
|
||||
# Exclude pyb module if the port is the WiPy
|
||||
if micropy_port == 'wipy':
|
||||
exclude_patterns.append('library/pyb*')
|
||||
else: # exclude machine
|
||||
exclude_patterns.append('library/machine*')
|
||||
|
||||
254
docs/esp8266/quickref.rst
Normal file
254
docs/esp8266/quickref.rst
Normal file
@@ -0,0 +1,254 @@
|
||||
.. _quickref:
|
||||
|
||||
Quick reference for the ESP8266
|
||||
===============================
|
||||
|
||||
.. image:: https://learn.adafruit.com/system/assets/assets/000/028/689/medium640/adafruit_products_pinoutstop.jpg
|
||||
:alt: Adafruit Feather HUZZAH board
|
||||
:width: 640px
|
||||
|
||||
The Adafruit Feather HUZZAH board (image attribution: Adafruit).
|
||||
|
||||
General board control
|
||||
---------------------
|
||||
|
||||
The MicroPython REPL is on UART0 (GPIO1=TX, GPIO3=RX) at baudrate 115200.
|
||||
Tab-completion is useful to find out what methods an object has.
|
||||
Paste mode (ctrl-E) is useful to paste a large slab of Python code into
|
||||
the REPL.
|
||||
|
||||
The ``machine`` module::
|
||||
|
||||
import machine
|
||||
|
||||
machine.freq() # get the current frequency of the CPU
|
||||
machine.freq(160000000) # set the CPU frequency to 160 MHz
|
||||
|
||||
The ``esp`` module::
|
||||
|
||||
import esp
|
||||
|
||||
esp.osdebug(None) # turn off vendor O/S debugging messages
|
||||
esp.osdebug(0) # redirect vendor O/S debugging messages to UART(0)
|
||||
|
||||
Networking
|
||||
----------
|
||||
|
||||
The ``network`` module::
|
||||
|
||||
import network
|
||||
|
||||
wlan = network.WLAN(network.STA_IF) # create station interface
|
||||
wlan.active(True) # activate the interface
|
||||
wlan.scan() # scan for access points
|
||||
wlan.isconnected() # check if the station is connected to an AP
|
||||
wlan.connect('essid', 'password') # connect to an AP
|
||||
wlan.mac() # get the interface's MAC adddress
|
||||
wlan.ifconfig() # get the interface's IP/netmask/gw/DNS addresses
|
||||
|
||||
ap = network.WLAN(network.AP_IF) # create access-point interface
|
||||
ap.active(True) # activate the interface
|
||||
ap.config(essid='ESP-AP') # set the ESSID of the access point
|
||||
|
||||
A useful function for connecting to your local WiFi network is::
|
||||
|
||||
def do_connect():
|
||||
import network
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
if not wlan.isconnected():
|
||||
print('connecting to network...')
|
||||
wlan.connect('essid', 'password')
|
||||
while not wlan.isconnected():
|
||||
pass
|
||||
print('network config:', wlan.ifconfig())
|
||||
|
||||
Once the network is established the ``socket`` module can be used
|
||||
to create and use TCP/UDP sockets as usual.
|
||||
|
||||
Delay and timing
|
||||
----------------
|
||||
|
||||
Use the ``time`` module::
|
||||
|
||||
import time
|
||||
|
||||
time.sleep(1) # sleep for 1 second
|
||||
time.sleep_ms(500) # sleep for 500 milliseconds
|
||||
time.sleep_us(10) # sleep for 10 microseconds
|
||||
start = time.ticks_ms() # get millisecond counter
|
||||
delta = time.ticks_diff(start, time.ticks_ms()) # compute time difference
|
||||
|
||||
Timers
|
||||
------
|
||||
|
||||
Virtual (RTOS-based) timers are supported. Use the ``machine.Timer`` class
|
||||
with timer ID of -1::
|
||||
|
||||
from machine import Timer
|
||||
|
||||
tim = Timer(-1)
|
||||
tim.init(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1))
|
||||
tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2))
|
||||
|
||||
The period is in milliseconds.
|
||||
|
||||
Pins and GPIO
|
||||
-------------
|
||||
|
||||
Use the ``machine.Pin`` class::
|
||||
|
||||
from machine import Pin
|
||||
|
||||
p0 = Pin(0, Pin.OUT) # create output pin on GPIO0
|
||||
p0.high() # set pin to high
|
||||
p0.low() # set pin to low
|
||||
p0.value(1) # set pin to high
|
||||
|
||||
p2 = Pin(2, Pin.IN) # create input pin on GPIO2
|
||||
print(p2.value()) # get value, 0 or 1
|
||||
|
||||
p4 = Pin(4, Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor
|
||||
p5 = Pin(5, Pin.OUT, value=1) # set pin high on creation
|
||||
|
||||
Available pins are: 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, which correspond
|
||||
to the actual GPIO pin numbers of ESP8266 chip. Note that many end-user
|
||||
boards use their own adhoc pin numbering (marked e.g. D0, D1, ...). As
|
||||
MicroPython supports different boards and modules, physical pin numbering
|
||||
was chosen as the lowest common denominator. For mapping between board
|
||||
logical pins and physical chip pins, consult your board documentation.
|
||||
|
||||
Note that Pin(1) and Pin(3) are REPL UART TX and RX respectively.
|
||||
Also note that Pin(16) is a special pin (used for wakeup from deepsleep
|
||||
mode) and may be not available for use with higher-level classes like
|
||||
``Neopixel``.
|
||||
|
||||
PWM (pulse width modulation)
|
||||
----------------------------
|
||||
|
||||
PWM can be enabled on all pins except Pin(16). There is a single frequency
|
||||
for all channels, with range between 1 and 1000 (measured in Hz). The duty
|
||||
cycle is between 0 and 1023 inclusive.
|
||||
|
||||
Use the ``machine.PWM`` class::
|
||||
|
||||
from machine import Pin, PWM
|
||||
|
||||
pwm0 = PWM(Pin(0)) # create PWM object from a pin
|
||||
pwm0.freq() # get current frequency
|
||||
pwm0.freq(1000) # set frequency
|
||||
pwm0.duty() # get current duty cycle
|
||||
pwm0.duty(200) # set duty cycle
|
||||
pwm0.deinit() # turn off PWM on the pin
|
||||
|
||||
pwm2 = PWM(Pin(2), freq=500, duty=512) # create and configure in one go
|
||||
|
||||
ADC (analog to digital conversion)
|
||||
----------------------------------
|
||||
|
||||
ADC is available on a dedicated pin.
|
||||
Note that input voltages on the ADC pin must be between 0v and 1.0v.
|
||||
|
||||
Use the ``machine.ADC`` class::
|
||||
|
||||
from machine import ADC
|
||||
|
||||
adc = ADC(0) # create ADC object on ADC pin
|
||||
adc.read() # read value, 0-1024
|
||||
|
||||
SPI bus
|
||||
-------
|
||||
|
||||
The SPI driver is implemented in software and works on all pins::
|
||||
|
||||
from machine import Pin, SPI
|
||||
|
||||
# construct an SPI bus on the given pins
|
||||
# polarity is the idle state of SCK
|
||||
# phase=0 means sample on the first edge of SCK, phase=1 means the second
|
||||
spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4))
|
||||
|
||||
spi.init(baudrate=200000) # set the baudrate
|
||||
|
||||
spi.read(10) # read 10 bytes on MISO
|
||||
spi.read(10, 0xff) # read 10 bytes while outputing 0xff on MOSI
|
||||
|
||||
buf = bytearray(50) # create a buffer
|
||||
spi.readinto(buf) # read into the given buffer (reads 50 bytes in this case)
|
||||
spi.readinto(buf, 0xff) # read into the given buffer and output 0xff on MOSI
|
||||
|
||||
spi.write(b'12345') # write 5 bytes on MOSI
|
||||
|
||||
buf = bytearray(4) # create a buffer
|
||||
spi.write_readinto(b'1234', buf) # write to MOSI and read from MISO into the buffer
|
||||
spi.write_readinto(buf, buf) # write buf to MOSI and read MISO back into buf
|
||||
|
||||
I2C bus
|
||||
-------
|
||||
|
||||
The I2C driver is implemented in software and works on all pins::
|
||||
|
||||
from machine import Pin, I2C
|
||||
|
||||
# construct an I2C bus
|
||||
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)
|
||||
|
||||
i2c.readfrom(0x3a, 4) # read 4 bytes from slave device with address 0x3a
|
||||
i2c.writeto(0x3a, '12') # write '12' to slave device with address 0x3a
|
||||
|
||||
buf = bytearray(10) # create a buffer with 10 bytes
|
||||
i2c.writeto(0x3a, buf) # write the given buffer to the slave
|
||||
|
||||
i2c.readfrom(0x3a, 4, stop=False) # don't send a stop bit after reading
|
||||
i2c.writeto(0x3a, buf, stop=False) # don't send a stop bit after writing
|
||||
|
||||
OneWire driver
|
||||
--------------
|
||||
|
||||
The OneWire driver is implemented in software and works on all pins::
|
||||
|
||||
from machine import Pin
|
||||
import onewire
|
||||
|
||||
ow = onewire.OneWire(Pin(12)) # create a OneWire bus on GPIO12
|
||||
ow.scan() # return a list of devices on the bus
|
||||
ow.reset() # reset the bus
|
||||
ow.read_byte() # read a byte
|
||||
ow.read_bytes(5) # read 5 bytes
|
||||
ow.write_byte(0x12) # write a byte on the bus
|
||||
ow.write_bytes('123') # write bytes on the bus
|
||||
ow.select_rom(b'12345678') # select a specific device by its ROM code
|
||||
|
||||
There is a specific driver for DS18B20 devices::
|
||||
|
||||
import time
|
||||
ds = onewire.DS18B20(ow)
|
||||
roms = ds.scan()
|
||||
ds.start_measure()
|
||||
time.sleep_ms(750)
|
||||
for rom in roms:
|
||||
print(ds.get_temp(rom))
|
||||
|
||||
Be sure to put a 4.7k pull-up resistor on the data line.
|
||||
|
||||
NeoPixel driver
|
||||
---------------
|
||||
|
||||
Use the ``neopixel`` module::
|
||||
|
||||
from machine import Pin
|
||||
from neopixel import NeoPixel
|
||||
|
||||
pin = Pin(0, Pin.OUT) # set GPIO0 to output to drive NeoPixels
|
||||
np = NeoPixel(pin, 8) # create NeoPixel driver on GPIO0 for 8 pixels
|
||||
np[0] = (255, 255, 255) # set the first pixel to white
|
||||
np.write() # write data to all pixels
|
||||
r, g, b = np[0] # get first pixel colour
|
||||
|
||||
import neopixel
|
||||
neopixel.demo(np) # run a demo
|
||||
|
||||
For low-level driving of a NeoPixel::
|
||||
|
||||
import esp
|
||||
esp.neopixel_write(pin, grb_buf, is800khz)
|
||||
@@ -3,6 +3,7 @@ MicroPython documentation contents
|
||||
|
||||
.. toctree::
|
||||
|
||||
esp8266/quickref.rst
|
||||
library/index.rst
|
||||
reference/index.rst
|
||||
license.rst
|
||||
|
||||
@@ -3,6 +3,7 @@ MicroPython documentation and references
|
||||
|
||||
.. toctree::
|
||||
|
||||
esp8266/quickref.rst
|
||||
library/index.rst
|
||||
license.rst
|
||||
esp8266_contents.rst
|
||||
|
||||
@@ -10,46 +10,6 @@ The ``esp`` module contains specific functions related to the ESP8266 module.
|
||||
Functions
|
||||
---------
|
||||
|
||||
.. function:: mac([address])
|
||||
|
||||
Get or set the network interface's MAC address.
|
||||
|
||||
If the ``address`` parameter is provided, sets the address to its value. If
|
||||
the function is called wihout parameters, returns the current address.
|
||||
|
||||
.. function:: getaddrinfo((hostname, port, lambda))
|
||||
|
||||
Initiate resolving of the given hostname.
|
||||
|
||||
When the hostname is resolved, the provided ``lambda`` callback will be
|
||||
called with two arguments, first being the hostname being resolved,
|
||||
second a tuple with information about that hostname.
|
||||
|
||||
.. function:: wifi_mode([mode])
|
||||
|
||||
Get or set the wireless network operating mode.
|
||||
|
||||
If the ``mode`` parameter is provided, sets the mode to its value. If
|
||||
the function is called wihout parameters, returns the current mode.
|
||||
|
||||
The possible modes are defined as constants:
|
||||
|
||||
* ``STA_MODE`` -- station mode,
|
||||
* ``AP_MODE`` -- software access point mode,
|
||||
* ``STA_AP_MODE`` -- mixed station and software access point mode.
|
||||
|
||||
.. function:: phy_mode([mode])
|
||||
|
||||
Get or set the network interface mode.
|
||||
|
||||
If the ``mode`` parameter is provided, sets the mode to its value. If
|
||||
the function is called wihout parameters, returns the current mode.
|
||||
|
||||
The possible modes are defined as constants:
|
||||
* ``MODE_11B`` -- IEEE 802.11b,
|
||||
* ``MODE_11G`` -- IEEE 802.11g,
|
||||
* ``MODE_11N`` -- IEEE 802.11n.
|
||||
|
||||
.. function:: sleep_type([sleep_type])
|
||||
|
||||
Get or set the sleep type.
|
||||
@@ -79,11 +39,3 @@ Functions
|
||||
.. function:: flash_id()
|
||||
|
||||
Read the device ID of the flash memory.
|
||||
|
||||
Classes
|
||||
-------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
esp.socket.rst
|
||||
|
||||
@@ -55,6 +55,17 @@ For additional libraries, please download them from the `micropython-lib reposit
|
||||
sys.rst
|
||||
time.rst
|
||||
|
||||
.. only:: port_esp8266
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
gc.rst
|
||||
math.rst
|
||||
struct.rst
|
||||
sys.rst
|
||||
time.rst
|
||||
|
||||
Python micro-libraries
|
||||
----------------------
|
||||
|
||||
@@ -85,6 +96,19 @@ library.
|
||||
usocket.rst
|
||||
uzlib.rst
|
||||
|
||||
.. only:: port_esp8266
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ubinascii.rst
|
||||
uctypes.rst
|
||||
uhashlib.rst
|
||||
uheapq.rst
|
||||
ujson.rst
|
||||
ure.rst
|
||||
uzlib.rst
|
||||
|
||||
.. only:: port_pyboard
|
||||
|
||||
Libraries specific to the pyboard
|
||||
@@ -134,6 +158,6 @@ library.
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
pyb.rst
|
||||
esp.rst
|
||||
network.rst
|
||||
esp.rst
|
||||
machine.rst
|
||||
|
||||
@@ -24,7 +24,7 @@ Usage Model:
|
||||
print(pin.id())
|
||||
|
||||
pin_int = Pin('GP10', mode=Pin.IN, pull=Pin.PULL_DOWN)
|
||||
pin_int.irq(mode=Pin.IRQ_RISING, handler=pincb)
|
||||
pin_int.irq(trigger=Pin.IRQ_RISING, handler=pincb)
|
||||
# the callback can be triggered manually
|
||||
pin_int.irq()()
|
||||
# to disable the callback
|
||||
|
||||
@@ -5,55 +5,49 @@ class Timer -- control internal timers
|
||||
|
||||
.. only:: port_wipy
|
||||
|
||||
.. note::
|
||||
|
||||
Contrary with the rest of the API, timer IDs start at 1, not a t zero. This is because
|
||||
the ``Timer`` API is still provisional. A new MicroPython wide API will come soon.
|
||||
|
||||
Timers can be used for a great variety of tasks, calling a function periodically,
|
||||
counting events, and generating a PWM signal are among the most common use cases.
|
||||
Each timer consists of 2 16-bit channels and this channels can be tied together to
|
||||
form 1 32-bit timer. The operating mode needs to be configured per timer, but then
|
||||
Each timer consists of two 16-bit channels and this channels can be tied together to
|
||||
form one 32-bit timer. The operating mode needs to be configured per timer, but then
|
||||
the period (or the frequency) can be independently configured on each channel.
|
||||
By using the callback method, the timer event can call a Python function.
|
||||
|
||||
Example usage to toggle an LED at a fixed frequency::
|
||||
|
||||
from machine import Timer
|
||||
tim = Timer(4) # create a timer object using timer 4
|
||||
from machine import Pin
|
||||
led = Pin('GP16', mode=Pin.OUT) # enable GP16 as output to drive the LED
|
||||
tim = Timer(3) # create a timer object using timer 3
|
||||
tim.init(mode=Timer.PERIODIC) # initialize it in periodic mode
|
||||
tim_ch = tim.channel(Timer.A, freq=2) # configure channel A at a frequency of 2Hz
|
||||
tim_ch.callback(handler=lambda t:led.toggle()) # toggle a LED on every cycle of the timer
|
||||
tim_ch = tim.channel(Timer.A, freq=5) # configure channel A at a frequency of 5Hz
|
||||
tim_ch.irq(handler=lambda t:led.toggle(), trigger=Timer.TIMEOUT) # toggle a LED on every cycle of the timer
|
||||
|
||||
Example using named function for the callback::
|
||||
|
||||
from machine import Timer
|
||||
tim = Timer(1, mode=Timer.PERIODIC)
|
||||
tim_a = tim.channel(Timer.A, freq=1000)
|
||||
from machine import Pin
|
||||
tim = Timer(1, mode=Timer.PERIODIC, width=32)
|
||||
tim_a = tim.channel(Timer.A | Timer.B, freq=1) # 1 Hz frequency requires a 32 bit timer
|
||||
|
||||
led = Pin('GPIO2', mode=Pin.OUT)
|
||||
led = Pin('GP16', mode=Pin.OUT) # enable GP16 as output to drive the LED
|
||||
|
||||
def tick(timer): # we will receive the timer object when being called
|
||||
print(timer.time()) # show current timer's time value (is microseconds)
|
||||
global led
|
||||
led.toggle() # toggle the LED
|
||||
|
||||
tim_a.callback(handler=tick)
|
||||
tim_a.irq(handler=tick, trigger=Timer.TIMEOUT) # create the interrupt
|
||||
|
||||
Further examples::
|
||||
|
||||
from machine import Timer
|
||||
tim1 = Timer(2, mode=Timer.EVENT_COUNT) # initialize it capture mode
|
||||
tim2 = Timer(1, mode=Timer.PWM) # initialize it in PWM mode
|
||||
tim_ch = tim1.channel(Timer.A, freq=1, polarity=Timer.POSITIVE) # start the event counter with a frequency of 1Hz and triggered by positive edges
|
||||
tim_ch = tim2.channel(Timer.B, freq=10000, duty_cycle=50) # start the PWM on channel B with a 50% duty cycle
|
||||
tim_ch.time() # get the current time in usec (can also be set)
|
||||
tim_ch.freq(20) # set the frequency (can also get)
|
||||
tim_ch.duty_cycle(30) # set the duty cycle to 30% (can also get)
|
||||
tim_ch.duty_cycle(30, Timer.NEGATIVE) # set the duty cycle to 30% and change the polarity to negative
|
||||
tim_ch.event_count() # get the number of captured events
|
||||
tim_ch.event_time() # get the the time of the last captured event
|
||||
tim_ch.period(2000000) # change the period to 2 seconds
|
||||
|
||||
tim1 = Timer(1, mode=Timer.ONE_SHOT) # initialize it in one shot mode
|
||||
tim2 = Timer(2, mode=Timer.PWM) # initialize it in PWM mode
|
||||
tim1_ch = tim1.channel(Timer.A, freq=10, polarity=Timer.POSITIVE) # start the event counter with a frequency of 10Hz and triggered by positive edges
|
||||
tim2_ch = tim2.channel(Timer.B, freq=10000, duty_cycle=5000) # start the PWM on channel B with a 50% duty cycle
|
||||
tim2_ch.freq(20) # set the frequency (can also get)
|
||||
tim2_ch.duty_cycle(3010) # set the duty cycle to 30.1% (can also get)
|
||||
tim2_ch.duty_cycle(3020, Timer.NEGATIVE) # set the duty cycle to 30.2% and change the polarity to negative
|
||||
tim2_ch.period(2000000) # change the period to 2 seconds
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -69,9 +63,7 @@ Constructors
|
||||
|
||||
.. only:: port_wipy
|
||||
|
||||
Construct a new timer object of the given id. If additional
|
||||
arguments are given, then the timer is initialised by ``init(...)``.
|
||||
``id`` can be 1 to 4.
|
||||
Construct a new timer object of the given id. ``id`` can take values from 0 to 3.
|
||||
|
||||
|
||||
Methods
|
||||
@@ -94,10 +86,9 @@ Methods
|
||||
period of the channel expires.
|
||||
- ``Timer.PERIODIC`` - The timer runs periodically at the configured
|
||||
frequency of the channel.
|
||||
- ``Timer.EDGE_TIME`` - Meaure the time pin level changes.
|
||||
- ``Timer.EDGE_COUNT`` - Count the number of pin level changes.
|
||||
- ``Timer.PWM`` - Output a PWM signal on a pin.
|
||||
|
||||
- ``width`` must be either 16 or 32 (bits). For really low frequencies <= ~1Hz
|
||||
- ``width`` must be either 16 or 32 (bits). For really low frequencies < 5Hz
|
||||
(or large periods), 32-bit timers should be used. 32-bit mode is only available
|
||||
for ``ONE_SHOT`` AND ``PERIODIC`` modes.
|
||||
|
||||
@@ -112,7 +103,7 @@ Methods
|
||||
|
||||
If only a channel identifier passed, then a previously initialized channel
|
||||
object is returned (or ``None`` if there is no previous channel).
|
||||
|
||||
|
||||
Othwerwise, a TimerChannel object is initialized and returned.
|
||||
|
||||
The operating mode is is the one configured to the Timer object that was used to
|
||||
@@ -130,12 +121,22 @@ Methods
|
||||
|
||||
Either ``freq`` or ``period`` must be given, never both.
|
||||
|
||||
- ``polarity`` this is applicable for:
|
||||
|
||||
- ``PWM``, defines the polarity of the duty cycle
|
||||
- ``EDGE_TIME`` and ``EDGE_COUNT``, defines the polarity of the pin level change to detect.
|
||||
To detect both rising and falling edges, make ``polarity=Timer.POSITIVE | Timer.NEGATIVE``.
|
||||
- ``duty_cycle`` only applicable to ``PWM``. It's a percentage (0-100)
|
||||
- ``polarity`` this is applicable for ``PWM``, and defines the polarity of the duty cycle
|
||||
- ``duty_cycle`` only applicable to ``PWM``. It's a percentage (0.00-100.00). Since the WiPy
|
||||
doesn't support floating point numbers the duty cycle must be specified in the range 0-10000,
|
||||
where 10000 would represent 100.00, 5050 represents 50.50, and so on.
|
||||
|
||||
.. note::
|
||||
|
||||
When the channel is in PWM mode, the corresponding pin is assigned automatically, therefore
|
||||
there's no need to assign the alternate function of the pin via the ``Pin`` class. The pins which
|
||||
support PWM functionality are the following:
|
||||
|
||||
- ``GP24`` on Timer 0 channel A.
|
||||
- ``GP25`` on Timer 1 channel A.
|
||||
- ``GP9`` on Timer 2 channel B.
|
||||
- ``GP10`` on Timer 3 channel A.
|
||||
- ``GP11`` on Timer 3 channel B.
|
||||
|
||||
class TimerChannel --- setup a channel for a timer
|
||||
==================================================
|
||||
@@ -166,31 +167,49 @@ Methods
|
||||
- ``priority`` level of the interrupt. Can take values in the range 1-7.
|
||||
Higher values represent higher priorities.
|
||||
- ``handler`` is an optional function to be called when the interrupt is triggered.
|
||||
- ``trigger`` must be ``Timer.TIMEOUT`` when the operating mode is either ``Timer.PERIODIC`` or
|
||||
``Timer.ONE_SHOT``. In the case that mode is ``Timer.PWM`` then trigger must be equal to
|
||||
``Timer.MATCH``.
|
||||
|
||||
Returns a callback object.
|
||||
|
||||
.. only:: port_wipy
|
||||
|
||||
.. method:: timerchannel.freq([value])
|
||||
|
||||
|
||||
Get or set the timer channel frequency (in Hz).
|
||||
|
||||
.. method:: timerchannel.period([value])
|
||||
|
||||
Get or set the timer channel period (in microseconds).
|
||||
|
||||
.. method:: timerchannel.time([value])
|
||||
|
||||
Get or set the timer channel current **time** value (in microseconds).
|
||||
|
||||
.. method:: timerchannel.event_count()
|
||||
|
||||
Get the number of edge events counted.
|
||||
|
||||
.. method:: timerchannel.event_time()
|
||||
|
||||
Get the time of ocurrance of the last event.
|
||||
|
||||
.. method:: timerchannel.duty_cycle([value])
|
||||
|
||||
Get or set the duty cycle of the PWM signal (in the range of 0-100).
|
||||
|
||||
Get or set the duty cycle of the PWM signal. It's a percentage (0.00-100.00). Since the WiPy
|
||||
doesn't support floating point numbers the duty cycle must be specified in the range 0-10000,
|
||||
where 10000 would represent 100.00, 5050 represents 50.50, and so on.
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
.. data:: Timer.ONE_SHOT
|
||||
.. data:: Timer.PERIODIC
|
||||
.. data:: Timer.PWM
|
||||
|
||||
Selects the timer operating mode.
|
||||
|
||||
.. data:: Timer.A
|
||||
.. data:: Timer.B
|
||||
|
||||
Selects the timer channel. Must be ORed (``Timer.A`` | ``Timer.B``) when
|
||||
using a 32-bit timer.
|
||||
|
||||
.. data:: Timer.POSITIVE
|
||||
.. data:: Timer.NEGATIVE
|
||||
|
||||
Timer channel polarity selection (only relevant in PWM mode).
|
||||
|
||||
.. data:: Timer.TIMEOUT
|
||||
.. data:: Timer.MATCH
|
||||
|
||||
Timer channel IRQ triggers.
|
||||
|
||||
@@ -11,7 +11,7 @@ Reset related functions
|
||||
|
||||
.. function:: reset()
|
||||
|
||||
Resets the WiPy in a manner similar to pushing the external RESET
|
||||
Resets the device in a manner similar to pushing the external RESET
|
||||
button.
|
||||
|
||||
.. function:: reset_cause()
|
||||
@@ -87,7 +87,7 @@ Miscellaneous functions
|
||||
.. function:: unique_id()
|
||||
|
||||
Returns a string of 6 bytes (48 bits), which is the unique ID of the MCU.
|
||||
This also corresponds to the ``MAC address`` of the WiPy.
|
||||
This also corresponds to the network ``MAC address``.
|
||||
|
||||
.. _machine_constants:
|
||||
|
||||
|
||||
@@ -30,27 +30,27 @@ For example::
|
||||
|
||||
.. only:: port_wipy
|
||||
|
||||
.. _network.server:
|
||||
.. _network.Server:
|
||||
|
||||
class server
|
||||
class Server
|
||||
============
|
||||
|
||||
The server class controls the behaviour and the configuration of the FTP and telnet
|
||||
The ``Server`` class controls the behaviour and the configuration of the FTP and telnet
|
||||
services running on the WiPy. Any changes performed using this class' methods will
|
||||
affect both.
|
||||
|
||||
Example::
|
||||
|
||||
import network
|
||||
s = network.server()
|
||||
s.deinit() # disable the server
|
||||
server = network.Server()
|
||||
server.deinit() # disable the server
|
||||
# enable the server again with new settings
|
||||
s.init(login=('user', 'password'), timeout=600)
|
||||
server.init(login=('user', 'password'), timeout=600)
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
.. class:: network.server(id, ...)
|
||||
.. class:: network.Server(id, ...)
|
||||
|
||||
Create a server instance, see ``init`` for parameters of initialization.
|
||||
|
||||
@@ -229,26 +229,52 @@ For example::
|
||||
|
||||
.. only:: port_esp8266
|
||||
|
||||
Functions
|
||||
=========
|
||||
|
||||
.. function:: phy_mode([mode])
|
||||
|
||||
Get or set the PHY mode.
|
||||
|
||||
If the ``mode`` parameter is provided, sets the mode to its value. If
|
||||
the function is called wihout parameters, returns the current mode.
|
||||
|
||||
The possible modes are defined as constants:
|
||||
* ``MODE_11B`` -- IEEE 802.11b,
|
||||
* ``MODE_11G`` -- IEEE 802.11g,
|
||||
* ``MODE_11N`` -- IEEE 802.11n.
|
||||
|
||||
class WLAN
|
||||
==========
|
||||
|
||||
This class provides a driver for WiFi network processor in the ESP8266. Example usage::
|
||||
|
||||
import network
|
||||
# setup as a station
|
||||
nic = network.WLAN()
|
||||
# enable station interface and connect to WiFi access point
|
||||
nic = network.WLAN(network.STA_IF)
|
||||
nic.active(True)
|
||||
nic.connect('your-ssid', 'your-password')
|
||||
# now use socket as usual
|
||||
# now use sockets as usual
|
||||
|
||||
Constructors
|
||||
------------
|
||||
.. class:: WLAN()
|
||||
.. class:: WLAN(interface_id)
|
||||
|
||||
Create a WLAN driver object.
|
||||
Create a WLAN network interface object. Supported interfaces are
|
||||
``network.STA_IF`` (station aka client, connects to upstream WiFi access
|
||||
points) and ``network.AP_IF`` (access point, allows other WiFi clients to
|
||||
connect). Availability of the methods below depends on interface type.
|
||||
For example, only STA interface may ``connect()`` to an access point.
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
.. method:: wlan.active([is_active])
|
||||
|
||||
Activate ("up") or deactivate ("down") network interface, if boolean
|
||||
argument is passed. Otherwise, query current state if no argument is
|
||||
provided. Most other methods require active interface.
|
||||
|
||||
.. method:: wlan.connect(ssid, password)
|
||||
|
||||
Connect to the specified wireless network, using the specified password.
|
||||
@@ -257,16 +283,20 @@ For example::
|
||||
|
||||
Disconnect from the currently connected wireless network.
|
||||
|
||||
.. method:: wlan.scan(cb)
|
||||
.. method:: wlan.mac([address])
|
||||
|
||||
Initiate scanning for the available wireless networks.
|
||||
Get or set the network interface MAC address.
|
||||
|
||||
Scanning is only possible if the radio is in station or station+AP mode; if
|
||||
called while in AP only mode, an OSError exception will be raised.
|
||||
If the ``address`` parameter is provided, sets the address to its
|
||||
value, which should be bytes object of length 6. If the function
|
||||
is called wihout parameters, returns the current address.
|
||||
|
||||
Once the scanning is complete, the provided callback function ``cb`` will
|
||||
be called once for each network found, and passed a tuple with information
|
||||
about that network:
|
||||
.. method:: wlan.scan()
|
||||
|
||||
Scan for the available wireless networks.
|
||||
|
||||
Scanning is only possible on STA interface. Returns list of tuples with
|
||||
the information about WiFi access points:
|
||||
|
||||
(ssid, bssid, channel, RSSI, authmode, hidden)
|
||||
|
||||
@@ -283,7 +313,7 @@ For example::
|
||||
* 0 -- visible
|
||||
* 1 -- hidden
|
||||
|
||||
.. method:: status()
|
||||
.. method:: wlan.status()
|
||||
|
||||
Return the current status of the wireless connection.
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
.. module:: os
|
||||
:synopsis: basic "operating system" services
|
||||
|
||||
The ``os`` module contains functions for filesystem access and ``urandom``.
|
||||
The ``os`` module contains functions for filesystem access and ``urandom``
|
||||
function.
|
||||
|
||||
Pyboard specifics
|
||||
-----------------
|
||||
Port specifics
|
||||
--------------
|
||||
|
||||
The filesystem on the pyboard has ``/`` as the root directory and the
|
||||
The filesystem has ``/`` as the root directory and the
|
||||
available physical drives are accessible from here. They are currently:
|
||||
|
||||
``/flash`` -- the internal flash filesystem
|
||||
|
||||
@@ -18,6 +18,7 @@ class ADC -- analog to digital conversion
|
||||
val = adc.read_core_vbat() # read MCU VBAT
|
||||
val = adc.read_core_vref() # read MCU VREF
|
||||
|
||||
|
||||
Constructors
|
||||
------------
|
||||
|
||||
@@ -77,6 +78,65 @@ Methods
|
||||
The ADCAll Object
|
||||
-----------------
|
||||
|
||||
Instantiating this changes all ADC pins to analog inputs. It is possible to read the
|
||||
MCU temperature, VREF and VBAT without using ADCAll. The raw data can be accessed on
|
||||
ADC channels 16, 17 and 18 respectively. However appropriate scaling will need to be applied.
|
||||
.. only:: port_pyboard
|
||||
|
||||
Instantiating this changes all ADC pins to analog inputs. The raw MCU temperature,
|
||||
VREF and VBAT data can be accessed on ADC channels 16, 17 and 18 respectively.
|
||||
Appropriate scaling will need to be applied. The temperature sensor on the chip
|
||||
has poor absolute accuracy and is suitable only for detecting temperature changes.
|
||||
|
||||
The ``ADCAll`` ``read_core_vbat()`` and ``read_core_vref()`` methods read
|
||||
the backup battery voltage and the (1.21V nominal) reference voltage using the
|
||||
3.3V supply as a reference. Assuming the ``ADCAll`` object has been Instantiated with
|
||||
``adc = pyb.ADCAll(12)`` the 3.3V supply voltage may be calculated:
|
||||
|
||||
``v33 = 3.3 * 1.21 / adc.read_core_vref()``
|
||||
|
||||
If the 3.3V supply is correct the value of ``adc.read_core_vbat()`` will be
|
||||
valid. If the supply voltage can drop below 3.3V, for example in in battery
|
||||
powered systems with a discharging battery, the regulator will fail to preserve
|
||||
the 3.3V supply resulting in an incorrect reading. To produce a value which will
|
||||
remain valid under these circumstances use the following:
|
||||
|
||||
``vback = adc.read_core_vbat() * 1.21 / adc.read_core_vref()``
|
||||
|
||||
It is possible to access these values without incurring the side effects of ``ADCAll``::
|
||||
|
||||
def adcread(chan): # 16 temp 17 vbat 18 vref
|
||||
assert chan >= 16 and chan <= 18, 'Invalid ADC channel'
|
||||
start = pyb.millis()
|
||||
timeout = 100
|
||||
stm.mem32[stm.RCC + stm.RCC_APB2ENR] |= 0x100 # enable ADC1 clock.0x4100
|
||||
stm.mem32[stm.ADC1 + stm.ADC_CR2] = 1 # Turn on ADC
|
||||
stm.mem32[stm.ADC1 + stm.ADC_CR1] = 0 # 12 bit
|
||||
if chan == 17:
|
||||
stm.mem32[stm.ADC1 + stm.ADC_SMPR1] = 0x200000 # 15 cycles
|
||||
stm.mem32[stm.ADC + 4] = 1 << 23
|
||||
elif chan == 18:
|
||||
stm.mem32[stm.ADC1 + stm.ADC_SMPR1] = 0x1000000
|
||||
stm.mem32[stm.ADC + 4] = 0xc00000
|
||||
else:
|
||||
stm.mem32[stm.ADC1 + stm.ADC_SMPR1] = 0x40000
|
||||
stm.mem32[stm.ADC + 4] = 1 << 23
|
||||
stm.mem32[stm.ADC1 + stm.ADC_SQR3] = chan
|
||||
stm.mem32[stm.ADC1 + stm.ADC_CR2] = 1 | (1 << 30) | (1 << 10) # start conversion
|
||||
while not stm.mem32[stm.ADC1 + stm.ADC_SR] & 2: # wait for EOC
|
||||
if pyb.elapsed_millis(start) > timeout:
|
||||
raise OSError('ADC timout')
|
||||
data = stm.mem32[stm.ADC1 + stm.ADC_DR] # clear down EOC
|
||||
stm.mem32[stm.ADC1 + stm.ADC_CR2] = 0 # Turn off ADC
|
||||
return data
|
||||
|
||||
def v33():
|
||||
return 4096 * 1.21 / adcread(17)
|
||||
|
||||
def vbat():
|
||||
return 1.21 * 2 * adcread(18) / adcread(17) # 2:1 divider on Vbat channel
|
||||
|
||||
def vref():
|
||||
return 3.3 * adcread(17) / 4096
|
||||
|
||||
def temperature():
|
||||
return 25 + 400 * (3.3 * adcread(16) / 4096 - 0.76)
|
||||
|
||||
|
||||
@@ -63,16 +63,24 @@ Constructors
|
||||
|
||||
.. class:: pyb.I2C(bus, ...)
|
||||
|
||||
Construct an I2C object on the given bus. ``bus`` can be 1 or 2.
|
||||
With no additional parameters, the I2C object is created but not
|
||||
Construct an I2C object on the given bus. ``bus`` can be 1 or 2, 'X' or
|
||||
'Y'. With no additional parameters, the I2C object is created but not
|
||||
initialised (it has the settings from the last initialisation of
|
||||
the bus, if any). If extra arguments are given, the bus is initialised.
|
||||
See ``init`` for parameters of initialisation.
|
||||
|
||||
The physical pins of the I2C busses are:
|
||||
The physical pins of the I2C busses on Pyboards V1.0 and V1.1 are:
|
||||
|
||||
- ``I2C(1)`` is on the X position: ``(SCL, SDA) = (X9, X10) = (PB6, PB7)``
|
||||
- ``I2C(2)`` is on the Y position: ``(SCL, SDA) = (Y9, Y10) = (PB10, PB11)``
|
||||
|
||||
On the Pyboard Lite:
|
||||
|
||||
- ``I2C(1)`` is on the X position: ``(SCL, SDA) = (X9, X10) = (PB6, PB7)``
|
||||
- ``I2C(3)`` is on the Y position: ``(SCL, SDA) = (Y9, Y10) = (PA8, PB8)``
|
||||
|
||||
Calling the constructor with 'X' or 'Y' enables portability between Pyboard
|
||||
types.
|
||||
|
||||
Methods
|
||||
-------
|
||||
|
||||
@@ -33,9 +33,9 @@ Constructors
|
||||
|
||||
.. class:: pyb.SPI(bus, ...)
|
||||
|
||||
Construct an SPI object on the given bus. ``bus`` can be 1 or 2.
|
||||
With no additional parameters, the SPI object is created but not
|
||||
initialised (it has the settings from the last initialisation of
|
||||
Construct an SPI object on the given bus. ``bus`` can be 1 or 2, or
|
||||
'X' or 'Y'. With no additional parameters, the SPI object is created but
|
||||
not initialised (it has the settings from the last initialisation of
|
||||
the bus, if any). If extra arguments are given, the bus is initialised.
|
||||
See ``init`` for parameters of initialisation.
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ Methods
|
||||
|
||||
.. method:: uart.any()
|
||||
|
||||
Return ``True`` if any characters waiting, else ``False``.
|
||||
Returns the number of characters waiting (may be 0).
|
||||
|
||||
.. method:: uart.writechar(char)
|
||||
|
||||
|
||||
@@ -10,11 +10,25 @@ encodings of it in ASCII form (in both directions).
|
||||
Functions
|
||||
---------
|
||||
|
||||
.. function:: hexlify(data)
|
||||
.. function:: hexlify(data, [sep])
|
||||
|
||||
Convert binary data to hexadecimal representation. Return bytes string.
|
||||
|
||||
.. admonition:: Difference to CPython
|
||||
:class: attention
|
||||
|
||||
If additional argument, `sep` is supplied, it is used as a seperator
|
||||
between hexadecimal values.
|
||||
|
||||
.. function:: unhexlify(data)
|
||||
|
||||
Convert hexadecimal data to binary representation. Return bytes string.
|
||||
(i.e. inverse of hexlify)
|
||||
|
||||
.. function:: a2b_base64(data)
|
||||
|
||||
Convert Base64-encoded data to binary representation. Return bytes string.
|
||||
|
||||
.. function:: b2a_base64(data)
|
||||
|
||||
Encode binary data in Base64 format. Return string.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
This module implements "foreign data interface" for MicroPython. The idea
|
||||
behind it is similar to CPython's ``ctypes`` modules, but actual API is
|
||||
different, steamlined and optimized for small size.
|
||||
different, streamlined and optimized for small size.
|
||||
|
||||
Defining structure layout
|
||||
-------------------------
|
||||
|
||||
@@ -64,8 +64,8 @@ This code uses a few new concepts:
|
||||
Accepting arguments
|
||||
-------------------
|
||||
|
||||
Inline assembler functions can accept up to 3 arguments. If they are
|
||||
used, they must be named ``r0``, ``r1`` and ``r2`` to reflect the registers
|
||||
Inline assembler functions can accept up to 4 arguments. If they are
|
||||
used, they must be named ``r0``, ``r1``, ``r2`` and ``r3`` to reflect the registers
|
||||
and the calling conventions.
|
||||
|
||||
Here is a function that adds its arguments::
|
||||
|
||||
@@ -78,11 +78,23 @@ three arguments, which must (if used) be named ``r0``, ``r1`` and ``r2``. When
|
||||
the code executes the registers will be initialised to those values.
|
||||
|
||||
The data types which can be passed in this way are integers and memory
|
||||
addresses. With current firmware all possible 32 bit values may be passed.
|
||||
Returned integers are restricted in that the top two bits must be identical,
|
||||
limiting the range to -2**30 to 2**30 -1. The limitations on number of arguments
|
||||
and return values can be overcome by means of the ``array`` module which enables
|
||||
any number of values of any type to be accessed.
|
||||
addresses. With current firmware all possible 32 bit values may be passed and
|
||||
returned. If the return value may have the most significant bit set a Python
|
||||
type hint should be employed to enable MicroPython to determine whether the
|
||||
value should be interpreted as a signed or unsigned integer: types are
|
||||
``int`` or ``uint``.
|
||||
|
||||
::
|
||||
|
||||
@micropython.asm_thumb
|
||||
def uadd(r0, r1) -> uint:
|
||||
add(r0, r0, r1)
|
||||
|
||||
``hex(uadd(0x40000000,0x40000000))`` will return 0x80000000, demonstrating the
|
||||
passing and return of integers where bits 30 and 31 differ.
|
||||
|
||||
The limitations on the number of arguments and return values can be overcome by means
|
||||
of the ``array`` module which enables any number of values of any type to be accessed.
|
||||
|
||||
Multiple arguments
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -14,6 +14,7 @@ MicroPython are described in the sections here.
|
||||
|
||||
repl.rst
|
||||
isr_rules.rst
|
||||
speed_python.rst
|
||||
|
||||
.. only:: port_pyboard
|
||||
|
||||
|
||||
318
docs/reference/speed_python.rst
Normal file
318
docs/reference/speed_python.rst
Normal file
@@ -0,0 +1,318 @@
|
||||
Maximising Python Speed
|
||||
=======================
|
||||
|
||||
This tutorial describes ways of improving the performance of MicroPython code.
|
||||
Optimisations involving other languages are covered elsewhere, namely the use
|
||||
of modules written in C and the MicroPython inline ARM Thumb-2 assembler.
|
||||
|
||||
The process of developing high performance code comprises the following stages
|
||||
which should be performed in the order listed.
|
||||
|
||||
* Design for speed.
|
||||
* Code and debug.
|
||||
|
||||
Optimisation steps:
|
||||
|
||||
* Identify the slowest section of code.
|
||||
* Improve the efficiency of the Python code.
|
||||
* Use the native code emitter.
|
||||
* Use the viper code emitter.
|
||||
|
||||
Designing for speed
|
||||
-------------------
|
||||
|
||||
Performance issues should be considered at the outset. This involves taking a view
|
||||
on the sections of code which are most performance critical and devoting particular
|
||||
attention to their design. The process of optimisation begins when the code has
|
||||
been tested: if the design is correct at the outset optimisation will be
|
||||
straightforward and may actually be unnecessary.
|
||||
|
||||
Algorithms
|
||||
~~~~~~~~~~
|
||||
|
||||
The most important aspect of designing any routine for performance is ensuring that
|
||||
the best algorithm is employed. This is a topic for textbooks rather than for a
|
||||
MicroPython guide but spectacular performance gains can sometimes be achieved
|
||||
by adopting algorithms known for their efficiency.
|
||||
|
||||
RAM Allocation
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
To design efficient MicroPython code it is necessary to have an understanding of the
|
||||
way the interpreter allocates RAM. When an object is created or grows in size
|
||||
(for example where an item is appended to a list) the necessary RAM is allocated
|
||||
from a block known as the heap. This takes a significant amount of time;
|
||||
further it will on occasion trigger a process known as garbage collection which
|
||||
can take several milliseconds.
|
||||
|
||||
Consequently the performance of a function or method can be improved if an object is created
|
||||
once only and not permitted to grow in size. This implies that the object persists
|
||||
for the duration of its use: typically it will be instantiated in a class constructor
|
||||
and used in various methods.
|
||||
|
||||
This is covered in further detail :ref:`Controlling garbage collection <gc>` below.
|
||||
|
||||
Buffers
|
||||
~~~~~~~
|
||||
|
||||
An example of the above is the common case where a buffer is required, such as one
|
||||
used for communication with a device. A typical driver will create the buffer in the
|
||||
constructor and use it in its I/O methods which will be called repeatedly.
|
||||
|
||||
The MicroPython libraries typically provide optional support for pre-allocated buffers.
|
||||
For example the ``uart.readinto()`` method allows two options for its argument, an integer
|
||||
or a buffer. If an integer is supplied it will read up to that number of bytes and
|
||||
return the outcome: this implies that a buffer is created with a corresponding
|
||||
memory allocation. Providing a pre-allocated buffer as the argument avoids this. See
|
||||
the code fragment in :ref:`Caching object references <Caching>` below.
|
||||
|
||||
Floating Point
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
For the most speed critical sections of code it is worth noting that performing
|
||||
any kind of floating point operation involves heap allocation. Where possible use
|
||||
integer operations and restrict the use of floating point to sections of the code
|
||||
where performance is not paramount.
|
||||
|
||||
Arrays
|
||||
~~~~~~
|
||||
|
||||
Consider the use of the various types of array classes as an alternative to lists.
|
||||
The ``array`` module supports various element types with 8-bit elements supported
|
||||
by Python's built in ``bytes`` and ``bytearray`` classes. These data structures all store
|
||||
elements in contiguous memory locations. Once again to avoid memory allocation in critical
|
||||
code these should be pre-allocated and passed as arguments or as bound objects.
|
||||
|
||||
When passing slices of objects such as ``bytearray`` instances, Python creates
|
||||
a copy which involves allocation. This can be avoided using a ``memoryview``
|
||||
object:
|
||||
|
||||
.. code:: python
|
||||
|
||||
ba = bytearray(100)
|
||||
func(ba[3:10]) # a copy is passed
|
||||
mv = memoryview(ba)
|
||||
func(mv[3:10]) # a pointer to memory is passed
|
||||
|
||||
A ``memoryview`` can only be applied to objects supporting the buffer protocol - this
|
||||
includes arrays but not lists.
|
||||
|
||||
Identifying the slowest section of code
|
||||
---------------------------------------
|
||||
|
||||
This is a process known as profiling and is covered in textbooks and
|
||||
(for standard Python) supported by various software tools. For the type of
|
||||
smaller embedded application likely to be running on MicroPython platforms
|
||||
the slowest function or method can usually be established by judicious use
|
||||
of the timing ``ticks`` group of functions documented
|
||||
`here <http://docs.micropython.org/en/latest/pyboard/library/time.html>`_.
|
||||
Code execution time can be measured in ms, us, or CPU cycles.
|
||||
|
||||
The following enables any function or method to be timed by adding an
|
||||
``@timed_function`` decorator:
|
||||
|
||||
.. code:: python
|
||||
|
||||
def timed_function(f, *args, **kwargs):
|
||||
myname = str(f).split(' ')[1]
|
||||
def new_func(*args, **kwargs):
|
||||
t = time.ticks_us()
|
||||
result = f(*args, **kwargs)
|
||||
delta = time.ticks_diff(t, time.ticks_us())
|
||||
print('Function {} Time = {:6.3f}ms'.format(myname, delta/1000))
|
||||
return result
|
||||
return new_func
|
||||
|
||||
MicroPython code improvements
|
||||
-----------------------------
|
||||
|
||||
The const() declaration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
MicroPython provides a ``const()`` declaration. This works in a similar way
|
||||
to ``#define`` in C in that when the code is compiled to bytecode the compiler
|
||||
substitutes the numeric value for the identifier. This avoids a dictionary
|
||||
lookup at runtime. The argument to ``const()`` may be anything which, at
|
||||
compile time, evaluates to an integer e.g. ``0x100`` or ``1 << 8``.
|
||||
|
||||
.. _Caching:
|
||||
|
||||
Caching object references
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Where a function or method repeatedly accesses objects performance is improved
|
||||
by caching the object in a local variable:
|
||||
|
||||
.. code:: python
|
||||
|
||||
class foo(object):
|
||||
def __init__(self):
|
||||
ba = bytearray(100)
|
||||
def bar(self, obj_display):
|
||||
ba_ref = self.ba
|
||||
fb = obj_display.framebuffer
|
||||
# iterative code using these two objects
|
||||
|
||||
This avoids the need repeatedly to look up ``self.ba`` and ``obj_display.framebuffer``
|
||||
in the body of the method ``bar()``.
|
||||
|
||||
.. _gc:
|
||||
|
||||
Controlling garbage collection
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When memory allocation is required, MicroPython attempts to locate an adequately
|
||||
sized block on the heap. This may fail, usually because the heap is cluttered
|
||||
with objects which are no longer referenced by code. If a failure occurs, the
|
||||
process known as garbage collection reclaims the memory used by these redundant
|
||||
objects and the allocation is then tried again - a process which can take several
|
||||
milliseconds.
|
||||
|
||||
There are benefits in pre-empting this by periodically issuing ``gc.collect()``.
|
||||
Firstly doing a collection before it is actually required is quicker - typically on the
|
||||
order of 1ms if done frequently. Secondly you can determine the point in code
|
||||
where this time is used rather than have a longer delay occur at random points,
|
||||
possibly in a speed critical section. Finally performing collections regularly
|
||||
can reduce fragmentation in the heap. Severe fragmentation can lead to
|
||||
non-recoverable allocation failures.
|
||||
|
||||
Accessing hardware directly
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This comes into the category of more advanced programming and involves some knowledge
|
||||
of the target MCU. Consider the example of toggling an output pin on the Pyboard. The
|
||||
standard approach would be to write
|
||||
|
||||
.. code:: python
|
||||
|
||||
mypin.value(mypin.value() ^ 1) # mypin was instantiated as an output pin
|
||||
|
||||
This involves the overhead of two calls to the ``Pin`` instance's ``value()``
|
||||
method. This overhead can be eliminated by performing a read/write to the relevant bit
|
||||
of the chip's GPIO port output data register (odr). To facilitate this the ``stm``
|
||||
module provides a set of constants providing the addresses of the relevant registers.
|
||||
A fast toggle of pin ``P4`` (CPU pin ``A14``) - corresponding to the green LED -
|
||||
can be performed as follows:
|
||||
|
||||
.. code:: python
|
||||
|
||||
BIT14 = const(1 << 14)
|
||||
stm.mem16[stm.GPIOA + stm.GPIO_ODR] ^= BIT14
|
||||
|
||||
The Native code emitter
|
||||
-----------------------
|
||||
|
||||
This causes the MicroPython compiler to emit ARM native opcodes rather than
|
||||
bytecode. It covers the bulk of the Python language so most functions will require
|
||||
no adaptation (but see below). It is invoked by means of a function decorator:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@micropython.native
|
||||
def foo(self, arg):
|
||||
buf = self.linebuf # Cached object
|
||||
# code
|
||||
|
||||
There are certain limitations in the current implementation of the native code emitter.
|
||||
|
||||
* Context managers are not supported (the ``with`` statement).
|
||||
* Generators are not supported.
|
||||
* If ``raise`` is used an argument must be supplied.
|
||||
|
||||
The trade-off for the improved performance (roughly twices as fast as bytecode) is an
|
||||
increase in compiled code size.
|
||||
|
||||
The Viper code emitter
|
||||
----------------------
|
||||
|
||||
The optimisations discussed above involve standards-compliant Python code. The
|
||||
Viper code emitter is not fully compliant. It supports special Viper native data types
|
||||
in pursuit of performance. Integer processing is non-compliant because it uses machine
|
||||
words: arithmetic on 32 bit hardware is performed modulo 2**32.
|
||||
|
||||
Like the Native emitter Viper produces machine instructions but further optimisations
|
||||
are performed, substantially increasing performance especially for integer arithmetic and
|
||||
bit manipulations. It is invoked using a decorator:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@micropython.viper
|
||||
def foo(self, arg: int) -> int:
|
||||
# code
|
||||
|
||||
As the above fragment illustrates it is beneficial to use Python type hints to assist the Viper optimiser.
|
||||
Type hints provide information on the data types of arguments and of the return value; these
|
||||
are a standard Python language feature formally defined here `PEP0484 <https://www.python.org/dev/peps/pep-0484/>`_.
|
||||
Viper supports its own set of types namely ``int``, ``uint`` (unsigned integer), ``ptr``, ``ptr8``,
|
||||
``ptr16`` and ``ptr32``. The ``ptrX`` types are discussed below. Currently the ``uint`` type serves
|
||||
a single purpose: as a type hint for a function return value. If such a function returns ``0xffffffff``
|
||||
Python will interpret the result as 2**32 -1 rather than as -1.
|
||||
|
||||
In addition to the restrictions imposed by the native emitter the following constraints apply:
|
||||
|
||||
* Functions may have up to four arguments.
|
||||
* Default argument values are not permitted.
|
||||
* Floating point may be used but is not optimised.
|
||||
|
||||
Viper provides pointer types to assist the optimiser. These comprise
|
||||
|
||||
* ``ptr`` Pointer to an object.
|
||||
* ``ptr8`` Points to a byte.
|
||||
* ``ptr16`` Points to a 16 bit half-word.
|
||||
* ``ptr32`` Points to a 32 bit machine word.
|
||||
|
||||
The concept of a pointer may be unfamiliar to Python programmers. It has similarities
|
||||
to a Python ``memoryview`` object in that it provides direct access to data stored in memory.
|
||||
Items are accessed using subscript notation, but slices are not supported: a pointer can return
|
||||
a single item only. Its purpose is to provide fast random access to data stored in contiguous
|
||||
memory locations - such as data stored in objects which support the buffer protocol, and
|
||||
memory-mapped peripheral registers in a microcontroller. It should be noted that programming
|
||||
using pointers is hazardous: bounds checking is not performed and the compiler does nothing to
|
||||
prevent buffer overrun errors.
|
||||
|
||||
Typical usage is to cache variables:
|
||||
|
||||
.. code:: python
|
||||
|
||||
@micropython.viper
|
||||
def foo(self, arg: int) -> int:
|
||||
buf = ptr8(self.linebuf) # self.linebuf is a bytearray or bytes object
|
||||
for x in range(20, 30):
|
||||
bar = buf[x] # Access a data item through the pointer
|
||||
# code omitted
|
||||
|
||||
In this instance the compiler "knows" that ``buf`` is the address of an array of bytes;
|
||||
it can emit code to rapidly compute the address of ``buf[x]`` at runtime. Where casts are
|
||||
used to convert objects to Viper native types these should be performed at the start of
|
||||
the function rather than in critical timing loops as the cast operation can take several
|
||||
microseconds. The rules for casting are as follows:
|
||||
|
||||
* Casting operators are currently: ``int``, ``bool``, ``uint``, ``ptr``, ``ptr8``, ``ptr16`` and ``ptr32``.
|
||||
* The result of a cast will be a native Viper variable.
|
||||
* Arguments to a cast can be a Python object or a native Viper variable.
|
||||
* If argument is a native Viper variable, then cast is a no-op (i.e. costs nothing at runtime)
|
||||
that just changes the type (e.g. from ``uint`` to ``ptr8``) so that you can then store/load
|
||||
using this pointer.
|
||||
* If the argument is a Python object and the cast is ``int`` or ``uint``, then the Python object
|
||||
must be of integral type and the value of that integral object is returned.
|
||||
* The argument to a bool cast must be integral type (boolean or integer); when used as a return
|
||||
type the viper function will return True or False objects.
|
||||
* If the argument is a Python object and the cast is ``ptr``, ``ptr``, ``ptr16`` or ``ptr32``,
|
||||
then the Python object must either have the buffer protocol with read-write capabilities
|
||||
(in which case a pointer to the start of the buffer is returned) or it must be of integral
|
||||
type (in which case the value of that integral object is returned).
|
||||
|
||||
The following example illustrates the use of a ``ptr16`` cast to toggle pin X1 ``n`` times:
|
||||
|
||||
.. code:: python
|
||||
|
||||
BIT0 = const(1)
|
||||
@micropython.viper
|
||||
def toggle_n(n: int):
|
||||
odr = ptr16(stm.GPIOA + stm.GPIO_ODR)
|
||||
for _ in range(n):
|
||||
odr[0] ^= BIT0
|
||||
|
||||
A detailed technical description of the three code emitters may be found
|
||||
on Kickstarter here `Note 1 <https://www.kickstarter.com/projects/214379695/micro-python-python-for-microcontrollers/posts/664832>`_
|
||||
and here `Note 2 <https://www.kickstarter.com/projects/214379695/micro-python-python-for-microcontrollers/posts/665145>`_
|
||||
39
docs/templates/versions.html
vendored
Normal file
39
docs/templates/versions.html
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
|
||||
<span class="rst-current-version" data-toggle="rst-current-version">
|
||||
<span class="fa fa-book"> Ports and Versions</span>
|
||||
{{ port_short_name }} ({{ port_version }})
|
||||
<span class="fa fa-caret-down"></span>
|
||||
</span>
|
||||
<div class="rst-other-versions">
|
||||
<dl>
|
||||
<dt>Ports</dt>
|
||||
{% for slug, url in all_ports %}
|
||||
<dd><a href="{{ url }}">{{ slug }}</a></dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>Versions</dt>
|
||||
{% for slug, url in all_versions %}
|
||||
<dd><a href="{{ url }}">{{ slug }}</a></dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
<!--
|
||||
<dl>
|
||||
<dt>Downloads</dt>
|
||||
{% for type, url in downloads %}
|
||||
<dd><a href="{{ url }}">{{ type }}</a></dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
-->
|
||||
<hr/>
|
||||
<dl>
|
||||
<dt>External links</dt>
|
||||
<dd>
|
||||
<a href="http://www.micropython.org">micropython.org</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="https://github.com/micropython/micropython">GitHub</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,10 +41,10 @@
|
||||
{% endif %}
|
||||
<p class="biglink">
|
||||
<a class="biglink" href="{{ pathto("library/index") }}">Library Reference</a><br/>
|
||||
{% if port == "wipy" %}
|
||||
<span class="linkdescr">MicroPython libraries, including the <a href="{{ pathto("library/machine") }}">machine module</a></span>
|
||||
{% else %}
|
||||
{% if port == "pyboard" %}
|
||||
<span class="linkdescr">MicroPython libraries, including the <a href="{{ pathto("library/pyb") }}">pyb module</a></span>
|
||||
{% else %}
|
||||
<span class="linkdescr">MicroPython libraries, including the <a href="{{ pathto("library/machine") }}">machine module</a></span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</td>
|
||||
|
||||
@@ -49,10 +49,10 @@ See :ref:`machine.Timer <machine.Timer>` and :ref:`machine.Pin <machine.Pin>`. :
|
||||
from machine import Timer
|
||||
from machine import Pin
|
||||
|
||||
tim = Timer(1, mode=Timer.PERIODIC)
|
||||
tim = Timer(0, mode=Timer.PERIODIC)
|
||||
tim_a = tim.channel(Timer.A, freq=1000)
|
||||
tim_a.time() # get the value in microseconds
|
||||
tim_a.freq(1) # 1 Hz
|
||||
tim_a.freq(5) # 5 Hz
|
||||
|
||||
p_out = Pin('GP2', mode=Pin.OUT)
|
||||
tim_a.irq(handler=lambda t: p_out.toggle())
|
||||
@@ -63,16 +63,12 @@ PWM (pulse width modulation)
|
||||
See :ref:`machine.Pin <machine.Pin>` and :ref:`machine.Timer <machine.Timer>`. ::
|
||||
|
||||
from machine import Timer
|
||||
from machine import Pin
|
||||
|
||||
# assign GP25 to alternate function 9 (PWM)
|
||||
p_out = Pin('GP25', mode=Pin.AF, alt=9)
|
||||
|
||||
# timer 2 in PWM mode and width must be 16 buts
|
||||
tim = Timer(2, mode=Timer.PWM, width=16)
|
||||
# timer 1 in PWM mode and width must be 16 buts
|
||||
tim = Timer(1, mode=Timer.PWM, width=16)
|
||||
|
||||
# enable channel A @1KHz with a 50% duty cycle
|
||||
tim_a = tim.channel(Timer.A, freq=1000, duty_cycle=50)
|
||||
# enable channel A @1KHz with a 50.55% duty cycle
|
||||
tim_a = tim.channel(Timer.A, freq=1000, duty_cycle=5055)
|
||||
|
||||
ADC (analog to digital conversion)
|
||||
----------------------------------
|
||||
@@ -201,12 +197,12 @@ See :ref:`network.WLAN <network.WLAN>` and :mod:`machine`. ::
|
||||
Telnet and FTP server
|
||||
---------------------
|
||||
|
||||
See :ref:`network.server <network.server>` ::
|
||||
See :ref:`network.Server <network.Server>` ::
|
||||
|
||||
from network import server
|
||||
from network import Server
|
||||
|
||||
# init with new user, password and seconds timeout
|
||||
server = server.init(login=('user', 'password'), timeout=60)
|
||||
server = Server(login=('user', 'password'), timeout=60)
|
||||
server.timeout(300) # change the timeout
|
||||
server.timeout() # get the timeout
|
||||
server.isrunning() # check wether the server is running or not
|
||||
|
||||
@@ -25,6 +25,9 @@ class SDCard:
|
||||
#R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
|
||||
#R1_ADDRESS_ERROR = const(1 << 5)
|
||||
#R1_PARAMETER_ERROR = const(1 << 6)
|
||||
TOKEN_CMD25 = const(0xfc)
|
||||
TOKEN_STOP_TRAN = const(0xfd)
|
||||
TOKEN_DATA = const(0xfe)
|
||||
|
||||
def __init__(self, spi, cs):
|
||||
self.spi = spi
|
||||
@@ -136,6 +139,18 @@ class SDCard:
|
||||
self.spi.send(0xff)
|
||||
return -1
|
||||
|
||||
def cmd_nodata(self, cmd):
|
||||
self.spi.send(cmd)
|
||||
self.spi.send_recv(0xff) # ignore stuff byte
|
||||
for _ in range(CMD_TIMEOUT):
|
||||
if self.spi.send_recv(0xff)[0] == 0xff:
|
||||
self.cs.high()
|
||||
self.spi.send(0xff)
|
||||
return 0 # OK
|
||||
self.cs.high()
|
||||
self.spi.send(0xff)
|
||||
return 1 # timeout
|
||||
|
||||
def readinto(self, buf):
|
||||
self.cs.low()
|
||||
|
||||
@@ -154,11 +169,11 @@ class SDCard:
|
||||
self.cs.high()
|
||||
self.spi.send(0xff)
|
||||
|
||||
def write(self, buf):
|
||||
def write(self, token, buf):
|
||||
self.cs.low()
|
||||
|
||||
# send: start of block, data, checksum
|
||||
self.spi.send(0xfe)
|
||||
self.spi.send(token)
|
||||
self.spi.send(buf)
|
||||
self.spi.send(0xff)
|
||||
self.spi.send(0xff)
|
||||
@@ -176,29 +191,62 @@ class SDCard:
|
||||
self.cs.high()
|
||||
self.spi.send(0xff)
|
||||
|
||||
def write_token(self, token):
|
||||
self.cs.low()
|
||||
self.spi.send(token)
|
||||
self.spi.send(0xff)
|
||||
# wait for write to finish
|
||||
while self.spi.send_recv(0xff)[0] == 0:
|
||||
pass
|
||||
|
||||
self.cs.high()
|
||||
self.spi.send(0xff)
|
||||
|
||||
def count(self):
|
||||
return self.sectors
|
||||
|
||||
def readblocks(self, block_num, buf):
|
||||
# TODO support multiple block reads
|
||||
assert len(buf) == 512
|
||||
|
||||
# CMD17: set read address for single block
|
||||
if self.cmd(17, block_num * self.cdv, 0) != 0:
|
||||
return 1
|
||||
|
||||
# receive the data
|
||||
self.readinto(buf)
|
||||
nblocks, err = divmod(len(buf), 512)
|
||||
assert nblocks and not err, 'Buffer length is invalid'
|
||||
if nblocks == 1:
|
||||
# CMD17: set read address for single block
|
||||
if self.cmd(17, block_num * self.cdv, 0) != 0:
|
||||
return 1
|
||||
# receive the data
|
||||
self.readinto(buf)
|
||||
else:
|
||||
# CMD18: set read address for multiple blocks
|
||||
if self.cmd(18, block_num * self.cdv, 0) != 0:
|
||||
return 1
|
||||
offset = 0
|
||||
mv = memoryview(buf)
|
||||
while nblocks:
|
||||
self.readinto(mv[offset : offset + 512])
|
||||
offset += 512
|
||||
nblocks -= 1
|
||||
return self.cmd_nodata(12)
|
||||
return 0
|
||||
|
||||
def writeblocks(self, block_num, buf):
|
||||
# TODO support multiple block writes
|
||||
assert len(buf) == 512
|
||||
nblocks, err = divmod(len(buf), 512)
|
||||
assert nblocks and not err, 'Buffer length is invalid'
|
||||
if nblocks == 1:
|
||||
# CMD24: set write address for single block
|
||||
if self.cmd(24, block_num * self.cdv, 0) != 0:
|
||||
return 1
|
||||
|
||||
# CMD24: set write address for single block
|
||||
if self.cmd(24, block_num * self.cdv, 0) != 0:
|
||||
return 1
|
||||
|
||||
# send the data
|
||||
self.write(buf)
|
||||
# send the data
|
||||
self.write(TOKEN_DATA, buf)
|
||||
else:
|
||||
# CMD25: set write address for first block
|
||||
if self.cmd(25, block_num * self.cdv, 0) != 0:
|
||||
return 1
|
||||
# send the data
|
||||
offset = 0
|
||||
mv = memoryview(buf)
|
||||
while nblocks:
|
||||
self.write(TOKEN_CMD25, mv[offset : offset + 512])
|
||||
offset += 512
|
||||
nblocks -= 1
|
||||
self.write_token(TOKEN_STOP_TRAN)
|
||||
return 0
|
||||
|
||||
57
drivers/sdcard/sdtest.py
Normal file
57
drivers/sdcard/sdtest.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# Test for sdcard block protocol
|
||||
# Peter hinch 30th Jan 2016
|
||||
import os, sdcard, pyb
|
||||
|
||||
def sdtest():
|
||||
sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X21) # Compatible with PCB
|
||||
pyb.mount(sd, '/fc')
|
||||
print('Filesystem check')
|
||||
print(os.listdir('/fc'))
|
||||
|
||||
line = 'abcdefghijklmnopqrstuvwxyz\n'
|
||||
lines = line * 200 # 5400 chars
|
||||
short = '1234567890\n'
|
||||
|
||||
fn = '/fc/rats.txt'
|
||||
print()
|
||||
print('Multiple block read/write')
|
||||
with open(fn,'w') as f:
|
||||
n = f.write(lines)
|
||||
print(n, 'bytes written')
|
||||
n = f.write(short)
|
||||
print(n, 'bytes written')
|
||||
n = f.write(lines)
|
||||
print(n, 'bytes written')
|
||||
|
||||
with open(fn,'r') as f:
|
||||
result1 = f.read()
|
||||
print(len(result1), 'bytes read')
|
||||
|
||||
fn = '/fc/rats1.txt'
|
||||
print()
|
||||
print('Single block read/write')
|
||||
with open(fn,'w') as f:
|
||||
n = f.write(short) # one block
|
||||
print(n, 'bytes written')
|
||||
|
||||
with open(fn,'r') as f:
|
||||
result2 = f.read()
|
||||
print(len(result2), 'bytes read')
|
||||
|
||||
pyb.mount(None, '/fc')
|
||||
|
||||
print()
|
||||
print('Verifying data read back')
|
||||
success = True
|
||||
if result1 == ''.join((lines, short, lines)):
|
||||
print('Large file Pass')
|
||||
else:
|
||||
print('Large file Fail')
|
||||
success = False
|
||||
if result2 == short:
|
||||
print('Small file Pass')
|
||||
else:
|
||||
print('Small file Fail')
|
||||
success = False
|
||||
print()
|
||||
print('Tests', 'passed' if success else 'failed')
|
||||
@@ -9,8 +9,8 @@ include ../py/py.mk
|
||||
MAKE_FROZEN = ../tools/make-frozen.py
|
||||
|
||||
SCRIPTDIR = scripts
|
||||
PORT = /dev/ttyACM0
|
||||
BAUD = 115200
|
||||
PORT ?= /dev/ttyACM0
|
||||
BAUD ?= 115200
|
||||
CROSS_COMPILE = xtensa-lx106-elf-
|
||||
ESP_SDK = $(shell $(CC) -print-sysroot)/usr
|
||||
|
||||
@@ -23,18 +23,21 @@ INC += -I../lib/timeutils
|
||||
INC += -I$(BUILD)
|
||||
INC += -I$(ESP_SDK)/include
|
||||
|
||||
UART_OS = 1
|
||||
# UART for "os" messages. 0 is normal UART as used by MicroPython REPL,
|
||||
# 1 is debug UART (tx only).
|
||||
UART_OS = 0
|
||||
|
||||
CFLAGS_XTENSA = -fsingle-precision-constant -Wdouble-promotion \
|
||||
-D__ets__ -DICACHE_FLASH \
|
||||
-fno-inline-functions \
|
||||
-Wl,-EL -mlongcalls -mtext-section-literals \
|
||||
-DLWIP_OPEN_SRC
|
||||
|
||||
CFLAGS = $(INC) -Wall -Wpointer-arith -Werror -ansi -std=gnu99 -nostdlib -DUART_OS=$(UART_OS) \
|
||||
$(CFLAGS_XTENSA) $(COPT) $(CFLAGS_EXTRA)
|
||||
|
||||
LDFLAGS = -nostdlib -T esp8266.ld -Map=$(@:.elf=.map) --cref
|
||||
LIBS = -L$(ESP_SDK)/lib -lmain -ljson -llwip -lpp -lnet80211 -lwpa -lphy -lnet80211
|
||||
LIBS = -L$(ESP_SDK)/lib -lmain -ljson -lssl -llwip_open -lpp -lnet80211 -lwpa -lphy -lnet80211
|
||||
|
||||
LIBGCC_FILE_NAME = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
|
||||
LIBS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc
|
||||
@@ -45,7 +48,7 @@ CFLAGS += -g
|
||||
COPT = -O0
|
||||
else
|
||||
CFLAGS += -fdata-sections -ffunction-sections
|
||||
COPT += -Os -DNDEBUG
|
||||
COPT += -Os -mforce-l32 -DNDEBUG
|
||||
LDFLAGS += --gc-sections
|
||||
endif
|
||||
|
||||
@@ -54,29 +57,64 @@ SRC_C = \
|
||||
main.c \
|
||||
esp_mphal.c \
|
||||
gccollect.c \
|
||||
lexerstr32.c \
|
||||
uart.c \
|
||||
esppwm.c \
|
||||
espneopixel.c \
|
||||
modpyb.c \
|
||||
modpybpin.c \
|
||||
modpybpwm.c \
|
||||
modpybrtc.c \
|
||||
modpybadc.c \
|
||||
modpybuart.c \
|
||||
modpybi2c.c \
|
||||
modpybspi.c \
|
||||
modesp.c \
|
||||
modnetwork.c \
|
||||
modutime.c \
|
||||
moduos.c \
|
||||
modmachine.c \
|
||||
modonewire.c \
|
||||
utils.c \
|
||||
ets_alt_task.c \
|
||||
$(BUILD)/frozen.c \
|
||||
fatfs_port.o \
|
||||
|
||||
STM_SRC_C = $(addprefix stmhal/,\
|
||||
pybstdio.c \
|
||||
)
|
||||
|
||||
EXTMOD_SRC_C = $(addprefix extmod/,\
|
||||
modlwip.o \
|
||||
)
|
||||
|
||||
LIB_SRC_C = $(addprefix lib/,\
|
||||
libc/string0.c \
|
||||
libm/math.c \
|
||||
libm/fmodf.c \
|
||||
libm/roundf.c \
|
||||
libm/ef_sqrt.c \
|
||||
libm/kf_rem_pio2.c \
|
||||
libm/kf_sin.c \
|
||||
libm/kf_cos.c \
|
||||
libm/kf_tan.c \
|
||||
libm/ef_rem_pio2.c \
|
||||
libm/sf_sin.c \
|
||||
libm/sf_cos.c \
|
||||
libm/sf_tan.c \
|
||||
libm/sf_frexp.c \
|
||||
libm/sf_modf.c \
|
||||
libm/sf_ldexp.c \
|
||||
libm/asinfacosf.c \
|
||||
libm/atanf.c \
|
||||
libm/atan2f.c \
|
||||
mp-readline/readline.c \
|
||||
netutils/netutils.c \
|
||||
timeutils/timeutils.c \
|
||||
utils/pyexec.c \
|
||||
utils/printf.c \
|
||||
fatfs/ff.c \
|
||||
fatfs/option/ccsbcs.c \
|
||||
)
|
||||
|
||||
SRC_S = \
|
||||
@@ -87,6 +125,7 @@ OBJ += $(PY_O)
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(STM_SRC_C:.c=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
|
||||
#OBJ += $(BUILD)/pins_$(BOARD).o
|
||||
|
||||
@@ -111,8 +150,8 @@ $(BUILD)/frozen.c: $(wildcard $(SCRIPTDIR)/*) $(CONFVARS_FILE)
|
||||
|
||||
deploy: $(BUILD)/firmware-combined.bin
|
||||
$(ECHO) "Writing $< to the board"
|
||||
#$(Q)esptool.py --port $(PORT) write_flash 0 $<
|
||||
$(Q)esptool.py --port $(PORT) --baud $(BAUD) write_flash 0 $(BUILD)/firmware.elf-0x00000.bin 0x10000 $(BUILD)/firmware.elf-0x10000.bin
|
||||
$(Q)esptool.py --port $(PORT) --baud $(BAUD) write_flash --flash_size=8m 0 $<
|
||||
#$(Q)esptool.py --port $(PORT) --baud $(BAUD) write_flash --flash_size=8m 0 $(BUILD)/firmware.elf-0x00000.bin 0x9000 $(BUILD)/firmware.elf-0x0[1-f]000.bin
|
||||
|
||||
reset:
|
||||
echo -e "\r\nimport pyb; pyb.hard_reset()\r\n" >$(PORT)
|
||||
@@ -120,7 +159,7 @@ reset:
|
||||
$(BUILD)/firmware-combined.bin: $(BUILD)/firmware.elf
|
||||
$(ECHO) "Create $@"
|
||||
$(Q)esptool.py elf2image $^
|
||||
$(Q)$(PYTHON) makeimg.py $(BUILD)/firmware.elf-0x00000.bin $(BUILD)/firmware.elf-0x10000.bin $@
|
||||
$(Q)$(PYTHON) makeimg.py $(BUILD)/firmware.elf-0x00000.bin $(BUILD)/firmware.elf-0x0[1-f]000.bin $@
|
||||
|
||||
$(BUILD)/firmware.elf: $(OBJ)
|
||||
$(ECHO) "LINK $@"
|
||||
|
||||
@@ -8,49 +8,81 @@ WARNING: The port is highly experimental and any APIs are subject to change.
|
||||
|
||||
Currently implemented features include:
|
||||
- REPL (Python prompt) over UART0.
|
||||
- 24k heap RAM available for Python code.
|
||||
- Garbage collector, exceptions.
|
||||
- Unicode support.
|
||||
- Builtin modules: gc, array, collections, io, struct, sys, esp, network.
|
||||
- C long-long type used as bignum implementation (gives 64 bit signed ints).
|
||||
- Rudimentary WiFi support in station mode.
|
||||
- Sockets with callbacks.
|
||||
- Basic GPIO support.
|
||||
|
||||
Note that floating-point numbers are not supported.
|
||||
- Builtin modules: gc, array, collections, io, struct, sys, esp, network,
|
||||
many more.
|
||||
- Arbitrary-precision long integers and 30-bit precision floats.
|
||||
- Basic WiFi support.
|
||||
- Sockets using modlwip.
|
||||
- GPIO and bit-banging I2C, SPI support.
|
||||
- 1-Wire and WS2812 (aka Neopixel) protocols support.
|
||||
|
||||
On the TODO list:
|
||||
- Full wifi support.
|
||||
- Internal filesystem using the flash.
|
||||
- ...
|
||||
|
||||
Work-in-progress documentation is available at
|
||||
http://docs.micropython.org/en/latest/esp8266/ .
|
||||
|
||||
Build instructions
|
||||
------------------
|
||||
|
||||
The tool chain required for the build is the OpenSource ESP SDK, which can be
|
||||
found at <https://github.com/pfalcon/esp-open-sdk>. Clone this repository and
|
||||
run `make` in its directory to build and install the SDK locally.
|
||||
run `make` in its directory to build and install the SDK locally. Make sure
|
||||
to add toolchain bin directory to your PATH. Read esp-open-sdk's README for
|
||||
additional important information on toolchain setup.
|
||||
|
||||
Add the external dependencies to the MicroPython repository checkout:
|
||||
```bash
|
||||
$ git submodule update --init
|
||||
```
|
||||
See the README in the repository root for more information about external
|
||||
dependencies.
|
||||
|
||||
Then, to build MicroPython for the ESP8266, just run:
|
||||
```bash
|
||||
$ cd esp8266
|
||||
$ make
|
||||
```
|
||||
This should produce binary images in the `build/` subdirectory. To flash them
|
||||
to your ESP8266, use:
|
||||
This will produce binary images in the `build/` subdirectory. If you install
|
||||
MicroPython to your module for the first time, or after installing any other
|
||||
firmware, you should erase flash completely:
|
||||
|
||||
```
|
||||
esptool.py --port /dev//ttyXXX erase_flash
|
||||
```
|
||||
|
||||
Erase flash also as a troubleshooting measure, if a module doesn't behave as
|
||||
expected.
|
||||
|
||||
To flash MicroPython image to your ESP8266, use:
|
||||
```bash
|
||||
$ make deploy
|
||||
```
|
||||
This will use the `esptool.py` script to download the images. You must have
|
||||
your ESP module in the bootloader, and connected to a serial port on your PC.
|
||||
your ESP module in the bootloader mode, and connected to a serial port on your PC.
|
||||
The default serial port is `/dev/ttyACM0`. To specify another, use, eg:
|
||||
```bash
|
||||
$ make PORT=/dev/ttyUSB0 deploy
|
||||
```
|
||||
|
||||
The images that are built are:
|
||||
- `firmware.elf-0x00000.bin`: to be flashed at 0x00000
|
||||
- `firmware.elf-0x10000.bin`: to be flashed at 0x10000
|
||||
The image produced is `firmware-combined.bin`, to be flashed at 0x00000.
|
||||
|
||||
There is also a combined image, made up of the above 2 binary files with the
|
||||
appropriate padding:
|
||||
- `firmware-combined.bin`: to be flashed at 0x00000
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
While the port is still in alpha, it's known to be generally stable. If you
|
||||
experience strange bootloops, crashes, lockups, here's a list to check against:
|
||||
|
||||
- You didn't erase flash before programming MicroPython firmware.
|
||||
- Firmware can be occasionally flashed incorrectly. Just retry. Recent
|
||||
esptool.py versions have --verify option.
|
||||
- Power supply you use doesn't provide enough power for ESP8266 or isn't
|
||||
stable enough.
|
||||
- A module/flash may be defective (not unheard of for cheap modules).
|
||||
|
||||
Please consult dedicated ESP8266 forums/resources for hardware-related
|
||||
problems.
|
||||
|
||||
@@ -132,12 +132,12 @@ PROVIDE ( ets_memcmp = 0x400018d4 );
|
||||
PROVIDE ( ets_memcpy = 0x400018b4 );
|
||||
PROVIDE ( ets_memmove = 0x400018c4 );
|
||||
PROVIDE ( ets_memset = 0x400018a4 );
|
||||
PROVIDE ( ets_post = 0x40000e24 );
|
||||
PROVIDE ( _ets_post = 0x40000e24 );
|
||||
PROVIDE ( ets_printf = 0x400024cc );
|
||||
PROVIDE ( ets_putc = 0x40002be8 );
|
||||
PROVIDE ( ets_rtc_int_register = 0x40002a40 );
|
||||
PROVIDE ( ets_run = 0x40000e04 );
|
||||
PROVIDE ( ets_set_idle_cb = 0x40000dc0 );
|
||||
PROVIDE ( _ets_run = 0x40000e04 );
|
||||
PROVIDE ( _ets_set_idle_cb = 0x40000dc0 );
|
||||
PROVIDE ( ets_set_user_start = 0x40000fbc );
|
||||
PROVIDE ( ets_str2macaddr = 0x40002af8 );
|
||||
PROVIDE ( ets_strcmp = 0x40002aa8 );
|
||||
@@ -146,12 +146,12 @@ PROVIDE ( ets_strlen = 0x40002ac8 );
|
||||
PROVIDE ( ets_strncmp = 0x40002ab8 );
|
||||
PROVIDE ( ets_strncpy = 0x40002a98 );
|
||||
PROVIDE ( ets_strstr = 0x40002ad8 );
|
||||
PROVIDE ( ets_task = 0x40000dd0 );
|
||||
PROVIDE ( _ets_task = 0x40000dd0 );
|
||||
PROVIDE ( ets_timer_arm = 0x40002cc4 );
|
||||
PROVIDE ( ets_timer_disarm = 0x40002d40 );
|
||||
PROVIDE ( ets_timer_done = 0x40002d80 );
|
||||
PROVIDE ( ets_timer_handler_isr = 0x40002da8 );
|
||||
PROVIDE ( ets_timer_init = 0x40002e68 );
|
||||
PROVIDE ( _ets_timer_init = 0x40002e68 );
|
||||
PROVIDE ( ets_timer_setfn = 0x40002c48 );
|
||||
PROVIDE ( ets_uart_printf = 0x40002544 );
|
||||
PROVIDE ( ets_update_cpu_frequency = 0x40002f04 );
|
||||
@@ -343,5 +343,8 @@ PROVIDE ( xthal_window_spill = 0x4000e324 );
|
||||
PROVIDE ( xthal_window_spill_nw = 0x4000e320 );
|
||||
|
||||
PROVIDE ( Te0 = 0x3fffccf0 );
|
||||
PROVIDE ( Td0 = 0x3fffd100 );
|
||||
PROVIDE ( Td4s = 0x3fffd500);
|
||||
PROVIDE ( rcons = 0x3fffd0f0);
|
||||
PROVIDE ( UartDev = 0x3fffde10 );
|
||||
PROVIDE ( flashchip = 0x3fffc714);
|
||||
|
||||
@@ -5,7 +5,7 @@ MEMORY
|
||||
dport0_0_seg : org = 0x3ff00000, len = 0x10
|
||||
dram0_0_seg : org = 0x3ffe8000, len = 0x14000
|
||||
iram1_0_seg : org = 0x40100000, len = 0x8000
|
||||
irom0_0_seg : org = 0x40210000, len = 0x5A000
|
||||
irom0_0_seg : org = 0x40209000, len = 0x80000
|
||||
}
|
||||
|
||||
/* define the top of RAM */
|
||||
@@ -80,26 +80,84 @@ SECTIONS
|
||||
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
|
||||
|
||||
/* we put some specific text in this section */
|
||||
*py/*.o*(.literal* .text*)
|
||||
*pyexec.o(.literal*, .text*)
|
||||
*readline.o(.literal*, .text*)
|
||||
*pybstdio.o(.literal*, .text*)
|
||||
|
||||
*py/argcheck.o*(.literal* .text*)
|
||||
*py/asm*.o*(.literal* .text*)
|
||||
*py/bc.o*(.literal* .text*)
|
||||
*py/binary.o*(.literal* .text*)
|
||||
*py/builtin*.o*(.literal* .text*)
|
||||
*py/compile.o*(.literal* .text*)
|
||||
*py/emit*.o*(.literal* .text*)
|
||||
*py/formatfloat.o*(.literal* .text*)
|
||||
*py/frozenmod.o*(.literal* .text*)
|
||||
*py/gc.o*(.literal* .text*)
|
||||
*py/lexer*.o*(.literal* .text*)
|
||||
*py/malloc*.o*(.literal* .text*)
|
||||
*py/map*.o*(.literal* .text*)
|
||||
*py/mod*.o*(.literal* .text*)
|
||||
*py/mpprint.o*(.literal* .text*)
|
||||
*py/mpstate.o*(.literal* .text*)
|
||||
*py/mpz.o*(.literal* .text*)
|
||||
*py/native*.o*(.literal* .text*)
|
||||
*py/nlr*.o*(.literal* .text*)
|
||||
*py/obj*.o*(.literal* .text*)
|
||||
*py/opmethods.o*(.literal* .text*)
|
||||
*py/parse*.o*(.literal* .text*)
|
||||
*py/qstr.o*(.literal* .text*)
|
||||
*py/repl.o*(.literal* .text*)
|
||||
*py/runtime.o*(.literal* .text*)
|
||||
*py/scope.o*(.literal* .text*)
|
||||
*py/sequence.o*(.literal* .text*)
|
||||
*py/showbc.o*(.literal* .text*)
|
||||
*py/smallint.o*(.literal* .text*)
|
||||
*py/stackctrl.o*(.literal* .text*)
|
||||
*py/stream.o*(.literal* .text*)
|
||||
*py/unicode.o*(.literal* .text*)
|
||||
*py/vm.o*(.literal* .text*)
|
||||
*py/vstr.o*(.literal* .text*)
|
||||
*py/warning.o*(.literal* .text*)
|
||||
|
||||
*extmod/*.o*(.literal* .text*)
|
||||
|
||||
*lib/fatfs/*.o*(.literal*, .text*)
|
||||
*lib/libm/*.o*(.literal*, .text*)
|
||||
*lib/mp-readline/*.o(.literal*, .text*)
|
||||
*lib/netutils/*.o*(.literal*, .text*)
|
||||
*lib/timeutils/*.o*(.literal*, .text*)
|
||||
*lib/utils/*.o*(.literal*, .text*)
|
||||
|
||||
*stmhal/pybstdio.o(.literal*, .text*)
|
||||
|
||||
*gccollect.o(.literal* .text*)
|
||||
*gchelper.o(.literal* .text*)
|
||||
*lexerstr32.o(.literal* .text*)
|
||||
*utils.o(.literal* .text*)
|
||||
*modpyb.o(.literal*, .text*)
|
||||
*modpybpin.o(.literal*, .text*)
|
||||
*modpybpwm.o(.literal*, .text*)
|
||||
*modpybrtc.o(.literal*, .text*)
|
||||
*modpybadc.o(.literal*, .text*)
|
||||
*modpybuart.o(.literal*, .text*)
|
||||
*modpybi2c.o(.literal*, .text*)
|
||||
*modpybspi.o(.literal*, .text*)
|
||||
*modesp.o(.literal* .text*)
|
||||
*modnetwork.o(.literal* .text*)
|
||||
*moduos.o(.literal* .text*)
|
||||
*modutime.o(.literal* .text*)
|
||||
*modlwip.o(.literal* .text*)
|
||||
*modsocket.o(.literal* .text*)
|
||||
|
||||
/* we put as much rodata as possible in this section */
|
||||
/* note that only rodata accessed as a machine word is allowed here */
|
||||
*py/qstr.o(.rodata.const_pool)
|
||||
*py/*.o(.rodata.mp_type_*) /* catches type: mp_obj_type_t */
|
||||
*py/*.o(.rodata.*_locals_dict*) /* catches types: mp_obj_dict_t, mp_map_elem_t */
|
||||
*py/*.o(.rodata.mp_module_*) /* catches types: mp_obj_module_t, mp_obj_dict_t, mp_map_elem_t */
|
||||
*.o(.rodata.mp_type_*) /* catches type: mp_obj_type_t */
|
||||
*.o(.rodata.*_locals_dict*) /* catches types: mp_obj_dict_t, mp_map_elem_t */
|
||||
*.o(.rodata.mp_module_*) /* catches types: mp_obj_module_t, mp_obj_dict_t, mp_map_elem_t */
|
||||
*/frozen.o(.rodata.mp_frozen_sizes) /* frozen modules */
|
||||
*/frozen.o(.rodata.mp_frozen_content) /* frozen modules */
|
||||
|
||||
/* for -mforce-l32 */
|
||||
build/*.o(.rodata*)
|
||||
|
||||
_irom0_text_end = ABSOLUTE(.);
|
||||
} >irom0_0_seg :irom0_0_phdr
|
||||
@@ -172,6 +230,7 @@ SECTIONS
|
||||
.rodata : ALIGN(4)
|
||||
{
|
||||
_rodata_start = ABSOLUTE(.);
|
||||
*(.sdk.version)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r.*)
|
||||
|
||||
@@ -30,13 +30,24 @@
|
||||
#include "uart.h"
|
||||
#include "esp_mphal.h"
|
||||
#include "user_interface.h"
|
||||
#include "ets_alt_task.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/mpstate.h"
|
||||
#include "extmod/misc.h"
|
||||
#include "lib/utils/pyexec.h"
|
||||
|
||||
extern void ets_wdt_disable(void);
|
||||
extern void wdt_feed(void);
|
||||
extern void ets_delay_us();
|
||||
|
||||
STATIC byte input_buf_array[256];
|
||||
ringbuf_t input_buf = {input_buf_array, sizeof(input_buf_array)};
|
||||
void mp_hal_debug_tx_strn_cooked(void *env, const char *str, uint32_t len);
|
||||
const mp_print_t mp_debug_print = {NULL, mp_hal_debug_tx_strn_cooked};
|
||||
|
||||
void mp_hal_init(void) {
|
||||
ets_wdt_disable(); // it's a pain while developing
|
||||
mp_hal_rtc_init();
|
||||
uart_init(UART_BIT_RATE_115200, UART_BIT_RATE_115200);
|
||||
}
|
||||
|
||||
@@ -47,12 +58,15 @@ void mp_hal_feed_watchdog(void) {
|
||||
}
|
||||
|
||||
void mp_hal_delay_us(uint32_t us) {
|
||||
ets_delay_us(us);
|
||||
uint32_t start = system_get_time();
|
||||
while (system_get_time() - start < us) {
|
||||
ets_event_poll();
|
||||
}
|
||||
}
|
||||
|
||||
int mp_hal_stdin_rx_chr(void) {
|
||||
for (;;) {
|
||||
int c = uart0_rx();
|
||||
int c = ringbuf_get(&input_buf);
|
||||
if (c != -1) {
|
||||
return c;
|
||||
}
|
||||
@@ -61,19 +75,43 @@ int mp_hal_stdin_rx_chr(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_str(const char *str) {
|
||||
void mp_hal_stdout_tx_char(char c) {
|
||||
uart_tx_one_char(UART0, c);
|
||||
mp_uos_dupterm_tx_strn(&c, 1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void mp_hal_debug_str(const char *str) {
|
||||
while (*str) {
|
||||
uart_tx_one_char(UART0, *str++);
|
||||
}
|
||||
uart_flush(UART0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void mp_hal_stdout_tx_str(const char *str) {
|
||||
while (*str) {
|
||||
mp_hal_stdout_tx_char(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn(const char *str, uint32_t len) {
|
||||
while (len--) {
|
||||
uart_tx_one_char(UART0, *str++);
|
||||
mp_hal_stdout_tx_char(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, uint32_t len) {
|
||||
while (len--) {
|
||||
if (*str == '\n') {
|
||||
mp_hal_stdout_tx_char('\r');
|
||||
}
|
||||
mp_hal_stdout_tx_char(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_hal_debug_tx_strn_cooked(void *env, const char *str, uint32_t len) {
|
||||
(void)env;
|
||||
while (len--) {
|
||||
if (*str == '\n') {
|
||||
uart_tx_one_char(UART0, '\r');
|
||||
@@ -86,10 +124,95 @@ uint32_t mp_hal_ticks_ms(void) {
|
||||
return system_get_time() / 1000;
|
||||
}
|
||||
|
||||
uint32_t mp_hal_ticks_us(void) {
|
||||
return system_get_time();
|
||||
}
|
||||
|
||||
void mp_hal_delay_ms(uint32_t delay) {
|
||||
mp_hal_delay_us(delay * 1000);
|
||||
}
|
||||
|
||||
void mp_hal_set_interrupt_char(int c) {
|
||||
// TODO
|
||||
if (c != -1) {
|
||||
mp_obj_exception_clear_traceback(MP_STATE_PORT(mp_kbd_exception));
|
||||
}
|
||||
extern int interrupt_char;
|
||||
interrupt_char = c;
|
||||
}
|
||||
|
||||
void ets_event_poll(void) {
|
||||
ets_loop_iter();
|
||||
if (MP_STATE_VM(mp_pending_exception) != NULL) {
|
||||
mp_obj_t obj = MP_STATE_VM(mp_pending_exception);
|
||||
MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL;
|
||||
nlr_raise(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void __assert_func(const char *file, int line, const char *func, const char *expr) {
|
||||
printf("assert:%s:%d:%s: %s\n", file, line, func, expr);
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_AssertionError,
|
||||
"C-level assert"));
|
||||
}
|
||||
|
||||
void mp_hal_signal_input(void) {
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
system_os_post(UART_TASK_ID, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int call_dupterm_read(void) {
|
||||
if (MP_STATE_PORT(term_obj) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t read_m[3];
|
||||
mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_read, read_m);
|
||||
read_m[2] = MP_OBJ_NEW_SMALL_INT(1);
|
||||
mp_obj_t res = mp_call_method_n_kw(1, 0, read_m);
|
||||
if (res == mp_const_none) {
|
||||
return -2;
|
||||
}
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(res, &bufinfo, MP_BUFFER_READ);
|
||||
if (bufinfo.len == 0) {
|
||||
mp_printf(&mp_plat_print, "dupterm: EOF received, deactivating\n");
|
||||
MP_STATE_PORT(term_obj) = NULL;
|
||||
return -1;
|
||||
}
|
||||
nlr_pop();
|
||||
return *(byte*)bufinfo.buf;
|
||||
} else {
|
||||
// Temporarily disable dupterm to avoid infinite recursion
|
||||
mp_obj_t save_term = MP_STATE_PORT(term_obj);
|
||||
MP_STATE_PORT(term_obj) = NULL;
|
||||
mp_printf(&mp_plat_print, "dupterm: ");
|
||||
mp_obj_print_exception(&mp_plat_print, nlr.ret_val);
|
||||
MP_STATE_PORT(term_obj) = save_term;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
STATIC void dupterm_task_handler(os_event_t *evt) {
|
||||
while (1) {
|
||||
int c = call_dupterm_read();
|
||||
if (c < 0) {
|
||||
break;
|
||||
}
|
||||
ringbuf_put(&input_buf, c);
|
||||
}
|
||||
mp_hal_signal_input();
|
||||
}
|
||||
|
||||
STATIC os_event_t dupterm_evt_queue[4];
|
||||
|
||||
void dupterm_task_init() {
|
||||
system_os_task(dupterm_task_handler, DUPTERM_TASK_ID, dupterm_evt_queue, MP_ARRAY_SIZE(dupterm_evt_queue));
|
||||
}
|
||||
|
||||
void mp_hal_signal_dupterm_input(void) {
|
||||
system_os_post(DUPTERM_TASK_ID, 0, 0);
|
||||
}
|
||||
|
||||
@@ -27,18 +27,33 @@
|
||||
#ifndef _INCLUDED_MPHAL_H_
|
||||
#define _INCLUDED_MPHAL_H_
|
||||
|
||||
// SDK functions not declared in SDK itself
|
||||
void ets_isr_mask(unsigned);
|
||||
#include "py/ringbuf.h"
|
||||
|
||||
struct _mp_print_t;
|
||||
// Structure for UART-only output via mp_printf()
|
||||
extern const struct _mp_print_t mp_debug_print;
|
||||
|
||||
extern ringbuf_t input_buf;
|
||||
// Call this after putting data to input_buf
|
||||
void mp_hal_signal_input(void);
|
||||
// Call this when data is available in dupterm object
|
||||
void mp_hal_signal_dupterm_input(void);
|
||||
|
||||
void mp_hal_init(void);
|
||||
void mp_hal_rtc_init(void);
|
||||
void mp_hal_feed_watchdog(void);
|
||||
|
||||
uint32_t mp_hal_ticks_us(void);
|
||||
void mp_hal_delay_us(uint32_t);
|
||||
void mp_hal_set_interrupt_char(int c);
|
||||
uint32_t mp_hal_get_cpu_freq(void);
|
||||
|
||||
#define UART_TASK_ID 0
|
||||
#define DUPTERM_TASK_ID 1
|
||||
void uart_task_init();
|
||||
void dupterm_task_init();
|
||||
|
||||
void ets_event_poll(void);
|
||||
#define ETS_POLL_WHILE(cond) { while (cond) ets_event_poll(); }
|
||||
|
||||
#endif // _INCLUDED_MPHAL_H_
|
||||
|
||||
64
esp8266/espneopixel.c
Normal file
64
esp8266/espneopixel.c
Normal file
@@ -0,0 +1,64 @@
|
||||
// Original version from https://github.com/adafruit/Adafruit_NeoPixel
|
||||
// Modifications by dpgeorge to support auto-CPU-frequency detection
|
||||
|
||||
// This is a mash-up of the Due show() code + insights from Michael Miller's
|
||||
// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus
|
||||
// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution.
|
||||
|
||||
#include "c_types.h"
|
||||
#include "eagle_soc.h"
|
||||
#include "user_interface.h"
|
||||
#include "espneopixel.h"
|
||||
|
||||
#define NEO_KHZ400 (1)
|
||||
|
||||
static uint32_t _getCycleCount(void) __attribute__((always_inline));
|
||||
static inline uint32_t _getCycleCount(void) {
|
||||
uint32_t ccount;
|
||||
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
|
||||
return ccount;
|
||||
}
|
||||
|
||||
void /*ICACHE_RAM_ATTR*/ esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz) {
|
||||
|
||||
uint8_t *p, *end, pix, mask;
|
||||
uint32_t t, time0, time1, period, c, startTime, pinMask;
|
||||
|
||||
pinMask = 1 << pin;
|
||||
p = pixels;
|
||||
end = p + numBytes;
|
||||
pix = *p++;
|
||||
mask = 0x80;
|
||||
startTime = 0;
|
||||
|
||||
uint32_t fcpu = system_get_cpu_freq() * 1000000;
|
||||
|
||||
#ifdef NEO_KHZ400
|
||||
if(is800KHz) {
|
||||
#endif
|
||||
time0 = fcpu / 2500000; // 0.4us
|
||||
time1 = fcpu / 1250000; // 0.8us
|
||||
period = fcpu / 800000; // 1.25us per bit
|
||||
#ifdef NEO_KHZ400
|
||||
} else { // 400 KHz bitstream
|
||||
time0 = fcpu / 2000000; // 0.5uS
|
||||
time1 = fcpu / 833333; // 1.2us
|
||||
period = fcpu / 400000; // 2.5us per bit
|
||||
}
|
||||
#endif
|
||||
|
||||
for(t = time0;; t = time0) {
|
||||
if(pix & mask) t = time1; // Bit high duration
|
||||
while(((c = _getCycleCount()) - startTime) < period); // Wait for bit start
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinMask); // Set high
|
||||
startTime = c; // Save start time
|
||||
while(((c = _getCycleCount()) - startTime) < t); // Wait high duration
|
||||
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinMask); // Set low
|
||||
if(!(mask >>= 1)) { // Next bit/byte
|
||||
if(p >= end) break;
|
||||
pix = *p++;
|
||||
mask = 0x80;
|
||||
}
|
||||
}
|
||||
while((_getCycleCount() - startTime) < period); // Wait for last bit
|
||||
}
|
||||
1
esp8266/espneopixel.h
Normal file
1
esp8266/espneopixel.h
Normal file
@@ -0,0 +1 @@
|
||||
void esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
|
||||
428
esp8266/esppwm.c
Normal file
428
esp8266/esppwm.c
Normal file
@@ -0,0 +1,428 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2013-2014 Espressif Systems (Wuxi)
|
||||
*
|
||||
* FileName: pwm.c
|
||||
*
|
||||
* Description: pwm driver
|
||||
*
|
||||
* Modification history:
|
||||
* 2014/5/1, v1.0 create this file.
|
||||
* 2016/3/2: Modifications by dpgeorge to suit MicroPython
|
||||
*******************************************************************************/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "etshal.h"
|
||||
#include "os_type.h"
|
||||
#include "gpio.h"
|
||||
|
||||
#include "esppwm.h"
|
||||
|
||||
#include "py/mpprint.h"
|
||||
#define PWM_DBG(...)
|
||||
//#define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
|
||||
|
||||
#define ICACHE_RAM_ATTR // __attribute__((section(".text")))
|
||||
|
||||
#define PWM_CHANNEL 8
|
||||
#define PWM_DEPTH 1023
|
||||
#define PWM_FREQ_MAX 1000
|
||||
#define PWM_1S 1000000
|
||||
|
||||
struct pwm_single_param {
|
||||
uint16_t gpio_set;
|
||||
uint16_t gpio_clear;
|
||||
uint32_t h_time;
|
||||
};
|
||||
|
||||
struct pwm_param {
|
||||
uint32_t period;
|
||||
uint16_t freq;
|
||||
uint16_t duty[PWM_CHANNEL];
|
||||
};
|
||||
|
||||
STATIC const uint8_t pin_num[PWM_CHANNEL] = {0, 2, 4, 5, 12, 13, 14, 15};
|
||||
|
||||
STATIC struct pwm_single_param pwm_single_toggle[2][PWM_CHANNEL + 1];
|
||||
STATIC struct pwm_single_param *pwm_single;
|
||||
|
||||
STATIC struct pwm_param pwm;
|
||||
|
||||
STATIC int8_t pwm_out_io_num[PWM_CHANNEL] = {-1, -1, -1, -1, -1, -1, -1, -1};
|
||||
|
||||
STATIC uint8_t pwm_channel_toggle[2];
|
||||
STATIC uint8_t *pwm_channel;
|
||||
STATIC uint8_t pwm_toggle = 1;
|
||||
STATIC uint8_t pwm_timer_down = 1;
|
||||
STATIC uint8_t pwm_current_channel = 0;
|
||||
STATIC uint16_t pwm_gpio = 0;
|
||||
STATIC uint8_t pwm_channel_num = 0;
|
||||
|
||||
//XXX: 0xffffffff/(80000000/16)=35A
|
||||
#define US_TO_RTC_TIMER_TICKS(t) \
|
||||
((t) ? \
|
||||
(((t) > 0x35A) ? \
|
||||
(((t)>>2) * ((APB_CLK_FREQ>>4)/250000) + ((t)&0x3) * ((APB_CLK_FREQ>>4)/1000000)) : \
|
||||
(((t) *(APB_CLK_FREQ>>4)) / 1000000)) : \
|
||||
0)
|
||||
|
||||
//FRC1
|
||||
#define FRC1_ENABLE_TIMER BIT7
|
||||
|
||||
typedef enum {
|
||||
DIVDED_BY_1 = 0,
|
||||
DIVDED_BY_16 = 4,
|
||||
DIVDED_BY_256 = 8,
|
||||
} TIMER_PREDIVED_MODE;
|
||||
|
||||
typedef enum {
|
||||
TM_LEVEL_INT = 1,
|
||||
TM_EDGE_INT = 0,
|
||||
} TIMER_INT_MODE;
|
||||
|
||||
STATIC void ICACHE_FLASH_ATTR
|
||||
pwm_insert_sort(struct pwm_single_param pwm[], uint8 n)
|
||||
{
|
||||
uint8 i;
|
||||
|
||||
for (i = 1; i < n; i++) {
|
||||
if (pwm[i].h_time < pwm[i - 1].h_time) {
|
||||
int8 j = i - 1;
|
||||
struct pwm_single_param tmp;
|
||||
|
||||
memcpy(&tmp, &pwm[i], sizeof(struct pwm_single_param));
|
||||
memcpy(&pwm[i], &pwm[i - 1], sizeof(struct pwm_single_param));
|
||||
|
||||
while (tmp.h_time < pwm[j].h_time) {
|
||||
memcpy(&pwm[j + 1], &pwm[j], sizeof(struct pwm_single_param));
|
||||
j--;
|
||||
if (j < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&pwm[j + 1], &tmp, sizeof(struct pwm_single_param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC volatile uint8 critical = 0;
|
||||
|
||||
#define LOCK_PWM(c) do { \
|
||||
while( (c)==1 ); \
|
||||
(c) = 1; \
|
||||
} while (0)
|
||||
|
||||
#define UNLOCK_PWM(c) do { \
|
||||
(c) = 0; \
|
||||
} while (0)
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
pwm_start(void)
|
||||
{
|
||||
uint8 i, j;
|
||||
PWM_DBG("--Function pwm_start() is called\n");
|
||||
PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
|
||||
PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
|
||||
PWM_DBG("pwm.period:%d,pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.period,pwm.duty[0],pwm.duty[1],pwm.duty[2]);
|
||||
|
||||
LOCK_PWM(critical); // enter critical
|
||||
|
||||
struct pwm_single_param *local_single = pwm_single_toggle[pwm_toggle ^ 0x01];
|
||||
uint8 *local_channel = &pwm_channel_toggle[pwm_toggle ^ 0x01];
|
||||
|
||||
// step 1: init PWM_CHANNEL+1 channels param
|
||||
for (i = 0; i < pwm_channel_num; i++) {
|
||||
uint32 us = pwm.period * pwm.duty[i] / PWM_DEPTH;
|
||||
local_single[i].h_time = US_TO_RTC_TIMER_TICKS(us);
|
||||
PWM_DBG("i:%d us:%d ht:%d\n",i,us,local_single[i].h_time);
|
||||
local_single[i].gpio_set = 0;
|
||||
local_single[i].gpio_clear = 1 << pin_num[pwm_out_io_num[i]];
|
||||
}
|
||||
|
||||
local_single[pwm_channel_num].h_time = US_TO_RTC_TIMER_TICKS(pwm.period);
|
||||
local_single[pwm_channel_num].gpio_set = pwm_gpio;
|
||||
local_single[pwm_channel_num].gpio_clear = 0;
|
||||
PWM_DBG("i:%d period:%d ht:%d\n",pwm_channel_num,pwm.period,local_single[pwm_channel_num].h_time);
|
||||
// step 2: sort, small to big
|
||||
pwm_insert_sort(local_single, pwm_channel_num + 1);
|
||||
|
||||
*local_channel = pwm_channel_num + 1;
|
||||
PWM_DBG("1channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
|
||||
// step 3: combine same duty channels
|
||||
for (i = pwm_channel_num; i > 0; i--) {
|
||||
if (local_single[i].h_time == local_single[i - 1].h_time) {
|
||||
local_single[i - 1].gpio_set |= local_single[i].gpio_set;
|
||||
local_single[i - 1].gpio_clear |= local_single[i].gpio_clear;
|
||||
|
||||
for (j = i + 1; j < *local_channel; j++) {
|
||||
memcpy(&local_single[j - 1], &local_single[j], sizeof(struct pwm_single_param));
|
||||
}
|
||||
|
||||
(*local_channel)--;
|
||||
}
|
||||
}
|
||||
PWM_DBG("2channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
|
||||
// step 4: cacl delt time
|
||||
for (i = *local_channel - 1; i > 0; i--) {
|
||||
local_single[i].h_time -= local_single[i - 1].h_time;
|
||||
}
|
||||
|
||||
// step 5: last channel needs to clean
|
||||
local_single[*local_channel-1].gpio_clear = 0;
|
||||
|
||||
// step 6: if first channel duty is 0, remove it
|
||||
if (local_single[0].h_time == 0) {
|
||||
local_single[*local_channel - 1].gpio_set &= ~local_single[0].gpio_clear;
|
||||
local_single[*local_channel - 1].gpio_clear |= local_single[0].gpio_clear;
|
||||
|
||||
for (i = 1; i < *local_channel; i++) {
|
||||
memcpy(&local_single[i - 1], &local_single[i], sizeof(struct pwm_single_param));
|
||||
}
|
||||
|
||||
(*local_channel)--;
|
||||
}
|
||||
|
||||
// if timer is down, need to set gpio and start timer
|
||||
if (pwm_timer_down == 1) {
|
||||
pwm_channel = local_channel;
|
||||
pwm_single = local_single;
|
||||
// start
|
||||
gpio_output_set(local_single[0].gpio_set, local_single[0].gpio_clear, pwm_gpio, 0);
|
||||
|
||||
// yeah, if all channels' duty is 0 or 255, don't need to start timer, otherwise start...
|
||||
if (*local_channel != 1) {
|
||||
pwm_timer_down = 0;
|
||||
RTC_REG_WRITE(FRC1_LOAD_ADDRESS, local_single[0].h_time);
|
||||
}
|
||||
}
|
||||
|
||||
if (pwm_toggle == 1) {
|
||||
pwm_toggle = 0;
|
||||
} else {
|
||||
pwm_toggle = 1;
|
||||
}
|
||||
|
||||
UNLOCK_PWM(critical); // leave critical
|
||||
PWM_DBG("3channel:%d,single[0]:%d,[1]:%d,[2]:%d,[3]:%d\n",*local_channel,local_single[0].h_time,local_single[1].h_time,local_single[2].h_time,local_single[3].h_time);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_set_duty
|
||||
* Description : set each channel's duty params
|
||||
* Parameters : uint8 duty : 0 ~ PWM_DEPTH
|
||||
* uint8 channel : channel index
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
pwm_set_duty(uint16 duty, uint8 channel)
|
||||
{
|
||||
uint8 i;
|
||||
for(i=0;i<pwm_channel_num;i++){
|
||||
if(pwm_out_io_num[i] == channel){
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i==pwm_channel_num) // non found
|
||||
return;
|
||||
|
||||
LOCK_PWM(critical); // enter critical
|
||||
if (duty < 1) {
|
||||
pwm.duty[channel] = 0;
|
||||
} else if (duty >= PWM_DEPTH) {
|
||||
pwm.duty[channel] = PWM_DEPTH;
|
||||
} else {
|
||||
pwm.duty[channel] = duty;
|
||||
}
|
||||
UNLOCK_PWM(critical); // leave critical
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_set_freq
|
||||
* Description : set pwm frequency
|
||||
* Parameters : uint16 freq : 100hz typically
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
pwm_set_freq(uint16 freq, uint8 channel)
|
||||
{
|
||||
LOCK_PWM(critical); // enter critical
|
||||
if (freq > PWM_FREQ_MAX) {
|
||||
pwm.freq = PWM_FREQ_MAX;
|
||||
} else if (freq < 1) {
|
||||
pwm.freq = 1;
|
||||
} else {
|
||||
pwm.freq = freq;
|
||||
}
|
||||
|
||||
pwm.period = PWM_1S / pwm.freq;
|
||||
UNLOCK_PWM(critical); // leave critical
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_get_duty
|
||||
* Description : get duty of each channel
|
||||
* Parameters : uint8 channel : channel index
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
uint16 ICACHE_FLASH_ATTR
|
||||
pwm_get_duty(uint8 channel)
|
||||
{
|
||||
uint8 i;
|
||||
for(i=0;i<pwm_channel_num;i++){
|
||||
if(pwm_out_io_num[i] == channel){
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i==pwm_channel_num) // non found
|
||||
return 0;
|
||||
|
||||
return pwm.duty[channel];
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_get_freq
|
||||
* Description : get pwm frequency
|
||||
* Parameters : NONE
|
||||
* Returns : uint16 : pwm frequency
|
||||
*******************************************************************************/
|
||||
uint16 ICACHE_FLASH_ATTR
|
||||
pwm_get_freq(uint8 channel)
|
||||
{
|
||||
return pwm.freq;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_period_timer
|
||||
* Description : pwm period timer function, output high level,
|
||||
* start each channel's high level timer
|
||||
* Parameters : NONE
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
STATIC void ICACHE_RAM_ATTR
|
||||
pwm_tim1_intr_handler(void *dummy)
|
||||
{
|
||||
(void)dummy;
|
||||
uint8 local_toggle = pwm_toggle; // pwm_toggle may change outside
|
||||
RTC_CLR_REG_MASK(FRC1_INT_ADDRESS, FRC1_INT_CLR_MASK);
|
||||
|
||||
if (pwm_current_channel >= (*pwm_channel - 1)) { // *pwm_channel may change outside
|
||||
pwm_single = pwm_single_toggle[local_toggle];
|
||||
pwm_channel = &pwm_channel_toggle[local_toggle];
|
||||
|
||||
gpio_output_set(pwm_single[*pwm_channel - 1].gpio_set,
|
||||
pwm_single[*pwm_channel - 1].gpio_clear,
|
||||
pwm_gpio,
|
||||
0);
|
||||
|
||||
pwm_current_channel = 0;
|
||||
|
||||
if (*pwm_channel != 1) {
|
||||
RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time);
|
||||
} else {
|
||||
pwm_timer_down = 1;
|
||||
}
|
||||
} else {
|
||||
gpio_output_set(pwm_single[pwm_current_channel].gpio_set,
|
||||
pwm_single[pwm_current_channel].gpio_clear,
|
||||
pwm_gpio, 0);
|
||||
|
||||
pwm_current_channel++;
|
||||
RTC_REG_WRITE(FRC1_LOAD_ADDRESS, pwm_single[pwm_current_channel].h_time);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : pwm_init
|
||||
* Description : pwm gpio, params and timer initialization
|
||||
* Parameters : uint16 freq : pwm freq param
|
||||
* uint16 *duty : each channel's duty
|
||||
* Returns : NONE
|
||||
*******************************************************************************/
|
||||
void ICACHE_FLASH_ATTR
|
||||
pwm_init(void)
|
||||
{
|
||||
uint8 i;
|
||||
|
||||
RTC_REG_WRITE(FRC1_CTRL_ADDRESS, //FRC2_AUTO_RELOAD|
|
||||
DIVDED_BY_16
|
||||
| FRC1_ENABLE_TIMER
|
||||
| TM_EDGE_INT);
|
||||
RTC_REG_WRITE(FRC1_LOAD_ADDRESS, 0);
|
||||
|
||||
for (i = 0; i < PWM_CHANNEL; i++) {
|
||||
pwm_gpio = 0;
|
||||
pwm.duty[i] = 0;
|
||||
}
|
||||
|
||||
pwm_set_freq(500, 0);
|
||||
pwm_start();
|
||||
|
||||
ETS_FRC_TIMER1_INTR_ATTACH(pwm_tim1_intr_handler, NULL);
|
||||
TM1_EDGE_INT_ENABLE();
|
||||
ETS_FRC1_INTR_ENABLE();
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR
|
||||
pwm_add(uint8_t pin_id, uint32_t pin_mux, uint32_t pin_func){
|
||||
PWM_DBG("--Function pwm_add() is called. channel:%d\n", channel);
|
||||
PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
|
||||
PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
|
||||
PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]);
|
||||
int channel = -1;
|
||||
for (int i = 0; i < PWM_CHANNEL; ++i) {
|
||||
if (pin_num[i] == pin_id) {
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (channel == -1) {
|
||||
return -1;
|
||||
}
|
||||
uint8 i;
|
||||
for(i=0;i<PWM_CHANNEL;i++){
|
||||
if(pwm_out_io_num[i]==channel) // already exist
|
||||
return channel;
|
||||
if(pwm_out_io_num[i] == -1){ // empty exist
|
||||
LOCK_PWM(critical); // enter critical
|
||||
pwm_out_io_num[i] = channel;
|
||||
pwm.duty[i] = 0;
|
||||
pwm_gpio |= (1 << pin_num[channel]);
|
||||
PIN_FUNC_SELECT(pin_mux, pin_func);
|
||||
GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel])), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[channel]))) & (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain;
|
||||
pwm_channel_num++;
|
||||
UNLOCK_PWM(critical); // leave critical
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ICACHE_FLASH_ATTR
|
||||
pwm_delete(uint8 channel){
|
||||
PWM_DBG("--Function pwm_delete() is called. channel:%d\n", channel);
|
||||
PWM_DBG("pwm_gpio:%x,pwm_channel_num:%d\n",pwm_gpio,pwm_channel_num);
|
||||
PWM_DBG("pwm_out_io_num[0]:%d,[1]:%d,[2]:%d\n",pwm_out_io_num[0],pwm_out_io_num[1],pwm_out_io_num[2]);
|
||||
PWM_DBG("pwm.duty[0]:%d,[1]:%d,[2]:%d\n",pwm.duty[0],pwm.duty[1],pwm.duty[2]);
|
||||
uint8 i,j;
|
||||
for(i=0;i<pwm_channel_num;i++){
|
||||
if(pwm_out_io_num[i]==channel){ // exist
|
||||
LOCK_PWM(critical); // enter critical
|
||||
pwm_out_io_num[i] = -1;
|
||||
pwm_gpio &= ~(1 << pin_num[channel]); //clear the bit
|
||||
for(j=i;j<pwm_channel_num-1;j++){
|
||||
pwm_out_io_num[j] = pwm_out_io_num[j+1];
|
||||
pwm.duty[j] = pwm.duty[j+1];
|
||||
}
|
||||
pwm_out_io_num[pwm_channel_num-1] = -1;
|
||||
pwm.duty[pwm_channel_num-1] = 0;
|
||||
pwm_channel_num--;
|
||||
UNLOCK_PWM(critical); // leave critical
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// non found
|
||||
return true;
|
||||
}
|
||||
17
esp8266/esppwm.h
Normal file
17
esp8266/esppwm.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef __ESPPWM_H__
|
||||
#define __ESPPWM_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void pwm_init(void);
|
||||
void pwm_start(void);
|
||||
|
||||
void pwm_set_duty(uint16_t duty, uint8_t channel);
|
||||
uint16_t pwm_get_duty(uint8_t channel);
|
||||
void pwm_set_freq(uint16_t freq, uint8_t channel);
|
||||
uint16_t pwm_get_freq(uint8_t channel);
|
||||
int pwm_add(uint8_t pin_id, uint32_t pin_mux, uint32_t pin_func);
|
||||
bool pwm_delete(uint8_t channel);
|
||||
|
||||
#endif
|
||||
194
esp8266/ets_alt_task.c
Normal file
194
esp8266/ets_alt_task.c
Normal file
@@ -0,0 +1,194 @@
|
||||
#include <stdio.h>
|
||||
#include "osapi.h"
|
||||
#include "os_type.h"
|
||||
#include "ets_sys.h"
|
||||
#include <esp_sdk_ver.h>
|
||||
#include "etshal.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
// Use standard ets_task or alternative impl
|
||||
#define USE_ETS_TASK 0
|
||||
|
||||
#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
struct task_entry {
|
||||
os_event_t *queue;
|
||||
os_task_t task;
|
||||
uint8_t qlen;
|
||||
uint8_t prio;
|
||||
int8_t i_get;
|
||||
int8_t i_put;
|
||||
};
|
||||
|
||||
static void (*idle_cb)(void *);
|
||||
static void *idle_arg;
|
||||
|
||||
#if ESP_SDK_VERSION >= 010500
|
||||
# define FIRST_PRIO 0
|
||||
#else
|
||||
# define FIRST_PRIO 0x14
|
||||
#endif
|
||||
#define LAST_PRIO 0x20
|
||||
#define PRIO2ID(prio) ((prio) - FIRST_PRIO)
|
||||
|
||||
volatile struct task_entry emu_tasks[PRIO2ID(LAST_PRIO) + 1];
|
||||
|
||||
static inline int prio2id(uint8_t prio) {
|
||||
int id = PRIO2ID(prio);
|
||||
if (id < 0 || id >= MP_ARRAY_SIZE(emu_tasks)) {
|
||||
printf("task prio out of range: %d\n", prio);
|
||||
while (1);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
void dump_task(int prio, volatile struct task_entry *t) {
|
||||
printf("q for task %d: queue: %p, get ptr: %d, put ptr: %d, qlen: %d\n",
|
||||
prio, t->queue, t->i_get, t->i_put, t->qlen);
|
||||
}
|
||||
|
||||
void dump_tasks(void) {
|
||||
for (int i = 0; i < MP_ARRAY_SIZE(emu_tasks); i++) {
|
||||
if (emu_tasks[i].qlen) {
|
||||
dump_task(i + FIRST_PRIO, &emu_tasks[i]);
|
||||
}
|
||||
}
|
||||
printf("====\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ets_task(os_task_t task, uint8 prio, os_event_t *queue, uint8 qlen) {
|
||||
static unsigned cnt;
|
||||
printf("#%d ets_task(%p, %d, %p, %d)\n", cnt++, task, prio, queue, qlen);
|
||||
#if USE_ETS_TASK
|
||||
return _ets_task(task, prio, queue, qlen);
|
||||
#else
|
||||
int id = prio2id(prio);
|
||||
emu_tasks[id].task = task;
|
||||
emu_tasks[id].queue = queue;
|
||||
emu_tasks[id].qlen = qlen;
|
||||
emu_tasks[id].i_get = 0;
|
||||
emu_tasks[id].i_put = 0;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ets_post(uint8 prio, os_signal_t sig, os_param_t param) {
|
||||
// static unsigned cnt; printf("#%d ets_post(%d, %x, %x)\n", cnt++, prio, sig, param);
|
||||
#if USE_ETS_TASK
|
||||
return _ets_post(prio, sig, param);
|
||||
#else
|
||||
ets_intr_lock();
|
||||
|
||||
const int id = prio2id(prio);
|
||||
os_event_t *q = emu_tasks[id].queue;
|
||||
if (emu_tasks[id].i_put == -1) {
|
||||
// queue is full
|
||||
printf("ets_post: task %d queue full\n", prio);
|
||||
return false;
|
||||
}
|
||||
q = &q[emu_tasks[id].i_put++];
|
||||
q->sig = sig;
|
||||
q->par = param;
|
||||
if (emu_tasks[id].i_put == emu_tasks[id].qlen) {
|
||||
emu_tasks[id].i_put = 0;
|
||||
}
|
||||
if (emu_tasks[id].i_put == emu_tasks[id].i_get) {
|
||||
// queue got full
|
||||
emu_tasks[id].i_put = -1;
|
||||
}
|
||||
//printf("after ets_post: "); dump_task(prio, &emu_tasks[id]);
|
||||
//dump_tasks();
|
||||
|
||||
ets_intr_unlock();
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ets_loop_iter(void) {
|
||||
//static unsigned cnt;
|
||||
bool progress = false;
|
||||
for (volatile struct task_entry *t = emu_tasks; t < &emu_tasks[MP_ARRAY_SIZE(emu_tasks)]; t++) {
|
||||
system_soft_wdt_feed();
|
||||
ets_intr_lock();
|
||||
//printf("etc_loop_iter: "); dump_task(t - emu_tasks + FIRST_PRIO, t);
|
||||
if (t->i_get != t->i_put) {
|
||||
progress = true;
|
||||
//printf("#%d Calling task %d(%p) (%x, %x)\n", cnt++,
|
||||
// t - emu_tasks + FIRST_PRIO, t->task, t->queue[t->i_get].sig, t->queue[t->i_get].par);
|
||||
int idx = t->i_get;
|
||||
if (t->i_put == -1) {
|
||||
t->i_put = t->i_get;
|
||||
}
|
||||
if (++t->i_get == t->qlen) {
|
||||
t->i_get = 0;
|
||||
}
|
||||
//ets_intr_unlock();
|
||||
t->task(&t->queue[idx]);
|
||||
//ets_intr_lock();
|
||||
//printf("Done calling task %d\n", t - emu_tasks + FIRST_PRIO);
|
||||
}
|
||||
ets_intr_unlock();
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
#if SDK_BELOW_1_1_1
|
||||
void my_timer_isr(void *arg) {
|
||||
// uart0_write_char('+');
|
||||
ets_post(0x1f, 0, 0);
|
||||
}
|
||||
|
||||
// Timer init func is in ROM, and calls ets_task by relative addr directly in ROM
|
||||
// so, we have to re-init task using our handler
|
||||
void ets_timer_init() {
|
||||
printf("ets_timer_init\n");
|
||||
// _ets_timer_init();
|
||||
ets_isr_attach(10, my_timer_isr, NULL);
|
||||
SET_PERI_REG_MASK(0x3FF00004, 4);
|
||||
ETS_INTR_ENABLE(10);
|
||||
ets_task((os_task_t)0x40002E3C, 0x1f, (os_event_t*)0x3FFFDDC0, 4);
|
||||
|
||||
WRITE_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x30, 0);
|
||||
WRITE_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x28, 0x88);
|
||||
WRITE_PERI_REG(PERIPHS_TIMER_BASEDDR + 0x30, 0);
|
||||
printf("Installed timer ISR\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ets_run(void) {
|
||||
#if USE_ETS_TASK
|
||||
#if SDK_BELOW_1_1_1
|
||||
ets_isr_attach(10, my_timer_isr, NULL);
|
||||
#endif
|
||||
_ets_run();
|
||||
#else
|
||||
// ets_timer_init();
|
||||
*(char*)0x3FFFC6FC = 0;
|
||||
ets_intr_lock();
|
||||
printf("ets_alt_task: ets_run\n");
|
||||
#if DEBUG
|
||||
dump_tasks();
|
||||
#endif
|
||||
ets_intr_unlock();
|
||||
while (1) {
|
||||
if (!ets_loop_iter()) {
|
||||
//printf("idle\n");
|
||||
ets_intr_lock();
|
||||
if (idle_cb) {
|
||||
idle_cb(idle_arg);
|
||||
}
|
||||
asm("waiti 0");
|
||||
ets_intr_unlock();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ets_set_idle_cb(void (*handler)(void *), void *arg) {
|
||||
//printf("ets_set_idle_cb(%p, %p)\n", handler, arg);
|
||||
idle_cb = handler;
|
||||
idle_arg = arg;
|
||||
}
|
||||
1
esp8266/ets_alt_task.h
Normal file
1
esp8266/ets_alt_task.h
Normal file
@@ -0,0 +1 @@
|
||||
bool ets_loop_iter(void);
|
||||
@@ -1,9 +1,23 @@
|
||||
#ifndef _INCLUDED_ETSHAL_H_
|
||||
#define _INCLUDED_ETSHAL_H_
|
||||
|
||||
void ets_isr_unmask();
|
||||
#include <os_type.h>
|
||||
|
||||
// see http://esp8266-re.foogod.com/wiki/Random_Number_Generator
|
||||
#define WDEV_HWRNG ((volatile uint32_t*)0x3ff20e44)
|
||||
|
||||
void ets_delay_us();
|
||||
void ets_intr_lock(void);
|
||||
void ets_intr_unlock(void);
|
||||
void ets_isr_mask(uint32_t mask);
|
||||
void ets_isr_unmask(uint32_t mask);
|
||||
void ets_isr_attach(int irq_no, void (*handler)(void *), void *arg);
|
||||
void ets_install_putc1();
|
||||
void ets_isr_attach();
|
||||
void uart_div_modify();
|
||||
void ets_set_idle_cb(void (*handler)(void *), void *arg);
|
||||
|
||||
void ets_timer_arm_new(os_timer_t *tim, uint32_t millis, bool repeat, bool is_milli_timer);
|
||||
void ets_timer_setfn(os_timer_t *tim, ETSTimerFunc callback, void *cb_data);
|
||||
void ets_timer_disarm(os_timer_t *tim);
|
||||
|
||||
#endif // _INCLUDED_ETSHAL_H_
|
||||
|
||||
6
esp8266/fatfs_port.c
Normal file
6
esp8266/fatfs_port.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "lib/fatfs/ff.h"
|
||||
#include "lib/fatfs/diskio.h"
|
||||
|
||||
DWORD get_fattime(void) {
|
||||
return 0;
|
||||
}
|
||||
@@ -39,10 +39,6 @@ void gc_collect(void) {
|
||||
// start the GC
|
||||
gc_collect_start();
|
||||
|
||||
// We need to scan everything in RAM that can hold a pointer.
|
||||
// The data segment is used, but should not contain pointers, so we just scan the bss.
|
||||
gc_collect_root((void**)&_bss_start, ((uint32_t)&_bss_end - (uint32_t)&_bss_start) / sizeof(uint32_t));
|
||||
|
||||
// get the registers and the sp
|
||||
mp_uint_t regs[8];
|
||||
mp_uint_t sp = gc_helper_get_regs_and_sp(regs);
|
||||
|
||||
69
esp8266/lexerstr32.c
Normal file
69
esp8266/lexerstr32.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2016 Damien P. George
|
||||
* Copyright (c) 2016 Paul Sokolovsky
|
||||
*
|
||||
* 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 "py/lexer.h"
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
|
||||
typedef struct _mp_lexer_str32_buf_t {
|
||||
const uint32_t *src_cur;
|
||||
uint32_t val;
|
||||
uint8_t byte_off;
|
||||
} mp_lexer_str32_buf_t;
|
||||
|
||||
STATIC mp_uint_t str32_buf_next_byte(mp_lexer_str32_buf_t *sb) {
|
||||
byte c = sb->val & 0xff;
|
||||
if (c == 0) {
|
||||
return MP_LEXER_EOF;
|
||||
}
|
||||
|
||||
if (++sb->byte_off > 3) {
|
||||
sb->byte_off = 0;
|
||||
sb->val = *sb->src_cur++;
|
||||
} else {
|
||||
sb->val >>= 8;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
STATIC void str32_buf_free(mp_lexer_str32_buf_t *sb) {
|
||||
m_del_obj(mp_lexer_str32_buf_t, sb);
|
||||
}
|
||||
|
||||
mp_lexer_t *mp_lexer_new_from_str32(qstr src_name, const char *str, mp_uint_t len, mp_uint_t free_len) {
|
||||
mp_lexer_str32_buf_t *sb = m_new_obj_maybe(mp_lexer_str32_buf_t);
|
||||
if (sb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
sb->byte_off = (uint32_t)str & 3;
|
||||
sb->src_cur = (uint32_t*)(str - sb->byte_off);
|
||||
sb->val = *sb->src_cur++ >> sb->byte_off * 8;
|
||||
return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_byte_t)str32_buf_next_byte, (mp_lexer_stream_close_t)str32_buf_free);
|
||||
}
|
||||
|
||||
#endif // MICROPY_ENABLE_COMPILER
|
||||
@@ -38,51 +38,106 @@
|
||||
#include "gccollect.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
STATIC char heap[16384];
|
||||
STATIC char heap[24 * 1024];
|
||||
|
||||
STATIC void mp_reset(void) {
|
||||
mp_stack_set_limit(10240);
|
||||
mp_stack_set_top((void*)0x40000000);
|
||||
mp_stack_set_limit(8192);
|
||||
mp_hal_init();
|
||||
gc_init(heap, heap + sizeof(heap));
|
||||
mp_init();
|
||||
mp_obj_list_init(mp_sys_path, 0);
|
||||
mp_obj_list_init(mp_sys_argv, 0);
|
||||
#if MICROPY_VFS_FAT
|
||||
memset(MP_STATE_PORT(fs_user_mount), 0, sizeof(MP_STATE_PORT(fs_user_mount)));
|
||||
#endif
|
||||
MP_STATE_PORT(mp_kbd_exception) = mp_obj_new_exception(&mp_type_KeyboardInterrupt);
|
||||
MP_STATE_PORT(term_obj) = MP_OBJ_NULL;
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
pyexec_frozen_module("main");
|
||||
pyexec_frozen_module("_boot");
|
||||
pyexec_file("boot.py");
|
||||
pyexec_file("main.py");
|
||||
#endif
|
||||
}
|
||||
|
||||
void soft_reset(void) {
|
||||
mp_hal_stdout_tx_str("PYB: soft reset\r\n");
|
||||
mp_hal_stdout_tx_str("PYB: soft reboot\r\n");
|
||||
mp_hal_delay_us(10000); // allow UART to flush output
|
||||
mp_reset();
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
pyexec_event_repl_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void init_done(void) {
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
uart_task_init();
|
||||
#endif
|
||||
mp_reset();
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
pyexec_event_repl_init();
|
||||
uart_task_init();
|
||||
#endif
|
||||
dupterm_task_init();
|
||||
|
||||
#if !MICROPY_REPL_EVENT_DRIVEN
|
||||
soft_reset:
|
||||
for (;;) {
|
||||
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
|
||||
if (pyexec_raw_repl() != 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (pyexec_friendly_repl() != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
soft_reset();
|
||||
goto soft_reset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void user_init(void) {
|
||||
system_init_done_cb(init_done);
|
||||
}
|
||||
|
||||
mp_lexer_t *fat_vfs_lexer_new_from_file(const char *filename);
|
||||
mp_import_stat_t fat_vfs_import_stat(const char *path);
|
||||
|
||||
mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
|
||||
#if MICROPY_VFS_FAT
|
||||
return fat_vfs_lexer_new_from_file(filename);
|
||||
#else
|
||||
(void)filename;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
mp_import_stat_t mp_import_stat(const char *path) {
|
||||
#if MICROPY_VFS_FAT
|
||||
return fat_vfs_import_stat(path);
|
||||
#else
|
||||
(void)path;
|
||||
return MP_IMPORT_STAT_NO_EXIST;
|
||||
#endif
|
||||
}
|
||||
|
||||
mp_obj_t vfs_proxy_call(qstr method_name, mp_uint_t n_args, const mp_obj_t *args);
|
||||
mp_obj_t mp_builtin_open(uint n_args, const mp_obj_t *args, mp_map_t *kwargs) {
|
||||
#if MICROPY_VFS_FAT
|
||||
// TODO: Handle kwargs!
|
||||
return vfs_proxy_call(MP_QSTR_open, n_args, args);
|
||||
#else
|
||||
return mp_const_none;
|
||||
#endif
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
|
||||
|
||||
void mp_keyboard_interrupt(void) {
|
||||
MP_STATE_VM(mp_pending_exception) = MP_STATE_PORT(mp_kbd_exception);
|
||||
}
|
||||
|
||||
void nlr_jump_fail(void *val) {
|
||||
printf("NLR jump failed\n");
|
||||
for (;;) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import sys
|
||||
|
||||
SEGS_MAX_SIZE = 0x9000
|
||||
|
||||
assert len(sys.argv) == 4
|
||||
|
||||
with open(sys.argv[3], 'wb') as fout:
|
||||
@@ -9,7 +11,7 @@ with open(sys.argv[3], 'wb') as fout:
|
||||
fout.write(data_flash)
|
||||
print('flash ', len(data_flash))
|
||||
|
||||
pad = b'\xff' * (0x10000 - len(data_flash))
|
||||
pad = b'\xff' * (SEGS_MAX_SIZE - len(data_flash))
|
||||
fout.write(pad)
|
||||
print('padding ', len(pad))
|
||||
|
||||
@@ -18,4 +20,4 @@ with open(sys.argv[3], 'wb') as fout:
|
||||
fout.write(data_rom)
|
||||
print('irom0text', len(data_rom))
|
||||
|
||||
print('total ', 0x10000 + len(data_rom))
|
||||
print('total ', SEGS_MAX_SIZE + len(data_rom))
|
||||
|
||||
120
esp8266/modesp.c
120
esp8266/modesp.c
@@ -35,12 +35,16 @@
|
||||
#include "py/runtime.h"
|
||||
#include "netutils.h"
|
||||
#include "queue.h"
|
||||
#include "ets_sys.h"
|
||||
#include "uart.h"
|
||||
#include "user_interface.h"
|
||||
#include "espconn.h"
|
||||
#include "spi_flash.h"
|
||||
#include "utils.h"
|
||||
#include "espneopixel.h"
|
||||
#include "modpyb.h"
|
||||
|
||||
#define MODESP_ESPCONN (1)
|
||||
#define MODESP_ESPCONN (0)
|
||||
|
||||
#if MODESP_ESPCONN
|
||||
STATIC const mp_obj_type_t esp_socket_type;
|
||||
@@ -512,25 +516,15 @@ void error_check(bool status, const char *msg) {
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t esp_wifi_mode(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
return mp_obj_new_int(wifi_get_opmode());
|
||||
STATIC mp_obj_t esp_osdebug(mp_obj_t val) {
|
||||
if (val == mp_const_none) {
|
||||
uart_os_config(-1);
|
||||
} else {
|
||||
wifi_set_opmode(mp_obj_get_int(args[0]));
|
||||
return mp_const_none;
|
||||
uart_os_config(mp_obj_get_int(val));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_wifi_mode_obj, 0, 1, esp_wifi_mode);
|
||||
|
||||
STATIC mp_obj_t esp_phy_mode(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
return mp_obj_new_int(wifi_get_phy_mode());
|
||||
} else {
|
||||
wifi_set_phy_mode(mp_obj_get_int(args[0]));
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_phy_mode_obj, 0, 1, esp_phy_mode);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_osdebug_obj, esp_osdebug);
|
||||
|
||||
STATIC mp_obj_t esp_sleep_type(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
@@ -553,42 +547,106 @@ STATIC mp_obj_t esp_flash_id() {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_id_obj, esp_flash_id);
|
||||
|
||||
STATIC mp_obj_t esp_flash_read(mp_obj_t offset_in, mp_obj_t len_in) {
|
||||
STATIC mp_obj_t esp_flash_read(mp_obj_t offset_in, mp_obj_t len_or_buf_in) {
|
||||
mp_int_t offset = mp_obj_get_int(offset_in);
|
||||
mp_int_t len = mp_obj_get_int(len_in);
|
||||
byte *buf = m_new(byte, len);
|
||||
|
||||
mp_int_t len;
|
||||
byte *buf;
|
||||
bool alloc_buf = MP_OBJ_IS_INT(len_or_buf_in);
|
||||
|
||||
if (alloc_buf) {
|
||||
len = mp_obj_get_int(len_or_buf_in);
|
||||
buf = m_new(byte, len);
|
||||
} else {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(len_or_buf_in, &bufinfo, MP_BUFFER_WRITE);
|
||||
len = bufinfo.len;
|
||||
buf = bufinfo.buf;
|
||||
}
|
||||
|
||||
// We know that allocation will be 4-byte aligned for sure
|
||||
SpiFlashOpResult res = spi_flash_read(offset, (uint32_t*)buf, len);
|
||||
if (res == SPI_FLASH_RESULT_OK) {
|
||||
return mp_obj_new_bytes(buf, len);
|
||||
if (alloc_buf) {
|
||||
return mp_obj_new_bytes(buf, len);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
if (alloc_buf) {
|
||||
m_del(byte, buf, len);
|
||||
}
|
||||
m_del(byte, buf, len);
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(res == SPI_FLASH_RESULT_TIMEOUT ? ETIMEDOUT : EIO)));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_flash_read_obj, esp_flash_read);
|
||||
|
||||
STATIC mp_obj_t esp_flash_write(mp_obj_t offset_in, const mp_obj_t buf_in) {
|
||||
mp_int_t offset = mp_obj_get_int(offset_in);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
|
||||
if (bufinfo.len & 0x3) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "len must be multiple of 4"));
|
||||
}
|
||||
SpiFlashOpResult res = spi_flash_write(offset, bufinfo.buf, bufinfo.len);
|
||||
if (res == SPI_FLASH_RESULT_OK) {
|
||||
return mp_const_none;
|
||||
}
|
||||
nlr_raise(mp_obj_new_exception_arg1(
|
||||
&mp_type_OSError,
|
||||
MP_OBJ_NEW_SMALL_INT(res == SPI_FLASH_RESULT_TIMEOUT ? ETIMEDOUT : EIO)));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_flash_write_obj, esp_flash_write);
|
||||
|
||||
STATIC mp_obj_t esp_flash_erase(mp_obj_t sector_in) {
|
||||
mp_int_t sector = mp_obj_get_int(sector_in);
|
||||
SpiFlashOpResult res = spi_flash_erase_sector(sector);
|
||||
if (res == SPI_FLASH_RESULT_OK) {
|
||||
return mp_const_none;
|
||||
}
|
||||
nlr_raise(mp_obj_new_exception_arg1(
|
||||
&mp_type_OSError,
|
||||
MP_OBJ_NEW_SMALL_INT(res == SPI_FLASH_RESULT_TIMEOUT ? ETIMEDOUT : EIO)));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_flash_erase_obj, esp_flash_erase);
|
||||
|
||||
STATIC mp_obj_t esp_neopixel_write_(mp_obj_t pin, mp_obj_t buf, mp_obj_t is800k) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
|
||||
esp_neopixel_write(mp_obj_get_pin_obj(pin)->phys_port,
|
||||
(uint8_t*)bufinfo.buf, bufinfo.len, mp_obj_is_true(is800k));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_neopixel_write_obj, esp_neopixel_write_);
|
||||
|
||||
STATIC mp_obj_t esp_freemem() {
|
||||
return MP_OBJ_NEW_SMALL_INT(system_get_free_heap_size());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_freemem_obj, esp_freemem);
|
||||
|
||||
STATIC mp_obj_t esp_meminfo() {
|
||||
system_print_meminfo();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_meminfo_obj, esp_meminfo);
|
||||
|
||||
STATIC const mp_map_elem_t esp_module_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_esp) },
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_wifi_mode), (mp_obj_t)&esp_wifi_mode_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_phy_mode), (mp_obj_t)&esp_phy_mode_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_osdebug), (mp_obj_t)&esp_osdebug_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sleep_type), (mp_obj_t)&esp_sleep_type_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_deepsleep), (mp_obj_t)&esp_deepsleep_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_flash_id), (mp_obj_t)&esp_flash_id_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_flash_read), (mp_obj_t)&esp_flash_read_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_flash_write), (mp_obj_t)&esp_flash_write_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_flash_erase), (mp_obj_t)&esp_flash_erase_obj },
|
||||
#if MODESP_ESPCONN
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&esp_socket_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&esp_getaddrinfo_obj },
|
||||
#endif
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write), (mp_obj_t)&esp_neopixel_write_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_freemem), (mp_obj_t)&esp_freemem_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_meminfo), (mp_obj_t)&esp_meminfo_obj },
|
||||
|
||||
#if MODESP_INCLUDE_CONSTANTS
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11B),
|
||||
MP_OBJ_NEW_SMALL_INT(PHY_MODE_11B) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11G),
|
||||
MP_OBJ_NEW_SMALL_INT(PHY_MODE_11G) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11N),
|
||||
MP_OBJ_NEW_SMALL_INT(PHY_MODE_11N) },
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_SLEEP_NONE),
|
||||
MP_OBJ_NEW_SMALL_INT(NONE_SLEEP_T) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_SLEEP_LIGHT),
|
||||
|
||||
174
esp8266/modmachine.c
Normal file
174
esp8266/modmachine.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2015 Damien P. George
|
||||
* Copyright (c) 2016 Paul Sokolovsky
|
||||
*
|
||||
* 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 "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "extmod/machine_mem.h"
|
||||
#include "utils.h"
|
||||
#include "modpyb.h"
|
||||
|
||||
#include "os_type.h"
|
||||
#include "osapi.h"
|
||||
#include "etshal.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
#if MICROPY_PY_MACHINE
|
||||
|
||||
STATIC mp_obj_t machine_freq(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
// get
|
||||
return mp_obj_new_int(system_get_cpu_freq() * 1000000);
|
||||
} else {
|
||||
// set
|
||||
mp_int_t freq = mp_obj_get_int(args[0]) / 1000000;
|
||||
if (freq != 80 && freq != 160) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
|
||||
"frequency can only be either 80Mhz or 160MHz"));
|
||||
}
|
||||
system_update_cpu_freq(freq);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq);
|
||||
|
||||
STATIC mp_obj_t machine_reset(void) {
|
||||
system_restart();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
|
||||
|
||||
STATIC mp_obj_t machine_unique_id(void) {
|
||||
uint32_t id = system_get_chip_id();
|
||||
return mp_obj_new_bytes((byte*)&id, sizeof(id));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
|
||||
|
||||
typedef struct _esp_timer_obj_t {
|
||||
mp_obj_base_t base;
|
||||
os_timer_t timer;
|
||||
mp_obj_t callback;
|
||||
} esp_timer_obj_t;
|
||||
|
||||
const mp_obj_type_t esp_timer_type;
|
||||
|
||||
STATIC void esp_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
esp_timer_obj_t *self = self_in;
|
||||
mp_printf(print, "Timer(%p)", &self->timer);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t esp_timer_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 1, 1, false);
|
||||
esp_timer_obj_t *tim = m_new_obj(esp_timer_obj_t);
|
||||
tim->base.type = &esp_timer_type;
|
||||
return tim;
|
||||
}
|
||||
|
||||
STATIC void esp_timer_cb(void *arg) {
|
||||
esp_timer_obj_t *self = arg;
|
||||
call_function_1_protected(self->callback, self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t esp_timer_init_helper(esp_timer_obj_t *self, 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_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
|
||||
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
|
||||
{ MP_QSTR_callback, 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, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
self->callback = args[2].u_obj;
|
||||
// Be sure to disarm timer before making any changes
|
||||
os_timer_disarm(&self->timer);
|
||||
os_timer_setfn(&self->timer, esp_timer_cb, self);
|
||||
os_timer_arm(&self->timer, args[0].u_int, args[1].u_int);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t esp_timer_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
return esp_timer_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_timer_init_obj, 1, esp_timer_init);
|
||||
|
||||
STATIC mp_obj_t esp_timer_deinit(mp_obj_t self_in) {
|
||||
esp_timer_obj_t *self = self_in;
|
||||
os_timer_disarm(&self->timer);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_timer_deinit_obj, esp_timer_deinit);
|
||||
|
||||
STATIC const mp_map_elem_t esp_timer_locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&esp_timer_deinit_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&esp_timer_init_obj },
|
||||
// { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&esp_timer_callback_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ONE_SHOT), MP_OBJ_NEW_SMALL_INT(false) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_PERIODIC), MP_OBJ_NEW_SMALL_INT(true) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(esp_timer_locals_dict, esp_timer_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t esp_timer_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_Timer,
|
||||
.print = esp_timer_print,
|
||||
.make_new = esp_timer_make_new,
|
||||
.locals_dict = (mp_obj_t)&esp_timer_locals_dict,
|
||||
};
|
||||
|
||||
STATIC const mp_rom_map_elem_t machine_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&esp_timer_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pyb_pin_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pyb_pwm_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pyb_adc_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&pyb_i2c_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&pyb_spi_type) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_machine = {
|
||||
.base = { &mp_type_module },
|
||||
.name = MP_QSTR_umachine,
|
||||
.globals = (mp_obj_dict_t*)&machine_module_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_MACHINE
|
||||
@@ -32,29 +32,77 @@
|
||||
#include "py/nlr.h"
|
||||
#include "py/objlist.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "netutils.h"
|
||||
#include "queue.h"
|
||||
#include "user_interface.h"
|
||||
#include "espconn.h"
|
||||
#include "spi_flash.h"
|
||||
#include "utils.h"
|
||||
#include "ets_alt_task.h"
|
||||
|
||||
#define MODNETWORK_INCLUDE_CONSTANTS (1)
|
||||
|
||||
typedef struct _wlan_if_obj_t {
|
||||
mp_obj_base_t base;
|
||||
int if_id;
|
||||
} wlan_if_obj_t;
|
||||
|
||||
void error_check(bool status, const char *msg);
|
||||
extern const mp_obj_module_t network_module;
|
||||
const mp_obj_type_t wlan_if_type;
|
||||
|
||||
STATIC mp_obj_t get_module() {
|
||||
return (mp_obj_t)&network_module;
|
||||
STATIC const wlan_if_obj_t wlan_objs[] = {
|
||||
{{&wlan_if_type}, STATION_IF},
|
||||
{{&wlan_if_type}, SOFTAP_IF},
|
||||
};
|
||||
|
||||
STATIC void require_if(mp_obj_t wlan_if, int if_no) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(wlan_if);
|
||||
if (self->if_id != if_no) {
|
||||
error_check(false, if_no == STATION_IF ? "STA required" : "AP required");
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(get_module_obj, get_module);
|
||||
|
||||
STATIC mp_obj_t get_wlan(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
int idx = 0;
|
||||
if (n_args > 0) {
|
||||
idx = mp_obj_get_int(args[0]);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(&wlan_objs[idx]);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_wlan_obj, 0, 1, get_wlan);
|
||||
|
||||
STATIC mp_obj_t esp_active(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
uint32_t mode = wifi_get_opmode();
|
||||
if (n_args > 1) {
|
||||
int mask = self->if_id == STATION_IF ? STATION_MODE : SOFTAP_MODE;
|
||||
if (mp_obj_get_int(args[1]) != 0) {
|
||||
mode |= mask;
|
||||
} else {
|
||||
mode &= ~mask;
|
||||
}
|
||||
error_check(wifi_set_opmode(mode), "Cannot update i/f status");
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// Get active status
|
||||
if (self->if_id == STATION_IF) {
|
||||
return mp_obj_new_bool(mode & STATION_MODE);
|
||||
} else {
|
||||
return mp_obj_new_bool(mode & SOFTAP_MODE);
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_active_obj, 1, 2, esp_active);
|
||||
|
||||
STATIC mp_obj_t esp_connect(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
require_if(args[0], STATION_IF);
|
||||
struct station_config config = {{0}};
|
||||
mp_uint_t len;
|
||||
const char *p;
|
||||
|
||||
p = mp_obj_str_get_data(args[0], &len);
|
||||
memcpy(config.ssid, p, len);
|
||||
p = mp_obj_str_get_data(args[1], &len);
|
||||
memcpy(config.ssid, p, len);
|
||||
p = mp_obj_str_get_data(args[2], &len);
|
||||
memcpy(config.password, p, len);
|
||||
|
||||
error_check(wifi_station_set_config(&config), "Cannot set STA config");
|
||||
@@ -62,24 +110,33 @@ STATIC mp_obj_t esp_connect(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_connect_obj, 2, 6, esp_connect);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_connect_obj, 3, 7, esp_connect);
|
||||
|
||||
STATIC mp_obj_t esp_disconnect() {
|
||||
STATIC mp_obj_t esp_disconnect(mp_obj_t self_in) {
|
||||
require_if(self_in, STATION_IF);
|
||||
error_check(wifi_station_disconnect(), "Cannot disconnect from AP");
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_disconnect_obj, esp_disconnect);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_disconnect_obj, esp_disconnect);
|
||||
|
||||
#define MODNETWORK_INCLUDE_CONSTANTS (1)
|
||||
|
||||
STATIC mp_obj_t esp_status() {
|
||||
return MP_OBJ_NEW_SMALL_INT(wifi_station_get_connect_status());
|
||||
STATIC mp_obj_t esp_status(mp_obj_t self_in) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->if_id == STATION_IF) {
|
||||
return MP_OBJ_NEW_SMALL_INT(wifi_station_get_connect_status());
|
||||
}
|
||||
return MP_OBJ_NEW_SMALL_INT(-1);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_status_obj, esp_status);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_status_obj, esp_status);
|
||||
|
||||
STATIC mp_obj_t *esp_scan_list = NULL;
|
||||
|
||||
STATIC void esp_scan_cb(scaninfo *si, STATUS status) {
|
||||
struct bss_info *bs;
|
||||
if (si->pbss) {
|
||||
if (esp_scan_list == NULL) {
|
||||
// called unexpectedly
|
||||
return;
|
||||
}
|
||||
if (si->pbss && status == 0) {
|
||||
struct bss_info *bs;
|
||||
STAILQ_FOREACH(bs, si->pbss, next) {
|
||||
mp_obj_tuple_t *t = mp_obj_new_tuple(6, NULL);
|
||||
t->items[0] = mp_obj_new_bytes(bs->ssid, strlen((char*)bs->ssid));
|
||||
@@ -88,69 +145,205 @@ STATIC void esp_scan_cb(scaninfo *si, STATUS status) {
|
||||
t->items[3] = MP_OBJ_NEW_SMALL_INT(bs->rssi);
|
||||
t->items[4] = MP_OBJ_NEW_SMALL_INT(bs->authmode);
|
||||
t->items[5] = MP_OBJ_NEW_SMALL_INT(bs->is_hidden);
|
||||
call_function_1_protected(MP_STATE_PORT(scan_cb_obj), t);
|
||||
mp_obj_list_append(*esp_scan_list, MP_OBJ_FROM_PTR(t));
|
||||
}
|
||||
} else {
|
||||
// indicate error
|
||||
*esp_scan_list = MP_OBJ_NULL;
|
||||
}
|
||||
esp_scan_list = NULL;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t esp_scan(mp_obj_t cb_in) {
|
||||
MP_STATE_PORT(scan_cb_obj) = cb_in;
|
||||
STATIC mp_obj_t esp_scan(mp_obj_t self_in) {
|
||||
if (wifi_get_opmode() == SOFTAP_MODE) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
|
||||
"Scan not supported in AP mode"));
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError,
|
||||
"scan unsupported in AP mode"));
|
||||
}
|
||||
mp_obj_t list = mp_obj_new_list(0, NULL);
|
||||
esp_scan_list = &list;
|
||||
wifi_station_scan(NULL, (scan_done_cb_t)esp_scan_cb);
|
||||
return mp_const_none;
|
||||
ETS_POLL_WHILE(esp_scan_list != NULL);
|
||||
if (list == MP_OBJ_NULL) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "scan failed"));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_scan_obj, esp_scan);
|
||||
|
||||
/// \method isconnected()
|
||||
/// Return True if connected to an AP and an IP address has been assigned,
|
||||
/// false otherwise.
|
||||
STATIC mp_obj_t esp_isconnected() {
|
||||
if (wifi_station_get_connect_status() == STATION_GOT_IP) {
|
||||
return mp_const_true;
|
||||
STATIC mp_obj_t esp_isconnected(mp_obj_t self_in) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->if_id == STATION_IF) {
|
||||
if (wifi_station_get_connect_status() == STATION_GOT_IP) {
|
||||
return mp_const_true;
|
||||
}
|
||||
} else {
|
||||
if (wifi_softap_get_station_num() > 0) {
|
||||
return mp_const_true;
|
||||
}
|
||||
}
|
||||
return mp_const_false;
|
||||
}
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_isconnected_obj, esp_isconnected);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_isconnected_obj, esp_isconnected);
|
||||
|
||||
STATIC mp_obj_t esp_mac(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
uint8_t mac[6];
|
||||
if (n_args == 0) {
|
||||
wifi_get_macaddr(STATION_IF, mac);
|
||||
if (n_args == 1) {
|
||||
wifi_get_macaddr(self->if_id, mac);
|
||||
return mp_obj_new_bytes(mac, sizeof(mac));
|
||||
} else {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
|
||||
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
|
||||
|
||||
if (bufinfo.len != 6) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
|
||||
"invalid buffer length"));
|
||||
}
|
||||
|
||||
wifi_set_macaddr(STATION_IF, bufinfo.buf);
|
||||
wifi_set_macaddr(self->if_id, bufinfo.buf);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_mac_obj, 0, 1, esp_mac);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_mac_obj, 1, 2, esp_mac);
|
||||
|
||||
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) },
|
||||
// MicroPython "network" module interface requires it to contains classes
|
||||
// to instantiate. But as we have just a static network interace,
|
||||
// use module as a "class", and just make all methods module-global
|
||||
// functions.
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_WLAN), (mp_obj_t)&get_module_obj },
|
||||
STATIC mp_obj_t esp_ifconfig(mp_obj_t self_in) {
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
struct ip_info info;
|
||||
wifi_get_ip_info(self->if_id, &info);
|
||||
mp_obj_t ifconfig[4] = {
|
||||
netutils_format_ipv4_addr((uint8_t*)&info.ip, NETUTILS_BIG),
|
||||
netutils_format_ipv4_addr((uint8_t*)&info.netmask, NETUTILS_BIG),
|
||||
netutils_format_ipv4_addr((uint8_t*)&info.gw, NETUTILS_BIG),
|
||||
MP_OBJ_NEW_QSTR(MP_QSTR_), // no DNS server
|
||||
};
|
||||
return mp_obj_new_tuple(4, ifconfig);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_ifconfig_obj, esp_ifconfig);
|
||||
|
||||
STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
|
||||
if (n_args != 1 && kwargs->used != 0) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
|
||||
"either pos or kw args are allowed"));
|
||||
}
|
||||
|
||||
wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
union {
|
||||
struct station_config sta;
|
||||
struct softap_config ap;
|
||||
} cfg;
|
||||
|
||||
if (self->if_id == STATION_IF) {
|
||||
error_check(wifi_station_get_config(&cfg.sta), "can't get STA config");
|
||||
} else {
|
||||
error_check(wifi_softap_get_config(&cfg.ap), "can't get AP config");
|
||||
}
|
||||
|
||||
if (kwargs->used != 0) {
|
||||
|
||||
for (mp_uint_t i = 0; i < kwargs->alloc; i++) {
|
||||
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
|
||||
#define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x)
|
||||
switch ((uintptr_t)kwargs->table[i].key) {
|
||||
case QS(MP_QSTR_essid): {
|
||||
mp_uint_t len;
|
||||
const char *s = mp_obj_str_get_data(kwargs->table[i].value, &len);
|
||||
len = MIN(len, sizeof(cfg.ap.ssid));
|
||||
memcpy(cfg.ap.ssid, s, len);
|
||||
cfg.ap.ssid_len = len;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
#undef QS
|
||||
}
|
||||
}
|
||||
|
||||
if (self->if_id == STATION_IF) {
|
||||
error_check(wifi_station_set_config(&cfg.sta), "can't set STA config");
|
||||
} else {
|
||||
error_check(wifi_softap_set_config(&cfg.ap), "can't set AP config");
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// Get config
|
||||
|
||||
if (n_args != 2) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
|
||||
"can query only one param"));
|
||||
}
|
||||
|
||||
#define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x)
|
||||
switch ((uintptr_t)args[1]) {
|
||||
case QS(MP_QSTR_essid):
|
||||
return mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len, false);
|
||||
}
|
||||
#undef QS
|
||||
|
||||
unknown:
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
|
||||
"unknown config param"));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_config_obj, 1, esp_config);
|
||||
|
||||
STATIC const mp_map_elem_t wlan_if_locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_active), (mp_obj_t)&esp_active_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&esp_connect_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&esp_disconnect_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_status), (mp_obj_t)&esp_status_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_scan), (mp_obj_t)&esp_scan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&esp_isconnected_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_mac), (mp_obj_t)&esp_mac_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_config), (mp_obj_t)&esp_config_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&esp_ifconfig_obj },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t wlan_if_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_WLAN,
|
||||
.locals_dict = (mp_obj_t)&wlan_if_locals_dict,
|
||||
};
|
||||
|
||||
STATIC mp_obj_t esp_wifi_mode(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
return mp_obj_new_int(wifi_get_opmode());
|
||||
} else {
|
||||
wifi_set_opmode(mp_obj_get_int(args[0]));
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_wifi_mode_obj, 0, 1, esp_wifi_mode);
|
||||
|
||||
STATIC mp_obj_t esp_phy_mode(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
return mp_obj_new_int(wifi_get_phy_mode());
|
||||
} else {
|
||||
wifi_set_phy_mode(mp_obj_get_int(args[0]));
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_phy_mode_obj, 0, 1, esp_phy_mode);
|
||||
|
||||
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)&get_wlan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_wifi_mode), (mp_obj_t)&esp_wifi_mode_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_phy_mode), (mp_obj_t)&esp_phy_mode_obj },
|
||||
|
||||
#if MODNETWORK_INCLUDE_CONSTANTS
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_STA_IF),
|
||||
MP_OBJ_NEW_SMALL_INT(STATION_IF)},
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_AP_IF),
|
||||
MP_OBJ_NEW_SMALL_INT(SOFTAP_IF)},
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_STAT_IDLE),
|
||||
MP_OBJ_NEW_SMALL_INT(STATION_IDLE)},
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_STAT_CONNECTING),
|
||||
@@ -163,6 +356,13 @@ STATIC const mp_map_elem_t mp_module_network_globals_table[] = {
|
||||
MP_OBJ_NEW_SMALL_INT(STATION_CONNECT_FAIL)},
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_STAT_GOT_IP),
|
||||
MP_OBJ_NEW_SMALL_INT(STATION_GOT_IP)},
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11B),
|
||||
MP_OBJ_NEW_SMALL_INT(PHY_MODE_11B) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11G),
|
||||
MP_OBJ_NEW_SMALL_INT(PHY_MODE_11G) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11N),
|
||||
MP_OBJ_NEW_SMALL_INT(PHY_MODE_11N) },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
185
esp8266/modonewire.c
Normal file
185
esp8266/modonewire.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Damien P. George
|
||||
*
|
||||
* 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 "etshal.h"
|
||||
#include "user_interface.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/mphal.h"
|
||||
#include "modpyb.h"
|
||||
|
||||
STATIC uint32_t disable_irq(void) {
|
||||
ets_intr_lock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC void enable_irq(uint32_t i) {
|
||||
ets_intr_unlock();
|
||||
}
|
||||
|
||||
STATIC void mp_hal_delay_us_no_irq(uint32_t us) {
|
||||
uint32_t start = system_get_time();
|
||||
while (system_get_time() - start < us) {
|
||||
}
|
||||
}
|
||||
|
||||
#define DELAY_US mp_hal_delay_us_no_irq
|
||||
|
||||
#define TIMING_RESET1 (0)
|
||||
#define TIMING_RESET2 (1)
|
||||
#define TIMING_RESET3 (2)
|
||||
#define TIMING_READ1 (3)
|
||||
#define TIMING_READ2 (4)
|
||||
#define TIMING_READ3 (5)
|
||||
#define TIMING_WRITE1 (6)
|
||||
#define TIMING_WRITE2 (7)
|
||||
#define TIMING_WRITE3 (8)
|
||||
|
||||
static int timings[] = {480, 40, 420, 5, 5, 40, 10, 50, 10};
|
||||
|
||||
STATIC mp_obj_t onewire_timings(mp_obj_t timings_in) {
|
||||
mp_obj_t *items;
|
||||
mp_obj_get_array_fixed_n(timings_in, 9, &items);
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
timings[i] = mp_obj_get_int(items[i]);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_timings_obj, onewire_timings);
|
||||
|
||||
STATIC mp_obj_t onewire_reset(mp_obj_t pin_in) {
|
||||
uint pin = mp_obj_get_pin(pin_in);
|
||||
pin_set(pin, 0);
|
||||
DELAY_US(timings[TIMING_RESET1]);
|
||||
uint32_t i = disable_irq();
|
||||
pin_set(pin, 1);
|
||||
DELAY_US(timings[TIMING_RESET2]);
|
||||
int status = !pin_get(pin);
|
||||
enable_irq(i);
|
||||
DELAY_US(timings[TIMING_RESET3]);
|
||||
return mp_obj_new_bool(status);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_reset_obj, onewire_reset);
|
||||
|
||||
STATIC int _onewire_readbit(uint pin) {
|
||||
pin_set(pin, 1);
|
||||
uint32_t i = disable_irq();
|
||||
pin_set(pin, 0);
|
||||
DELAY_US(timings[TIMING_READ1]);
|
||||
pin_set(pin, 1);
|
||||
DELAY_US(timings[TIMING_READ2]);
|
||||
int value = pin_get(pin);
|
||||
enable_irq(i);
|
||||
DELAY_US(timings[TIMING_READ3]);
|
||||
return value;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t onewire_readbit(mp_obj_t pin_in) {
|
||||
return MP_OBJ_NEW_SMALL_INT(_onewire_readbit(mp_obj_get_pin(pin_in)));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbit_obj, onewire_readbit);
|
||||
|
||||
STATIC mp_obj_t onewire_readbyte(mp_obj_t pin_in) {
|
||||
uint pin = mp_obj_get_pin(pin_in);
|
||||
uint8_t value = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
value |= _onewire_readbit(pin) << i;
|
||||
}
|
||||
return MP_OBJ_NEW_SMALL_INT(value);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_readbyte_obj, onewire_readbyte);
|
||||
|
||||
STATIC void _onewire_writebit(uint pin, int value) {
|
||||
uint32_t i = disable_irq();
|
||||
pin_set(pin, 0);
|
||||
DELAY_US(timings[TIMING_WRITE1]);
|
||||
pin_set(pin, value);
|
||||
DELAY_US(timings[TIMING_WRITE2]);
|
||||
pin_set(pin, 1);
|
||||
DELAY_US(timings[TIMING_WRITE3]);
|
||||
enable_irq(i);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t onewire_writebit(mp_obj_t pin_in, mp_obj_t value_in) {
|
||||
_onewire_writebit(mp_obj_get_pin(pin_in), mp_obj_get_int(value_in));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebit_obj, onewire_writebit);
|
||||
|
||||
STATIC mp_obj_t onewire_writebyte(mp_obj_t pin_in, mp_obj_t value_in) {
|
||||
uint pin = mp_obj_get_pin(pin_in);
|
||||
int value = mp_obj_get_int(value_in);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_onewire_writebit(pin, value & 1);
|
||||
value >>= 1;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(onewire_writebyte_obj, onewire_writebyte);
|
||||
|
||||
STATIC mp_obj_t onewire_crc8(mp_obj_t data) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
|
||||
uint8_t crc = 0;
|
||||
for (size_t i = 0; i < bufinfo.len; ++i) {
|
||||
uint8_t byte = ((uint8_t*)bufinfo.buf)[i];
|
||||
for (int b = 0; b < 8; ++b) {
|
||||
uint8_t fb_bit = (crc ^ byte) & 0x01;
|
||||
if (fb_bit == 0x01) {
|
||||
crc = crc ^ 0x18;
|
||||
}
|
||||
crc = (crc >> 1) & 0x7f;
|
||||
if (fb_bit == 0x01) {
|
||||
crc = crc | 0x80;
|
||||
}
|
||||
byte = byte >> 1;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_NEW_SMALL_INT(crc);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(onewire_crc8_obj, onewire_crc8);
|
||||
|
||||
STATIC const mp_map_elem_t onewire_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_onewire) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_timings), MP_ROM_PTR((mp_obj_t)&onewire_timings_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR((mp_obj_t)&onewire_reset_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readbit), MP_ROM_PTR((mp_obj_t)&onewire_readbit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readbyte), MP_ROM_PTR((mp_obj_t)&onewire_readbyte_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_writebit), MP_ROM_PTR((mp_obj_t)&onewire_writebit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_writebyte), MP_ROM_PTR((mp_obj_t)&onewire_writebyte_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_crc8), MP_ROM_PTR((mp_obj_t)&onewire_crc8_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(onewire_module_globals, onewire_module_globals_table);
|
||||
|
||||
const mp_obj_module_t onewire_module = {
|
||||
.base = { &mp_type_module },
|
||||
.name = MP_QSTR_onewire,
|
||||
.globals = (mp_obj_dict_t*)&onewire_module_globals,
|
||||
};
|
||||
@@ -77,23 +77,6 @@ STATIC mp_obj_t pyb_info(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_info_obj, 0, 1, pyb_info);
|
||||
|
||||
STATIC mp_obj_t pyb_freq(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
// get
|
||||
return mp_obj_new_int(system_get_cpu_freq() * 1000000);
|
||||
} else {
|
||||
// set
|
||||
mp_int_t freq = mp_obj_get_int(args[0]) / 1000000;
|
||||
if (freq != 80 && freq != 160) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
|
||||
"frequency can only be either 80Mhz or 160MHz"));
|
||||
}
|
||||
system_update_cpu_freq(freq);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_freq_obj, 0, 1, pyb_freq);
|
||||
|
||||
STATIC mp_obj_t pyb_sync(void) {
|
||||
//storage_flush();
|
||||
return mp_const_none;
|
||||
@@ -142,23 +125,10 @@ STATIC mp_obj_t pyb_udelay(mp_obj_t usec_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_udelay_obj, pyb_udelay);
|
||||
|
||||
STATIC mp_obj_t pyb_hard_reset(void) {
|
||||
system_restart();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_hard_reset_obj, pyb_hard_reset);
|
||||
|
||||
STATIC mp_obj_t pyb_unique_id(void) {
|
||||
uint32_t id = system_get_chip_id();
|
||||
return mp_obj_new_bytes((byte *)&id, sizeof(id));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_unique_id_obj, pyb_unique_id);
|
||||
|
||||
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_info), (mp_obj_t)&pyb_info_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_freq_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 },
|
||||
@@ -167,7 +137,6 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = {
|
||||
{ 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_hard_reset), (mp_obj_t)&pyb_hard_reset_obj },
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_Pin), (mp_obj_t)&pyb_pin_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ADC), (mp_obj_t)&pyb_adc_type },
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
extern const mp_obj_type_t pyb_pin_type;
|
||||
extern const mp_obj_type_t pyb_pwm_type;
|
||||
extern const mp_obj_type_t pyb_adc_type;
|
||||
extern const mp_obj_type_t pyb_rtc_type;
|
||||
extern const mp_obj_type_t pyb_uart_type;
|
||||
extern const mp_obj_type_t pyb_i2c_type;
|
||||
extern const mp_obj_type_t pyb_spi_type;
|
||||
|
||||
typedef struct _pyb_pin_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint16_t pin_id;
|
||||
uint16_t phys_port;
|
||||
uint32_t periph;
|
||||
uint16_t func;
|
||||
} pyb_pin_obj_t;
|
||||
|
||||
uint mp_obj_get_pin(mp_obj_t pin_in);
|
||||
pyb_pin_obj_t *mp_obj_get_pin_obj(mp_obj_t pin_in);
|
||||
int pin_get(uint pin);
|
||||
void pin_set(uint pin, int value);
|
||||
|
||||
323
esp8266/modpybi2c.c
Normal file
323
esp8266/modpybi2c.c
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* 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 "ets_sys.h"
|
||||
#include "etshal.h"
|
||||
#include "osapi.h"
|
||||
#include "gpio.h"
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "modpyb.h"
|
||||
|
||||
typedef struct _pyb_i2c_obj_t {
|
||||
mp_obj_base_t base;
|
||||
pyb_pin_obj_t *scl;
|
||||
pyb_pin_obj_t *sda;
|
||||
} pyb_i2c_obj_t;
|
||||
|
||||
// these set the frequency of SCL
|
||||
#define mphal_i2c_wait_a() os_delay_us(2)
|
||||
#define mphal_i2c_wait_b() os_delay_us(1)
|
||||
|
||||
STATIC void mphal_i2c_set_sda(pyb_i2c_obj_t *self, uint8_t sda) {
|
||||
uint32_t port = self->sda->phys_port;
|
||||
sda &= 0x01;
|
||||
gpio_output_set(sda << port, (1 - sda) << port, 1 << port, 0);
|
||||
}
|
||||
|
||||
STATIC void mphal_i2c_set_scl(pyb_i2c_obj_t *self, uint8_t scl) {
|
||||
uint32_t port = self->scl->phys_port;
|
||||
scl &= 0x01;
|
||||
gpio_output_set(scl << port, (1 - scl) << port, 1 << port, 0);
|
||||
}
|
||||
|
||||
STATIC int mphal_i2c_get_sda(pyb_i2c_obj_t *self) {
|
||||
return GPIO_INPUT_GET(GPIO_ID_PIN(self->sda->phys_port));
|
||||
}
|
||||
|
||||
STATIC void mphal_i2c_start(pyb_i2c_obj_t *self) {
|
||||
mphal_i2c_set_sda(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_sda(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
}
|
||||
|
||||
STATIC void mphal_i2c_stop(pyb_i2c_obj_t *self) {
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_sda(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_sda(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
}
|
||||
|
||||
STATIC void mphal_i2c_init(pyb_i2c_obj_t *self, uint32_t freq) {
|
||||
pyb_pin_obj_t *scl = self->scl;
|
||||
pyb_pin_obj_t *sda = self->sda;
|
||||
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
//ETS_INTR_LOCK();
|
||||
|
||||
PIN_FUNC_SELECT(sda->periph, sda->func);
|
||||
PIN_FUNC_SELECT(scl->periph, scl->func);
|
||||
|
||||
GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(sda->phys_port)),
|
||||
GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(sda->phys_port)))
|
||||
| GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); // open drain
|
||||
GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS,
|
||||
GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << sda->phys_port));
|
||||
GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(scl->phys_port)),
|
||||
GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(scl->phys_port)))
|
||||
| GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); // open drain
|
||||
GPIO_REG_WRITE(GPIO_ENABLE_ADDRESS,
|
||||
GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | (1 << scl->phys_port));
|
||||
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_set_sda(self, 1);
|
||||
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
//ETS_INTR_UNLOCK();
|
||||
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
// when SCL = 0, toggle SDA to clear up
|
||||
mphal_i2c_set_sda(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_sda(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
// set data_cnt to max value
|
||||
for (uint8_t i = 0; i < 28; i++) {
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
}
|
||||
|
||||
// reset all
|
||||
mphal_i2c_stop(self);
|
||||
}
|
||||
|
||||
STATIC int mphal_i2c_write_byte(pyb_i2c_obj_t *self, uint8_t val) {
|
||||
uint8_t dat;
|
||||
sint8 i;
|
||||
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
dat = val >> i;
|
||||
mphal_i2c_set_sda(self, dat);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
if (i == 0) {
|
||||
mphal_i2c_wait_b();
|
||||
}
|
||||
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
}
|
||||
|
||||
mphal_i2c_set_sda(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
int ret = mphal_i2c_get_sda(self);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
STATIC void mphal_i2c_write(pyb_i2c_obj_t *self, uint8_t addr, uint8_t *data, size_t len, bool stop) {
|
||||
mphal_i2c_start(self);
|
||||
if (!mphal_i2c_write_byte(self, addr << 1)) {
|
||||
goto er;
|
||||
}
|
||||
while (len--) {
|
||||
if (!mphal_i2c_write_byte(self, *data++)) {
|
||||
goto er;
|
||||
}
|
||||
}
|
||||
if (stop) {
|
||||
mphal_i2c_stop(self);
|
||||
}
|
||||
return;
|
||||
|
||||
er:
|
||||
mphal_i2c_stop(self);
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
|
||||
}
|
||||
|
||||
STATIC int mphal_i2c_read_byte(pyb_i2c_obj_t *self, uint8_t *val) {
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
uint8_t dat = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
dat = (dat << 1) | mphal_i2c_get_sda(self);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
}
|
||||
*val = dat;
|
||||
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_set_scl(self, 1);
|
||||
mphal_i2c_wait_a();
|
||||
mphal_i2c_wait_b();
|
||||
mphal_i2c_set_scl(self, 0);
|
||||
mphal_i2c_wait_a();
|
||||
|
||||
return 1; // success
|
||||
}
|
||||
|
||||
STATIC void mphal_i2c_read(pyb_i2c_obj_t *self, uint8_t addr, uint8_t *data, size_t len, bool stop) {
|
||||
mphal_i2c_start(self);
|
||||
if (!mphal_i2c_write_byte(self, (addr << 1) | 1)) {
|
||||
goto er;
|
||||
}
|
||||
while (len--) {
|
||||
if (!mphal_i2c_read_byte(self, data++)) {
|
||||
goto er;
|
||||
}
|
||||
}
|
||||
if (stop) {
|
||||
mphal_i2c_stop(self);
|
||||
}
|
||||
return;
|
||||
|
||||
er:
|
||||
mphal_i2c_stop(self);
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for I2C
|
||||
|
||||
STATIC void pyb_i2c_obj_init_helper(pyb_i2c_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_scl, ARG_sda, ARG_freq };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
self->scl = mp_obj_get_pin_obj(args[ARG_scl].u_obj);
|
||||
self->sda = mp_obj_get_pin_obj(args[ARG_sda].u_obj);
|
||||
mphal_i2c_init(self, args[ARG_freq].u_int);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true);
|
||||
pyb_i2c_obj_t *self = m_new_obj(pyb_i2c_obj_t);
|
||||
self->base.type = &pyb_i2c_type;
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
pyb_i2c_obj_init_helper(self, n_args, args, &kw_args);
|
||||
return (mp_obj_t)self;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_i2c_obj_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
pyb_i2c_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_init_obj, 1, pyb_i2c_obj_init);
|
||||
|
||||
STATIC mp_obj_t pyb_i2c_readfrom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_addr, ARG_n, ARG_stop };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_n, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_stop, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
|
||||
};
|
||||
pyb_i2c_obj_t *self = pos_args[0];
|
||||
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);
|
||||
|
||||
// do the I2C transfer
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, args[ARG_n].u_int);
|
||||
mphal_i2c_read(self, args[ARG_addr].u_int, (uint8_t*)vstr.buf, vstr.len, args[ARG_stop].u_bool);
|
||||
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_readfrom_obj, 1, pyb_i2c_readfrom);
|
||||
|
||||
STATIC mp_obj_t pyb_i2c_writeto(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_addr, ARG_buf, ARG_stop };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_stop, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
|
||||
};
|
||||
pyb_i2c_obj_t *self = pos_args[0];
|
||||
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 the buffer to write from
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ);
|
||||
|
||||
// do the I2C transfer
|
||||
mphal_i2c_write(self, args[ARG_addr].u_int, bufinfo.buf, bufinfo.len, args[ARG_stop].u_bool);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_writeto_obj, 1, pyb_i2c_writeto);
|
||||
|
||||
STATIC const mp_rom_map_elem_t pyb_i2c_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_i2c_init_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readfrom), MP_ROM_PTR(&pyb_i2c_readfrom_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&pyb_i2c_writeto_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(pyb_i2c_locals_dict, pyb_i2c_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t pyb_i2c_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_I2C,
|
||||
.make_new = pyb_i2c_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&pyb_i2c_locals_dict,
|
||||
};
|
||||
@@ -38,19 +38,12 @@
|
||||
|
||||
#define GPIO_MODE_INPUT (0)
|
||||
#define GPIO_MODE_OUTPUT (1)
|
||||
#define GPIO_MODE_OPEN_DRAIN (2) // synthesised
|
||||
#define GPIO_PULL_NONE (0)
|
||||
#define GPIO_PULL_UP (1)
|
||||
// Removed in SDK 1.1.0
|
||||
//#define GPIO_PULL_DOWN (2)
|
||||
|
||||
typedef struct _pyb_pin_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint16_t pin_id;
|
||||
uint16_t phys_port;
|
||||
uint32_t periph;
|
||||
uint16_t func;
|
||||
} pyb_pin_obj_t;
|
||||
|
||||
STATIC const pyb_pin_obj_t pyb_pin_obj[] = {
|
||||
{{&pyb_pin_type}, 0, 0, PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0},
|
||||
{{&pyb_pin_type}, 1, 1, PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1},
|
||||
@@ -64,8 +57,75 @@ STATIC const pyb_pin_obj_t pyb_pin_obj[] = {
|
||||
{{&pyb_pin_type}, 13, 13, PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13},
|
||||
{{&pyb_pin_type}, 14, 14, PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14},
|
||||
{{&pyb_pin_type}, 15, 15, PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15},
|
||||
// GPIO16 is special, belongs to different register set, and
|
||||
// otherwise handled specially.
|
||||
{{&pyb_pin_type}, 16, 16, -1, -1},
|
||||
};
|
||||
|
||||
STATIC uint8_t pin_mode[16 + 1];
|
||||
|
||||
pyb_pin_obj_t *mp_obj_get_pin_obj(mp_obj_t pin_in) {
|
||||
if (mp_obj_get_type(pin_in) != &pyb_pin_type) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "expecting a pin"));
|
||||
}
|
||||
pyb_pin_obj_t *self = pin_in;
|
||||
return self;
|
||||
}
|
||||
|
||||
uint mp_obj_get_pin(mp_obj_t pin_in) {
|
||||
return mp_obj_get_pin_obj(pin_in)->phys_port;
|
||||
}
|
||||
|
||||
int pin_get(uint pin) {
|
||||
if (pin == 16) {
|
||||
return READ_PERI_REG(RTC_GPIO_IN_DATA) & 1;
|
||||
}
|
||||
return GPIO_INPUT_GET(pin);
|
||||
}
|
||||
|
||||
void pin_set(uint pin, int value) {
|
||||
if (pin == 16) {
|
||||
int out_en = (pin_mode[pin] == GPIO_MODE_OUTPUT);
|
||||
WRITE_PERI_REG(PAD_XPD_DCDC_CONF, (READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xffffffbc) | 1);
|
||||
WRITE_PERI_REG(RTC_GPIO_CONF, READ_PERI_REG(RTC_GPIO_CONF) & ~1);
|
||||
WRITE_PERI_REG(RTC_GPIO_ENABLE, (READ_PERI_REG(RTC_GPIO_ENABLE) & ~1) | out_en);
|
||||
WRITE_PERI_REG(RTC_GPIO_OUT, (READ_PERI_REG(RTC_GPIO_OUT) & ~1) | value);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t enable = 0;
|
||||
uint32_t disable = 0;
|
||||
switch (pin_mode[pin]) {
|
||||
case GPIO_MODE_INPUT:
|
||||
value = -1;
|
||||
disable = 1;
|
||||
break;
|
||||
|
||||
case GPIO_MODE_OUTPUT:
|
||||
enable = 1;
|
||||
break;
|
||||
|
||||
case GPIO_MODE_OPEN_DRAIN:
|
||||
if (value == -1) {
|
||||
return;
|
||||
} else if (value == 0) {
|
||||
enable = 1;
|
||||
} else {
|
||||
value = -1;
|
||||
disable = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
enable <<= pin;
|
||||
disable <<= pin;
|
||||
if (value == -1) {
|
||||
gpio_output_set(0, 0, enable, disable);
|
||||
} else {
|
||||
gpio_output_set(value << pin, (1 - value) << pin, enable, disable);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void pyb_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
pyb_pin_obj_t *self = self_in;
|
||||
|
||||
@@ -77,43 +137,59 @@ STATIC void pyb_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki
|
||||
STATIC mp_obj_t pyb_pin_obj_init_helper(pyb_pin_obj_t *self, 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_mode, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
{ MP_QSTR_pull, MP_ARG_INT, {.u_int = GPIO_PULL_NONE}},
|
||||
{ MP_QSTR_pull, MP_ARG_INT, {.u_int = GPIO_PULL_NONE}},
|
||||
{ MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
};
|
||||
|
||||
// parse args
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
struct {
|
||||
mp_arg_val_t mode, pull, value;
|
||||
} args;
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args,
|
||||
MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
|
||||
|
||||
// get io mode
|
||||
uint mode = args[0].u_int;
|
||||
uint mode = args.mode.u_int;
|
||||
|
||||
// get pull mode
|
||||
uint pull = args[1].u_int;
|
||||
uint pull = args.pull.u_int;
|
||||
|
||||
// get initial value
|
||||
int value;
|
||||
if (args.value.u_obj == MP_OBJ_NULL) {
|
||||
value = -1;
|
||||
} else {
|
||||
value = mp_obj_is_true(args.value.u_obj);
|
||||
}
|
||||
|
||||
// save the mode
|
||||
pin_mode[self->phys_port] = mode;
|
||||
|
||||
// configure the GPIO as requested
|
||||
PIN_FUNC_SELECT(self->periph, self->func);
|
||||
#if 0
|
||||
// Removed in SDK 1.1.0
|
||||
if ((pull & GPIO_PULL_DOWN) == 0) {
|
||||
PIN_PULLDWN_DIS(self->periph);
|
||||
}
|
||||
#endif
|
||||
if ((pull & GPIO_PULL_UP) == 0) {
|
||||
PIN_PULLUP_DIS(self->periph);
|
||||
}
|
||||
#if 0
|
||||
if ((pull & GPIO_PULL_DOWN) != 0) {
|
||||
PIN_PULLDWN_EN(self->periph);
|
||||
}
|
||||
#endif
|
||||
if ((pull & GPIO_PULL_UP) != 0) {
|
||||
PIN_PULLUP_EN(self->periph);
|
||||
if (self->phys_port == 16) {
|
||||
// TODO: Set pull up/pull down
|
||||
} else {
|
||||
PIN_FUNC_SELECT(self->periph, self->func);
|
||||
#if 0
|
||||
// Removed in SDK 1.1.0
|
||||
if ((pull & GPIO_PULL_DOWN) == 0) {
|
||||
PIN_PULLDWN_DIS(self->periph);
|
||||
}
|
||||
#endif
|
||||
if ((pull & GPIO_PULL_UP) == 0) {
|
||||
PIN_PULLUP_DIS(self->periph);
|
||||
}
|
||||
#if 0
|
||||
if ((pull & GPIO_PULL_DOWN) != 0) {
|
||||
PIN_PULLDWN_EN(self->periph);
|
||||
}
|
||||
#endif
|
||||
if ((pull & GPIO_PULL_UP) != 0) {
|
||||
PIN_PULLUP_EN(self->periph);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO input mode is not working...
|
||||
if ((mode & GPIO_MODE_OUTPUT) == 0) {
|
||||
GPIO_DIS_OUTPUT(self->phys_port);
|
||||
}
|
||||
pin_set(self->phys_port, value);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
@@ -145,6 +221,20 @@ STATIC mp_obj_t pyb_pin_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp
|
||||
return (mp_obj_t)pin;
|
||||
}
|
||||
|
||||
// fast method for getting/setting pin value
|
||||
STATIC mp_obj_t pyb_pin_call(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, 0, 1, false);
|
||||
pyb_pin_obj_t *self = self_in;
|
||||
if (n_args == 0) {
|
||||
// get pin
|
||||
return MP_OBJ_NEW_SMALL_INT(GPIO_INPUT_GET(self->phys_port));
|
||||
} else {
|
||||
// set pin
|
||||
pin_set(self->phys_port, mp_obj_is_true(args[0]));
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
|
||||
// pin.init(mode, pull)
|
||||
STATIC mp_obj_t pyb_pin_obj_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
return pyb_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
@@ -153,26 +243,14 @@ MP_DEFINE_CONST_FUN_OBJ_KW(pyb_pin_init_obj, 1, pyb_pin_obj_init);
|
||||
|
||||
// pin.value([value])
|
||||
STATIC mp_obj_t pyb_pin_value(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
pyb_pin_obj_t *self = args[0];
|
||||
if (n_args == 1) {
|
||||
// get pin
|
||||
return MP_OBJ_NEW_SMALL_INT(GPIO_INPUT_GET(self->phys_port));
|
||||
} else {
|
||||
// set pin
|
||||
if (mp_obj_is_true(args[1])) {
|
||||
GPIO_OUTPUT_SET(self->phys_port, 1);
|
||||
} else {
|
||||
GPIO_OUTPUT_SET(self->phys_port, 0);
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
return pyb_pin_call(args[0], n_args - 1, 0, args + 1);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_pin_value_obj, 1, 2, pyb_pin_value);
|
||||
|
||||
// pin.low()
|
||||
STATIC mp_obj_t pyb_pin_low(mp_obj_t self_in) {
|
||||
pyb_pin_obj_t *self = self_in;
|
||||
GPIO_OUTPUT_SET(self->phys_port, 0);
|
||||
pin_set(self->phys_port, 0);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_pin_low_obj, pyb_pin_low);
|
||||
@@ -180,7 +258,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_pin_low_obj, pyb_pin_low);
|
||||
// pin.high()
|
||||
STATIC mp_obj_t pyb_pin_high(mp_obj_t self_in) {
|
||||
pyb_pin_obj_t *self = self_in;
|
||||
GPIO_OUTPUT_SET(self->phys_port, 1);
|
||||
pin_set(self->phys_port, 1);
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_pin_high_obj, pyb_pin_high);
|
||||
@@ -194,7 +272,8 @@ STATIC const mp_map_elem_t pyb_pin_locals_dict_table[] = {
|
||||
|
||||
// class constants
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_IN), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_INPUT) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_OUT_PP), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OUTPUT) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_OUT), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OUTPUT) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_OPEN_DRAIN), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OPEN_DRAIN) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_PULL_NONE), MP_OBJ_NEW_SMALL_INT(GPIO_PULL_NONE) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_PULL_UP), MP_OBJ_NEW_SMALL_INT(GPIO_PULL_UP) },
|
||||
//{ MP_OBJ_NEW_QSTR(MP_QSTR_PULL_DOWN), MP_OBJ_NEW_SMALL_INT(GPIO_PULL_DOWN) },
|
||||
@@ -207,5 +286,6 @@ const mp_obj_type_t pyb_pin_type = {
|
||||
.name = MP_QSTR_Pin,
|
||||
.print = pyb_pin_print,
|
||||
.make_new = pyb_pin_make_new,
|
||||
.call = pyb_pin_call,
|
||||
.locals_dict = (mp_obj_t)&pyb_pin_locals_dict,
|
||||
};
|
||||
|
||||
172
esp8266/modpybpwm.c
Normal file
172
esp8266/modpybpwm.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* This file is part of the Micro Python project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* 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 "esppwm.h"
|
||||
|
||||
#include "py/nlr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "modpyb.h"
|
||||
|
||||
typedef struct _pyb_pwm_obj_t {
|
||||
mp_obj_base_t base;
|
||||
pyb_pin_obj_t *pin;
|
||||
uint8_t active;
|
||||
uint8_t channel;
|
||||
} pyb_pwm_obj_t;
|
||||
|
||||
STATIC bool pwm_inited = false;
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for PWM
|
||||
|
||||
STATIC void pyb_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "PWM(%u", self->pin->pin_id);
|
||||
if (self->active) {
|
||||
mp_printf(print, ", freq=%u, duty=%u",
|
||||
pwm_get_freq(self->channel), pwm_get_duty(self->channel));
|
||||
}
|
||||
mp_printf(print, ")");
|
||||
}
|
||||
|
||||
STATIC void pyb_pwm_init_helper(pyb_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_freq, ARG_duty };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_duty, MP_ARG_INT, {.u_int = -1} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
int channel = pwm_add(self->pin->phys_port, self->pin->periph, self->pin->func);
|
||||
if (channel == -1) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
|
||||
"PWM not supported on pin %d", self->pin->phys_port));
|
||||
}
|
||||
|
||||
self->channel = channel;
|
||||
self->active = 1;
|
||||
if (args[ARG_freq].u_int != -1) {
|
||||
pwm_set_freq(args[ARG_freq].u_int, self->channel);
|
||||
}
|
||||
if (args[ARG_duty].u_int != -1) {
|
||||
pwm_set_duty(args[ARG_duty].u_int, self->channel);
|
||||
}
|
||||
|
||||
pwm_start();
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
|
||||
pyb_pin_obj_t *pin = mp_obj_get_pin_obj(args[0]);
|
||||
|
||||
// create PWM object from the given pin
|
||||
pyb_pwm_obj_t *self = m_new_obj(pyb_pwm_obj_t);
|
||||
self->base.type = &pyb_pwm_type;
|
||||
self->pin = pin;
|
||||
self->active = 0;
|
||||
self->channel = -1;
|
||||
|
||||
// start the PWM subsystem if it's not already running
|
||||
if (!pwm_inited) {
|
||||
pwm_init();
|
||||
pwm_inited = true;
|
||||
}
|
||||
|
||||
// start the PWM running for this channel
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
pyb_pwm_init_helper(self, n_args - 1, args + 1, &kw_args);
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_pwm_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
pyb_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_pwm_init_obj, 1, pyb_pwm_init);
|
||||
|
||||
STATIC mp_obj_t pyb_pwm_deinit(mp_obj_t self_in) {
|
||||
pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
pwm_delete(self->channel);
|
||||
self->active = 0;
|
||||
pwm_start();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_pwm_deinit_obj, pyb_pwm_deinit);
|
||||
|
||||
STATIC mp_obj_t pyb_pwm_freq(size_t n_args, const mp_obj_t *args) {
|
||||
//pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
return MP_OBJ_NEW_SMALL_INT(pwm_get_freq(0));
|
||||
} else {
|
||||
// set
|
||||
pwm_set_freq(mp_obj_get_int(args[1]), 0);
|
||||
pwm_start();
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_pwm_freq_obj, 1, 2, pyb_pwm_freq);
|
||||
|
||||
STATIC mp_obj_t pyb_pwm_duty(size_t n_args, const mp_obj_t *args) {
|
||||
pyb_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
if (!self->active) {
|
||||
pwm_add(self->pin->phys_port, self->pin->periph, self->pin->func);
|
||||
self->active = 1;
|
||||
}
|
||||
if (n_args == 1) {
|
||||
// get
|
||||
return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel));
|
||||
} else {
|
||||
// set
|
||||
pwm_set_duty(mp_obj_get_int(args[1]), self->channel);
|
||||
pwm_start();
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_pwm_duty_obj, 1, 2, pyb_pwm_duty);
|
||||
|
||||
STATIC const mp_rom_map_elem_t pyb_pwm_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_pwm_init_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pyb_pwm_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&pyb_pwm_freq_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&pyb_pwm_duty_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(pyb_pwm_locals_dict, pyb_pwm_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t pyb_pwm_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_PWM,
|
||||
.print = pyb_pwm_print,
|
||||
.make_new = pyb_pwm_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&pyb_pwm_locals_dict,
|
||||
};
|
||||
@@ -49,6 +49,20 @@ typedef struct _pyb_rtc_obj_t {
|
||||
// singleton RTC object
|
||||
STATIC const pyb_rtc_obj_t pyb_rtc_obj = {{&pyb_rtc_type}};
|
||||
|
||||
void mp_hal_rtc_init(void) {
|
||||
uint32_t magic;
|
||||
|
||||
system_rtc_mem_read(MEM_USER_MAGIC_ADDR, &magic, sizeof(magic));
|
||||
if (magic != MEM_MAGIC) {
|
||||
magic = MEM_MAGIC;
|
||||
system_rtc_mem_write(MEM_USER_MAGIC_ADDR, &magic, sizeof(magic));
|
||||
uint32_t cal = system_rtc_clock_cali_proc();
|
||||
int64_t delta = 0;
|
||||
system_rtc_mem_write(MEM_CAL_ADDR, &cal, sizeof(cal));
|
||||
system_rtc_mem_write(MEM_DELTA_ADDR, &delta, sizeof(delta));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_rtc_make_new(const mp_obj_type_t *type, 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);
|
||||
@@ -58,7 +72,7 @@ STATIC mp_obj_t pyb_rtc_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp
|
||||
}
|
||||
|
||||
STATIC uint64_t pyb_rtc_raw_us(uint64_t cal) {
|
||||
return system_get_rtc_time() * ((cal >> 12) * 1000 + (cal & 0xfff) / 4) / 1000;
|
||||
return (system_get_rtc_time() * cal) >> 12;
|
||||
};
|
||||
|
||||
void pyb_rtc_set_us_since_2000(uint64_t nowus) {
|
||||
@@ -146,7 +160,7 @@ STATIC mp_obj_t pyb_rtc_memory(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
}
|
||||
|
||||
magic = MEM_MAGIC;
|
||||
system_rtc_mem_write(MEM_USER_MAGIC_ADDR, &magic, sizeof(len));
|
||||
system_rtc_mem_write(MEM_USER_MAGIC_ADDR, &magic, sizeof(magic));
|
||||
len = bufinfo.len;
|
||||
system_rtc_mem_write(MEM_USER_LEN_ADDR, &len, sizeof(len));
|
||||
|
||||
|
||||
217
esp8266/modpybspi.c
Normal file
217
esp8266/modpybspi.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* 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 "ets_sys.h"
|
||||
#include "etshal.h"
|
||||
#include "ets_alt_task.h"
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "modpyb.h"
|
||||
|
||||
typedef struct _pyb_spi_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint32_t baudrate;
|
||||
uint8_t polarity;
|
||||
uint8_t phase;
|
||||
pyb_pin_obj_t *sck;
|
||||
pyb_pin_obj_t *mosi;
|
||||
pyb_pin_obj_t *miso;
|
||||
} pyb_spi_obj_t;
|
||||
|
||||
STATIC void mp_hal_spi_transfer(pyb_spi_obj_t *self, size_t src_len, const uint8_t *src_buf, size_t dest_len, uint8_t *dest_buf) {
|
||||
// only MSB transfer is implemented
|
||||
uint32_t delay_half = 500000 / self->baudrate + 1;
|
||||
for (size_t i = 0; i < src_len || i < dest_len; ++i) {
|
||||
uint8_t data_out;
|
||||
if (src_len == 1) {
|
||||
data_out = src_buf[0];
|
||||
} else {
|
||||
data_out = src_buf[i];
|
||||
}
|
||||
uint8_t data_in = 0;
|
||||
for (int j = 0; j < 8; ++j, data_out <<= 1) {
|
||||
pin_set(self->mosi->phys_port, (data_out >> 7) & 1);
|
||||
if (self->phase == 0) {
|
||||
ets_delay_us(delay_half);
|
||||
pin_set(self->sck->phys_port, 1 - self->polarity);
|
||||
} else {
|
||||
pin_set(self->sck->phys_port, 1 - self->polarity);
|
||||
ets_delay_us(delay_half);
|
||||
}
|
||||
data_in = (data_in << 1) | pin_get(self->miso->phys_port);
|
||||
if (self->phase == 0) {
|
||||
ets_delay_us(delay_half);
|
||||
pin_set(self->sck->phys_port, self->polarity);
|
||||
} else {
|
||||
pin_set(self->sck->phys_port, self->polarity);
|
||||
ets_delay_us(delay_half);
|
||||
}
|
||||
}
|
||||
if (dest_len != 0) {
|
||||
dest_buf[i] = data_in;
|
||||
}
|
||||
// make sure pending tasks have a chance to run
|
||||
ets_loop_iter();
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for SPI
|
||||
|
||||
STATIC void pyb_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
pyb_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "SPI(baudrate=%u, polarity=%u, phase=%u, sck=%u, mosi=%u, miso=%u)",
|
||||
self->baudrate, self->polarity, self->phase, self->sck->phys_port, self->mosi->phys_port, self->miso->phys_port);
|
||||
}
|
||||
|
||||
STATIC void pyb_spi_init_helper(pyb_spi_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_sck, ARG_mosi, ARG_miso };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_polarity, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_phase, MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if (args[ARG_baudrate].u_int != -1) {
|
||||
self->baudrate = args[ARG_baudrate].u_int;
|
||||
}
|
||||
if (args[ARG_polarity].u_int != -1) {
|
||||
self->polarity = args[ARG_polarity].u_int;
|
||||
}
|
||||
if (args[ARG_phase].u_int != -1) {
|
||||
self->phase = args[ARG_phase].u_int;
|
||||
}
|
||||
if (args[ARG_sck].u_obj != MP_OBJ_NULL) {
|
||||
self->sck = mp_obj_get_pin_obj(args[ARG_sck].u_obj);
|
||||
}
|
||||
if (args[ARG_mosi].u_obj != MP_OBJ_NULL) {
|
||||
self->mosi = mp_obj_get_pin_obj(args[ARG_mosi].u_obj);
|
||||
}
|
||||
if (args[ARG_miso].u_obj != MP_OBJ_NULL) {
|
||||
self->miso = mp_obj_get_pin_obj(args[ARG_miso].u_obj);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true);
|
||||
pyb_spi_obj_t *self = m_new_obj(pyb_spi_obj_t);
|
||||
self->base.type = &pyb_spi_type;
|
||||
// set defaults
|
||||
self->baudrate = 500000;
|
||||
self->polarity = 0;
|
||||
self->phase = 0;
|
||||
self->sck = NULL;
|
||||
self->mosi = NULL;
|
||||
self->miso = NULL;
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
pyb_spi_init_helper(self, n_args, args, &kw_args);
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
pyb_spi_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_init_obj, 1, pyb_spi_init);
|
||||
|
||||
STATIC mp_obj_t pyb_spi_read(size_t n_args, const mp_obj_t *args) {
|
||||
pyb_spi_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
uint8_t write_byte = 0;
|
||||
if (n_args == 3) {
|
||||
write_byte = mp_obj_get_int(args[2]);
|
||||
}
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, mp_obj_get_int(args[1]));
|
||||
mp_hal_spi_transfer(self, 1, &write_byte, vstr.len, (uint8_t*)vstr.buf);
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_spi_read_obj, 2, 3, pyb_spi_read);
|
||||
|
||||
STATIC mp_obj_t pyb_spi_readinto(size_t n_args, const mp_obj_t *args) {
|
||||
pyb_spi_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
|
||||
uint8_t write_byte = 0;
|
||||
if (n_args == 3) {
|
||||
write_byte = mp_obj_get_int(args[2]);
|
||||
}
|
||||
mp_hal_spi_transfer(self, 1, &write_byte, bufinfo.len, (uint8_t*)bufinfo.buf);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_spi_readinto_obj, 2, 3, pyb_spi_readinto);
|
||||
|
||||
STATIC mp_obj_t pyb_spi_write(mp_obj_t self_in, mp_obj_t wr_buf_in) {
|
||||
pyb_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_buffer_info_t src_buf;
|
||||
mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ);
|
||||
mp_hal_spi_transfer(self, src_buf.len, (const uint8_t*)src_buf.buf, 0, NULL);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(pyb_spi_write_obj, pyb_spi_write);
|
||||
|
||||
STATIC mp_obj_t pyb_spi_write_readinto(mp_obj_t self_in, mp_obj_t wr_buf_in, mp_obj_t rd_buf_in) {
|
||||
pyb_spi_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_buffer_info_t src_buf;
|
||||
mp_get_buffer_raise(wr_buf_in, &src_buf, MP_BUFFER_READ);
|
||||
mp_buffer_info_t dest_buf;
|
||||
mp_get_buffer_raise(rd_buf_in, &dest_buf, MP_BUFFER_WRITE);
|
||||
if (src_buf.len != dest_buf.len) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "buffers must be the same length"));
|
||||
}
|
||||
mp_hal_spi_transfer(self, src_buf.len, (const uint8_t*)src_buf.buf, dest_buf.len, (uint8_t*)dest_buf.buf);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_3(pyb_spi_write_readinto_obj, pyb_spi_write_readinto);
|
||||
|
||||
STATIC const mp_rom_map_elem_t pyb_spi_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_spi_init_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&pyb_spi_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&pyb_spi_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&pyb_spi_write_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write_readinto), MP_ROM_PTR(&pyb_spi_write_readinto_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(pyb_spi_locals_dict, pyb_spi_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t pyb_spi_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_SPI,
|
||||
.print = pyb_spi_print,
|
||||
.make_new = pyb_spi_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&pyb_spi_locals_dict,
|
||||
};
|
||||
157
esp8266/modpybuart.c
Normal file
157
esp8266/modpybuart.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* 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 "ets_sys.h"
|
||||
#include "uart.h"
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "modpyb.h"
|
||||
|
||||
typedef struct _pyb_uart_obj_t {
|
||||
mp_obj_base_t base;
|
||||
uint8_t uart_id;
|
||||
} pyb_uart_obj_t;
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings for UART
|
||||
|
||||
STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "UART(%u)", self->uart_id);
|
||||
}
|
||||
|
||||
STATIC void pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
/*
|
||||
enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 9600} },
|
||||
{ MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} },
|
||||
{ MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
{ MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} },
|
||||
{ MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
*/
|
||||
// not implemented
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
|
||||
|
||||
// get uart id
|
||||
mp_int_t uart_id = mp_obj_get_int(args[0]);
|
||||
if (uart_id != 0 && uart_id != 1) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) does not exist", uart_id));
|
||||
}
|
||||
|
||||
// create instance
|
||||
pyb_uart_obj_t *self = m_new_obj(pyb_uart_obj_t);
|
||||
self->base.type = &pyb_uart_type;
|
||||
self->uart_id = uart_id;
|
||||
|
||||
if (n_args > 1 || n_kw > 0) {
|
||||
// init 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);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t pyb_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
pyb_uart_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_init_obj, 1, pyb_uart_init);
|
||||
|
||||
STATIC const mp_rom_map_elem_t pyb_uart_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_uart_init_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
||||
};
|
||||
|
||||
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) {
|
||||
mp_not_implemented("reading from UART");
|
||||
}
|
||||
|
||||
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 = MP_OBJ_TO_PTR(self_in);
|
||||
const byte *buf = buf_in;
|
||||
|
||||
/* TODO implement non-blocking
|
||||
// wait to be able to write the first character
|
||||
if (!uart_tx_wait(self, timeout)) {
|
||||
*errcode = EAGAIN;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
*/
|
||||
|
||||
// write the data
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
uart_tx_one_char(self->uart_id, *buf++);
|
||||
}
|
||||
|
||||
// return number of bytes written
|
||||
return size;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t pyb_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
|
||||
*errcode = EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
|
||||
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_dict_t*)&pyb_uart_locals_dict,
|
||||
};
|
||||
@@ -25,15 +25,21 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/nlr.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objtuple.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "extmod/misc.h"
|
||||
#include "genhdr/mpversion.h"
|
||||
#include "etshal.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
extern const mp_obj_type_t mp_fat_vfs_type;
|
||||
|
||||
STATIC const qstr os_uname_info_fields[] = {
|
||||
MP_QSTR_sysname, MP_QSTR_nodename,
|
||||
MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine
|
||||
@@ -65,9 +71,52 @@ STATIC mp_obj_t os_uname(void) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname);
|
||||
|
||||
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_uname), (mp_obj_t)&os_uname_obj },
|
||||
#if MICROPY_VFS_FAT
|
||||
mp_obj_t vfs_proxy_call(qstr method_name, mp_uint_t n_args, const mp_obj_t *args) {
|
||||
if (MP_STATE_PORT(fs_user_mount)[0] == NULL) {
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENODEV)));
|
||||
}
|
||||
|
||||
mp_obj_t meth[n_args + 2];
|
||||
mp_load_method(MP_STATE_PORT(fs_user_mount)[0], method_name, meth);
|
||||
memcpy(meth + 2, args, n_args * sizeof(*args));
|
||||
return mp_call_method_n_kw(n_args, 0, meth);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t os_listdir(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
return vfs_proxy_call(MP_QSTR_listdir, n_args, args);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(os_listdir_obj, 0, 1, os_listdir);
|
||||
|
||||
STATIC mp_obj_t os_remove(mp_obj_t path_in) {
|
||||
return vfs_proxy_call(MP_QSTR_remove, 1, &path_in);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_remove_obj, os_remove);
|
||||
#endif
|
||||
|
||||
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] = *WDEV_HWRNG;
|
||||
}
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom);
|
||||
|
||||
STATIC const mp_rom_map_elem_t os_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) },
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
{ MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) },
|
||||
#endif
|
||||
#if MICROPY_VFS_FAT
|
||||
{ MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&os_listdir_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&os_remove_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "py/gc.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/smallint.h"
|
||||
#include "modpyb.h"
|
||||
#include "modpybrtc.h"
|
||||
#include "timeutils.h"
|
||||
@@ -101,11 +102,51 @@ MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime);
|
||||
/// \function sleep(seconds)
|
||||
/// Sleep for the given number of seconds.
|
||||
STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) {
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
mp_hal_delay_ms(1000 * mp_obj_get_float(seconds_o));
|
||||
#else
|
||||
mp_hal_delay_ms(1000 * mp_obj_get_int(seconds_o));
|
||||
#endif
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_obj, time_sleep);
|
||||
|
||||
STATIC mp_obj_t time_sleep_ms(mp_obj_t arg) {
|
||||
mp_hal_delay_ms(mp_obj_get_int(arg));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_ms_obj, time_sleep_ms);
|
||||
|
||||
STATIC mp_obj_t time_sleep_us(mp_obj_t arg) {
|
||||
mp_hal_delay_us(mp_obj_get_int(arg));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_us_obj, time_sleep_us);
|
||||
|
||||
STATIC mp_obj_t time_ticks_ms(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_ms() & MP_SMALL_INT_POSITIVE_MASK);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(time_ticks_ms_obj, time_ticks_ms);
|
||||
|
||||
STATIC mp_obj_t time_ticks_us(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(mp_hal_ticks_us() & MP_SMALL_INT_POSITIVE_MASK);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(time_ticks_us_obj, time_ticks_us);
|
||||
|
||||
STATIC mp_obj_t time_ticks_cpu(void) {
|
||||
// TODO
|
||||
return MP_OBJ_NEW_SMALL_INT(0 & MP_SMALL_INT_POSITIVE_MASK);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(time_ticks_cpu_obj, time_ticks_cpu);
|
||||
|
||||
STATIC mp_obj_t time_ticks_diff(mp_obj_t start_in, mp_obj_t end_in) {
|
||||
// we assume that the arguments come from ticks_xx so are small ints
|
||||
uint32_t start = MP_OBJ_SMALL_INT_VALUE(start_in);
|
||||
uint32_t end = MP_OBJ_SMALL_INT_VALUE(end_in);
|
||||
return MP_OBJ_NEW_SMALL_INT((end - start) & MP_SMALL_INT_POSITIVE_MASK);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(time_ticks_diff_obj, time_ticks_diff);
|
||||
|
||||
/// \function time()
|
||||
/// Returns the number of seconds, as an integer, since 1/1/2000.
|
||||
STATIC mp_obj_t time_time(void) {
|
||||
@@ -120,6 +161,12 @@ STATIC const mp_map_elem_t time_module_globals_table[] = {
|
||||
{ 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_sleep_ms), (mp_obj_t)&time_sleep_ms_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sleep_us), (mp_obj_t)&time_sleep_us_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ticks_ms), (mp_obj_t)&time_ticks_ms_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ticks_us), (mp_obj_t)&time_ticks_us_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ticks_cpu), (mp_obj_t)&time_ticks_cpu_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ticks_diff), (mp_obj_t)&time_ticks_diff_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_time_obj },
|
||||
};
|
||||
|
||||
|
||||
@@ -2,19 +2,23 @@
|
||||
|
||||
// options to control how Micro Python is built
|
||||
|
||||
#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C)
|
||||
#define MICROPY_ALLOC_PATH_MAX (128)
|
||||
#define MICROPY_EMIT_X64 (0)
|
||||
#define MICROPY_EMIT_THUMB (0)
|
||||
#define MICROPY_EMIT_INLINE_THUMB (0)
|
||||
#define MICROPY_MEM_STATS (0)
|
||||
#define MICROPY_DEBUG_PRINTERS (0)
|
||||
#define MICROPY_DEBUG_PRINTERS (1)
|
||||
#define MICROPY_ENABLE_GC (1)
|
||||
#define MICROPY_STACK_CHECK (0)
|
||||
#define MICROPY_REPL_EVENT_DRIVEN (1)
|
||||
#define MICROPY_STACK_CHECK (1)
|
||||
#define MICROPY_REPL_EVENT_DRIVEN (0)
|
||||
#define MICROPY_REPL_AUTO_INDENT (1)
|
||||
#define MICROPY_HELPER_REPL (1)
|
||||
#define MICROPY_HELPER_LEXER_UNIX (0)
|
||||
#define MICROPY_ENABLE_SOURCE_LINE (1)
|
||||
#define MICROPY_MODULE_WEAK_LINKS (1)
|
||||
#define MICROPY_CAN_OVERRIDE_BUILTINS (1)
|
||||
#define MICROPY_PY_BUILTINS_COMPLEX (0)
|
||||
#define MICROPY_PY_BUILTINS_STR_UNICODE (1)
|
||||
#define MICROPY_PY_BUILTINS_BYTEARRAY (1)
|
||||
#define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
|
||||
@@ -25,19 +29,55 @@
|
||||
#define MICROPY_PY___FILE__ (0)
|
||||
#define MICROPY_PY_GC (1)
|
||||
#define MICROPY_PY_ARRAY (1)
|
||||
#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1)
|
||||
#define MICROPY_PY_COLLECTIONS (1)
|
||||
#define MICROPY_PY_MATH (0)
|
||||
#define MICROPY_PY_MATH (1)
|
||||
#define MICROPY_PY_CMATH (0)
|
||||
#define MICROPY_PY_IO (1)
|
||||
#define MICROPY_PY_STRUCT (1)
|
||||
#define MICROPY_PY_SYS (1)
|
||||
#define MICROPY_PY_SYS_MAXSIZE (1)
|
||||
#define MICROPY_PY_SYS_EXIT (1)
|
||||
#define MICROPY_PY_SYS_STDFILES (1)
|
||||
#define MICROPY_CPYTHON_COMPAT (0)
|
||||
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG)
|
||||
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE)
|
||||
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE)
|
||||
#define MICROPY_PY_UBINASCII (1)
|
||||
#define MICROPY_PY_UCTYPES (1)
|
||||
#define MICROPY_PY_UHASHLIB (1)
|
||||
#define MICROPY_PY_UHASHLIB_SHA1 (1)
|
||||
#define MICROPY_PY_UHEAPQ (1)
|
||||
#define MICROPY_PY_UJSON (1)
|
||||
#define MICROPY_PY_URANDOM (1)
|
||||
#define MICROPY_PY_URE (1)
|
||||
#define MICROPY_PY_UZLIB (1)
|
||||
#define MICROPY_PY_LWIP (1)
|
||||
#define MICROPY_PY_MACHINE (1)
|
||||
#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
|
||||
#define MICROPY_PY_OS_DUPTERM (1)
|
||||
#define MICROPY_CPYTHON_COMPAT (1)
|
||||
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
|
||||
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
|
||||
#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL)
|
||||
#define MICROPY_STREAMS_NON_BLOCK (1)
|
||||
#define MICROPY_MODULE_FROZEN (1)
|
||||
#define MICROPY_MODULE_FROZEN_LEXER mp_lexer_new_from_str32
|
||||
|
||||
#define MICROPY_FATFS_ENABLE_LFN (1)
|
||||
#define MICROPY_FATFS_RPATH (2)
|
||||
#define MICROPY_FATFS_VOLUMES (2)
|
||||
#define MICROPY_FATFS_MAX_SS (4096)
|
||||
#define MICROPY_FATFS_LFN_CODE_PAGE (437) /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
|
||||
#define MICROPY_FSUSERMOUNT (1)
|
||||
#define MICROPY_VFS_FAT (1)
|
||||
|
||||
#define MICROPY_EVENT_POLL_HOOK {ets_event_poll();}
|
||||
#define MICROPY_VM_HOOK_COUNT (10)
|
||||
#define MICROPY_VM_HOOK_INIT static uint vm_hook_divisor = MICROPY_VM_HOOK_COUNT;
|
||||
#define MICROPY_VM_HOOK_POLL if (--vm_hook_divisor == 0) { \
|
||||
vm_hook_divisor = MICROPY_VM_HOOK_COUNT; \
|
||||
extern void ets_loop_iter(void); \
|
||||
ets_loop_iter(); \
|
||||
}
|
||||
#define MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_POLL
|
||||
#define MICROPY_VM_HOOK_RETURN MICROPY_VM_HOOK_POLL
|
||||
|
||||
// type definitions for the specific machine
|
||||
|
||||
@@ -45,6 +85,8 @@
|
||||
|
||||
#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p)))
|
||||
|
||||
#define MP_SSIZE_MAX (0x7fffffff)
|
||||
|
||||
#define UINT_FMT "%u"
|
||||
#define INT_FMT "%d"
|
||||
|
||||
@@ -53,6 +95,7 @@ typedef uint32_t mp_uint_t; // must be pointer size
|
||||
typedef void *machine_ptr_t; // must be of pointer size
|
||||
typedef const void *machine_const_ptr_t; // must be of pointer size
|
||||
typedef long mp_off_t;
|
||||
typedef uint32_t sys_prot_t; // for modlwip
|
||||
|
||||
#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
|
||||
|
||||
@@ -67,26 +110,33 @@ extern const struct _mp_obj_module_t esp_module;
|
||||
extern const struct _mp_obj_module_t network_module;
|
||||
extern const struct _mp_obj_module_t utime_module;
|
||||
extern const struct _mp_obj_module_t uos_module;
|
||||
extern const struct _mp_obj_module_t mp_module_lwip;
|
||||
extern const struct _mp_obj_module_t mp_module_machine;
|
||||
extern const struct _mp_obj_module_t onewire_module;
|
||||
|
||||
#define MICROPY_PORT_BUILTIN_MODULES \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_pyb), (mp_obj_t)&pyb_module }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_esp), (mp_obj_t)&esp_module }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_lwip), (mp_obj_t)&mp_module_lwip }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&mp_module_lwip }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_lwip }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&network_module }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&onewire_module }, \
|
||||
|
||||
#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&utime_module }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&uos_module }, \
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_json), (mp_obj_t)&mp_module_ujson }, \
|
||||
|
||||
#define MP_STATE_PORT MP_STATE_VM
|
||||
|
||||
#define MICROPY_PORT_ROOT_POINTERS \
|
||||
const char *readline_hist[8]; \
|
||||
\
|
||||
/* Singleton instance of scan callback, meaning that there can
|
||||
be only one concurrent AP scan. */ \
|
||||
mp_obj_t scan_cb_obj; \
|
||||
vstr_t *repl_line; \
|
||||
mp_obj_t mp_kbd_exception; \
|
||||
|
||||
// We need to provide a declaration/definition of alloca()
|
||||
#include <alloca.h>
|
||||
@@ -97,3 +147,5 @@ extern const struct _mp_obj_module_t uos_module;
|
||||
#define MICROPY_HW_BOARD_NAME "ESP module"
|
||||
#define MICROPY_HW_MCU_NAME "ESP8266"
|
||||
#define MICROPY_PY_SYS_PLATFORM "ESP8266"
|
||||
|
||||
#define _assert(expr) ((expr) ? (void)0 : __assert_func(__FILE__, __LINE__, __func__, #expr))
|
||||
|
||||
@@ -39,7 +39,6 @@ Q(elapsed_micros)
|
||||
Q(delay)
|
||||
Q(udelay)
|
||||
Q(sync)
|
||||
Q(hard_reset)
|
||||
Q(unique_id)
|
||||
|
||||
// uos module
|
||||
@@ -54,10 +53,12 @@ Q(machine)
|
||||
|
||||
Q(esp)
|
||||
Q(socket)
|
||||
Q(usocket)
|
||||
Q(connect)
|
||||
Q(disconnect)
|
||||
Q(wifi_mode)
|
||||
Q(phy_mode)
|
||||
Q(osdebug)
|
||||
Q(sleep_type)
|
||||
Q(deepsleep)
|
||||
Q(adc)
|
||||
@@ -65,7 +66,11 @@ Q(vdd33)
|
||||
Q(chip_id)
|
||||
Q(flash_id)
|
||||
Q(flash_read)
|
||||
Q(flash_write)
|
||||
Q(flash_erase)
|
||||
Q(sdk_version)
|
||||
Q(freemem)
|
||||
Q(meminfo)
|
||||
Q(getaddrinfo)
|
||||
Q(send)
|
||||
Q(sendto)
|
||||
@@ -84,6 +89,7 @@ Q(onconnect)
|
||||
Q(onrecv)
|
||||
Q(onsent)
|
||||
Q(ondisconnect)
|
||||
Q(neopixel_write)
|
||||
Q(MODE_11B)
|
||||
Q(MODE_11G)
|
||||
Q(MODE_11N)
|
||||
@@ -97,16 +103,23 @@ Q(STA_AP_MODE)
|
||||
// network module
|
||||
Q(network)
|
||||
Q(WLAN)
|
||||
Q(active)
|
||||
Q(scan)
|
||||
Q(status)
|
||||
Q(isconnected)
|
||||
Q(mac)
|
||||
Q(config)
|
||||
Q(ifconfig)
|
||||
Q(STA_IF)
|
||||
Q(AP_IF)
|
||||
Q(STAT_IDLE)
|
||||
Q(STAT_CONNECTING)
|
||||
Q(STAT_WRONG_PASSWORD)
|
||||
Q(STAT_NO_AP_FOUND)
|
||||
Q(STAT_CONNECT_FAIL)
|
||||
Q(STAT_GOT_IP)
|
||||
// config keys
|
||||
Q(essid)
|
||||
|
||||
// Pin class
|
||||
Q(Pin)
|
||||
@@ -117,12 +130,19 @@ Q(value)
|
||||
Q(low)
|
||||
Q(high)
|
||||
Q(IN)
|
||||
Q(OUT_PP)
|
||||
Q(OUT_OD)
|
||||
Q(OUT)
|
||||
Q(OPEN_DRAIN)
|
||||
Q(PULL_NONE)
|
||||
Q(PULL_UP)
|
||||
Q(PULL_DOWN)
|
||||
|
||||
// PWM class
|
||||
Q(PWM)
|
||||
Q(init)
|
||||
Q(deinit)
|
||||
Q(freq)
|
||||
Q(duty)
|
||||
|
||||
// RTC
|
||||
Q(RTC)
|
||||
Q(datetime)
|
||||
@@ -132,9 +152,70 @@ Q(memory)
|
||||
Q(ADC)
|
||||
Q(read)
|
||||
|
||||
// UART
|
||||
Q(UART)
|
||||
Q(init)
|
||||
|
||||
// I2C
|
||||
Q(I2C)
|
||||
Q(init)
|
||||
Q(scl)
|
||||
Q(sda)
|
||||
Q(freq)
|
||||
Q(readfrom)
|
||||
Q(writeto)
|
||||
Q(stop)
|
||||
Q(buf)
|
||||
Q(addr)
|
||||
Q(n)
|
||||
|
||||
// SPI
|
||||
Q(SPI)
|
||||
Q(init)
|
||||
Q(baudrate)
|
||||
Q(phase)
|
||||
Q(polarity)
|
||||
Q(sck)
|
||||
Q(mosi)
|
||||
Q(miso)
|
||||
Q(read)
|
||||
Q(readinto)
|
||||
Q(write)
|
||||
Q(write_readinto)
|
||||
|
||||
// utime
|
||||
Q(utime)
|
||||
Q(localtime)
|
||||
Q(mktime)
|
||||
Q(sleep)
|
||||
Q(sleep_ms)
|
||||
Q(sleep_us)
|
||||
Q(ticks_ms)
|
||||
Q(ticks_us)
|
||||
Q(ticks_cpu)
|
||||
Q(ticks_diff)
|
||||
Q(time)
|
||||
|
||||
// machine
|
||||
Q(reset)
|
||||
Q(Timer)
|
||||
Q(callback)
|
||||
Q(deinit)
|
||||
Q(init)
|
||||
Q(mode)
|
||||
Q(period)
|
||||
Q(ONE_SHOT)
|
||||
Q(PERIODIC)
|
||||
|
||||
// onewire
|
||||
Q(_onewire)
|
||||
Q(onewire)
|
||||
Q(timings)
|
||||
Q(reset)
|
||||
Q(readbit)
|
||||
Q(readbyte)
|
||||
Q(writebit)
|
||||
Q(writebyte)
|
||||
Q(crc8)
|
||||
|
||||
Q(json)
|
||||
|
||||
64
esp8266/tests/neopixel.py
Normal file
64
esp8266/tests/neopixel.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import time
|
||||
import machine
|
||||
from esp import neopixel_write
|
||||
|
||||
class NeoPixel:
|
||||
def __init__(self, pin, n):
|
||||
self.pin = pin
|
||||
self.n = n
|
||||
self.buf = bytearray(n * 3)
|
||||
|
||||
def __setitem__(self, index, val):
|
||||
r, g, b = val
|
||||
self.buf[index * 3] = g
|
||||
self.buf[index * 3 + 1] = r
|
||||
self.buf[index * 3 + 2] = b
|
||||
|
||||
def __getitem__(self, index):
|
||||
i = index * 3
|
||||
return self.buf[i], self.buf[i + 1], self.buf[i + 2]
|
||||
|
||||
def write(self):
|
||||
neopixel_write(self.pin, self.buf, True)
|
||||
|
||||
def test():
|
||||
# put a neopixel strip on GPIO4
|
||||
p = machine.Pin(4, machine.Pin.OUT)
|
||||
np = NeoPixel(p, 8)
|
||||
n = np.n
|
||||
|
||||
# cycle
|
||||
for i in range(4 * n):
|
||||
for j in range(n):
|
||||
np[j] = (0, 0, 0)
|
||||
np[i % n] = (255, 255, 255)
|
||||
np.write()
|
||||
time.sleep_ms(25)
|
||||
|
||||
# bounce
|
||||
for i in range(4 * n):
|
||||
for j in range(n):
|
||||
np[j] = (0, 0, 128)
|
||||
if (i // n) % 2 == 0:
|
||||
np[i % n] = (0, 0, 0)
|
||||
else:
|
||||
np[n - 1 - (i % n)] = (0, 0, 0)
|
||||
np.write()
|
||||
time.sleep_ms(60)
|
||||
|
||||
# fade in/out
|
||||
for i in range(0, 4 * 256, 8):
|
||||
for j in range(n):
|
||||
if (i // 256) % 2 == 0:
|
||||
val = i & 0xff
|
||||
else:
|
||||
val = 255 - (i & 0xff)
|
||||
np[j] = (val, 0, 0)
|
||||
np.write()
|
||||
|
||||
# clear
|
||||
for i in range(n):
|
||||
np[i] = (0, 0, 0)
|
||||
np.write()
|
||||
|
||||
test()
|
||||
147
esp8266/tests/onewire.py
Normal file
147
esp8266/tests/onewire.py
Normal file
@@ -0,0 +1,147 @@
|
||||
import time
|
||||
import pyb
|
||||
import _onewire as _ow
|
||||
|
||||
class OneWire:
|
||||
CMD_SEARCHROM = const(0xf0)
|
||||
CMD_READROM = const(0x33)
|
||||
CMD_MATCHROM = const(0x55)
|
||||
CMD_SKIPROM = const(0xcc)
|
||||
|
||||
def __init__(self, pin):
|
||||
self.pin = pin
|
||||
self.pin.init(pin.OPEN_DRAIN, pin.PULL_NONE)
|
||||
|
||||
def reset(self):
|
||||
return _ow.reset(self.pin)
|
||||
|
||||
def read_bit(self):
|
||||
return _ow.readbit(self.pin)
|
||||
|
||||
def read_byte(self):
|
||||
return _ow.readbyte(self.pin)
|
||||
|
||||
def read_bytes(self, count):
|
||||
buf = bytearray(count)
|
||||
for i in range(count):
|
||||
buf[i] = _ow.readbyte(self.pin)
|
||||
return buf
|
||||
|
||||
def write_bit(self, value):
|
||||
return _ow.writebit(self.pin, value)
|
||||
|
||||
def write_byte(self, value):
|
||||
return _ow.writebyte(self.pin, value)
|
||||
|
||||
def write_bytes(self, buf):
|
||||
for b in buf:
|
||||
_ow.writebyte(self.pin, b)
|
||||
|
||||
def select_rom(self, rom):
|
||||
self.reset()
|
||||
self.write_byte(CMD_MATCHROM)
|
||||
self.write_bytes(rom)
|
||||
|
||||
def scan(self):
|
||||
devices = []
|
||||
diff = 65
|
||||
rom = False
|
||||
for i in range(0xff):
|
||||
rom, diff = self._search_rom(rom, diff)
|
||||
if rom:
|
||||
devices += [rom]
|
||||
if diff == 0:
|
||||
break
|
||||
return devices
|
||||
|
||||
def _search_rom(self, l_rom, diff):
|
||||
if not self.reset():
|
||||
return None, 0
|
||||
self.write_byte(CMD_SEARCHROM)
|
||||
if not l_rom:
|
||||
l_rom = bytearray(8)
|
||||
rom = bytearray(8)
|
||||
next_diff = 0
|
||||
i = 64
|
||||
for byte in range(8):
|
||||
r_b = 0
|
||||
for bit in range(8):
|
||||
b = self.read_bit()
|
||||
if self.read_bit():
|
||||
if b: # there are no devices or there is an error on the bus
|
||||
return None, 0
|
||||
else:
|
||||
if not b: # collision, two devices with different bit meaning
|
||||
if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i):
|
||||
b = 1
|
||||
next_diff = i
|
||||
self.write_bit(b)
|
||||
if b:
|
||||
r_b |= 1 << bit
|
||||
i -= 1
|
||||
rom[byte] = r_b
|
||||
return rom, next_diff
|
||||
|
||||
def crc8(self, data):
|
||||
return _ow.crc8(data)
|
||||
|
||||
class DS18B20:
|
||||
THERM_CMD_CONVERTTEMP = const(0x44)
|
||||
THERM_CMD_RSCRATCHPAD = const(0xbe)
|
||||
|
||||
def __init__(self, onewire):
|
||||
self.ow = onewire
|
||||
self.roms = []
|
||||
|
||||
def scan(self):
|
||||
self.roms = []
|
||||
for rom in self.ow.scan():
|
||||
if rom[0] == 0x28:
|
||||
self.roms += [rom]
|
||||
return self.roms
|
||||
|
||||
def start_measure(self):
|
||||
if not self.ow.reset():
|
||||
return False
|
||||
self.ow.write_byte(CMD_SKIPROM)
|
||||
self.ow.write_byte(THERM_CMD_CONVERTTEMP)
|
||||
return True
|
||||
|
||||
def get_temp(self, rom):
|
||||
if not self.ow.reset():
|
||||
return None
|
||||
|
||||
self.ow.select_rom(rom)
|
||||
self.ow.write_byte(THERM_CMD_RSCRATCHPAD)
|
||||
|
||||
buf = self.ow.read_bytes(9)
|
||||
if self.ow.crc8(buf):
|
||||
return None
|
||||
|
||||
return self._convert_temp(buf)
|
||||
|
||||
def _convert_temp(self, data):
|
||||
temp_lsb = data[0]
|
||||
temp_msb = data[1]
|
||||
return (temp_msb << 8 | temp_lsb) / 16
|
||||
|
||||
# connect 1-wire temp sensors to GPIO12 for this test
|
||||
def test():
|
||||
dat = pyb.Pin(12)
|
||||
ow = OneWire(dat)
|
||||
|
||||
ds = DS18B20(ow)
|
||||
roms = ow.scan()
|
||||
print('found devices:', roms)
|
||||
|
||||
for i in range(4):
|
||||
print('temperatures:', end=' ')
|
||||
ds.start_measure()
|
||||
time.sleep_ms(750)
|
||||
for rom in roms:
|
||||
print(ds.get_temp(rom), end=' ')
|
||||
print()
|
||||
|
||||
#pyb.freq(80000000)
|
||||
#pyb.freq(160000000)
|
||||
test()
|
||||
@@ -19,21 +19,33 @@
|
||||
#include "user_interface.h"
|
||||
#include "esp_mphal.h"
|
||||
|
||||
#define RX_BUF_SIZE (256)
|
||||
#define UART_REPL UART0
|
||||
|
||||
// UartDev is defined and initialized in rom code.
|
||||
extern UartDevice UartDev;
|
||||
|
||||
// the uart to which OS messages go; -1 to disable
|
||||
static int uart_os = UART_OS;
|
||||
|
||||
/* unused
|
||||
// circular buffer for RX buffering
|
||||
#define RX_BUF_SIZE (256)
|
||||
static uint16_t rx_buf_in;
|
||||
static uint16_t rx_buf_out;
|
||||
static uint8_t rx_buf[RX_BUF_SIZE];
|
||||
*/
|
||||
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
static os_event_t uart_evt_queue[16];
|
||||
#endif
|
||||
|
||||
static void uart0_rx_intr_handler(void *para);
|
||||
|
||||
void soft_reset(void);
|
||||
void mp_keyboard_interrupt(void);
|
||||
|
||||
int interrupt_char;
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : uart_config
|
||||
* Description : Internal used function
|
||||
@@ -83,9 +95,11 @@ static void ICACHE_FLASH_ATTR uart_config(uint8 uart_no) {
|
||||
// enable rx_interrupt
|
||||
SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA);
|
||||
|
||||
/* unused
|
||||
// init RX buffer
|
||||
rx_buf_in = 0;
|
||||
rx_buf_out = 0;
|
||||
*/
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -105,6 +119,15 @@ void uart_tx_one_char(uint8 uart, uint8 TxChar) {
|
||||
WRITE_PERI_REG(UART_FIFO(uart), TxChar);
|
||||
}
|
||||
|
||||
void uart_flush(uint8 uart) {
|
||||
while (true) {
|
||||
uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S);
|
||||
if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : uart1_write_char
|
||||
* Description : Internal used function
|
||||
@@ -114,15 +137,23 @@ void uart_tx_one_char(uint8 uart, uint8 TxChar) {
|
||||
*******************************************************************************/
|
||||
static void ICACHE_FLASH_ATTR
|
||||
uart_os_write_char(char c) {
|
||||
if (uart_os == -1) {
|
||||
return;
|
||||
}
|
||||
if (c == '\n') {
|
||||
uart_tx_one_char(UART_OS, '\r');
|
||||
uart_tx_one_char(UART_OS, '\n');
|
||||
uart_tx_one_char(uart_os, '\r');
|
||||
uart_tx_one_char(uart_os, '\n');
|
||||
} else if (c == '\r') {
|
||||
} else {
|
||||
uart_tx_one_char(UART_OS, c);
|
||||
uart_tx_one_char(uart_os, c);
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR
|
||||
uart_os_config(int uart) {
|
||||
uart_os = uart;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : uart0_rx_intr_handler
|
||||
* Description : Internal used function
|
||||
@@ -150,7 +181,22 @@ static void uart0_rx_intr_handler(void *para) {
|
||||
read_chars:
|
||||
#if 1 //MICROPY_REPL_EVENT_DRIVEN is not available here
|
||||
ETS_UART_INTR_DISABLE();
|
||||
system_os_post(UART_TASK_ID, 0, 0);
|
||||
|
||||
while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
|
||||
uint8 RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xff;
|
||||
if (RcvChar == interrupt_char) {
|
||||
mp_keyboard_interrupt();
|
||||
} else {
|
||||
ringbuf_put(&input_buf, RcvChar);
|
||||
}
|
||||
}
|
||||
|
||||
mp_hal_signal_input();
|
||||
|
||||
// Clear pending FIFO interrupts
|
||||
WRITE_PERI_REG(UART_INT_CLR(UART_REPL), UART_RXFIFO_TOUT_INT_CLR | UART_RXFIFO_FULL_INT_ST);
|
||||
ETS_UART_INTR_ENABLE();
|
||||
|
||||
#else
|
||||
while (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
|
||||
uint8 RcvChar = READ_PERI_REG(UART_FIFO(uart_no)) & 0xff;
|
||||
@@ -164,6 +210,7 @@ static void uart0_rx_intr_handler(void *para) {
|
||||
}
|
||||
}
|
||||
|
||||
/* unused
|
||||
int uart0_rx(void) {
|
||||
if (rx_buf_out != rx_buf_in) {
|
||||
int chr = rx_buf[rx_buf_out];
|
||||
@@ -173,6 +220,7 @@ int uart0_rx(void) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
int uart_rx_one_char(uint8 uart_no) {
|
||||
if (READ_PERI_REG(UART_STATUS(uart_no)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
|
||||
@@ -196,7 +244,7 @@ void ICACHE_FLASH_ATTR uart_init(UartBautRate uart0_br, UartBautRate uart1_br) {
|
||||
uart_config(UART1);
|
||||
ETS_UART_INTR_ENABLE();
|
||||
|
||||
// install uart1 putc callback
|
||||
// install handler for "os" messages
|
||||
os_install_putc1((void *)uart_os_write_char);
|
||||
}
|
||||
|
||||
@@ -209,22 +257,30 @@ void ICACHE_FLASH_ATTR uart_reattach() {
|
||||
#include "py/obj.h"
|
||||
#include "lib/utils/pyexec.h"
|
||||
|
||||
void soft_reset(void);
|
||||
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
void uart_task_handler(os_event_t *evt) {
|
||||
if (pyexec_repl_active) {
|
||||
// TODO: Just returning here isn't exactly right.
|
||||
// What really should be done is something like
|
||||
// enquing delayed event to itself, for another
|
||||
// chance to feed data to REPL. Otherwise, there
|
||||
// can be situation when buffer has bunch of data,
|
||||
// and sits unprocessed, because we consumed all
|
||||
// processing signals like this.
|
||||
return;
|
||||
}
|
||||
|
||||
int c, ret = 0;
|
||||
while ((c = uart_rx_one_char(UART_REPL)) >= 0) {
|
||||
while ((c = ringbuf_get(&input_buf)) >= 0) {
|
||||
if (c == interrupt_char) {
|
||||
mp_keyboard_interrupt();
|
||||
}
|
||||
ret = pyexec_event_repl_process_char(c);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear pending FIFO interrupts
|
||||
WRITE_PERI_REG(UART_INT_CLR(UART_REPL), UART_RXFIFO_TOUT_INT_CLR | UART_RXFIFO_FULL_INT_ST);
|
||||
// Enable UART interrupts, so our task will receive events again from IRQ handler
|
||||
ETS_UART_INTR_ENABLE();
|
||||
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
soft_reset();
|
||||
}
|
||||
@@ -233,3 +289,4 @@ void uart_task_handler(os_event_t *evt) {
|
||||
void uart_task_init() {
|
||||
system_os_task(uart_task_handler, UART_TASK_ID, uart_evt_queue, sizeof(uart_evt_queue) / sizeof(*uart_evt_queue));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -92,5 +92,7 @@ typedef struct {
|
||||
void uart_init(UartBautRate uart0_br, UartBautRate uart1_br);
|
||||
int uart0_rx(void);
|
||||
void uart_tx_one_char(uint8 uart, uint8 TxChar);
|
||||
void uart_flush(uint8 uart);
|
||||
void uart_os_config(int uart);
|
||||
|
||||
#endif // _INCLUDED_UART_H_
|
||||
|
||||
28
examples/network/http_client.py
Normal file
28
examples/network/http_client.py
Normal file
@@ -0,0 +1,28 @@
|
||||
try:
|
||||
import usocket as socket
|
||||
except:
|
||||
import socket
|
||||
|
||||
|
||||
def main(use_stream=False):
|
||||
s = socket.socket()
|
||||
|
||||
ai = socket.getaddrinfo("google.com", 80)
|
||||
print("Address infos:", ai)
|
||||
addr = ai[0][4]
|
||||
|
||||
print("Connect address:", addr)
|
||||
s.connect(addr)
|
||||
|
||||
if use_stream:
|
||||
# MicroPython socket objects support stream (aka file) interface
|
||||
# directly, but the line below is needed for CPython.
|
||||
s = s.makefile("rwb", 0)
|
||||
s.write(b"GET / HTTP/1.0\n\n")
|
||||
print(s.readall())
|
||||
else:
|
||||
s.send(b"GET / HTTP/1.0\n\n")
|
||||
print(s.recv(4096))
|
||||
|
||||
|
||||
main()
|
||||
36
examples/network/http_client_ssl.py
Normal file
36
examples/network/http_client_ssl.py
Normal file
@@ -0,0 +1,36 @@
|
||||
try:
|
||||
import usocket as _socket
|
||||
except:
|
||||
import _socket
|
||||
try:
|
||||
import ussl as ssl
|
||||
except:
|
||||
import ssl
|
||||
|
||||
|
||||
def main(use_stream=True):
|
||||
s = _socket.socket()
|
||||
|
||||
ai = _socket.getaddrinfo("google.com", 443)
|
||||
print("Address infos:", ai)
|
||||
addr = ai[0][4]
|
||||
|
||||
print("Connect address:", addr)
|
||||
s.connect(addr)
|
||||
|
||||
s = ssl.wrap_socket(s)
|
||||
print(s)
|
||||
|
||||
if use_stream:
|
||||
# Both CPython and MicroPython SSLSocket objects support read() and
|
||||
# write() methods.
|
||||
s.write(b"GET / HTTP/1.0\n\n")
|
||||
print(s.read(4096))
|
||||
else:
|
||||
# MicroPython SSLSocket objects implement only stream interface, not
|
||||
# socket interface
|
||||
s.send(b"GET / HTTP/1.0\n\n")
|
||||
print(s.recv(4096))
|
||||
|
||||
|
||||
main()
|
||||
47
examples/network/http_server.py
Normal file
47
examples/network/http_server.py
Normal file
@@ -0,0 +1,47 @@
|
||||
try:
|
||||
import usocket as socket
|
||||
except:
|
||||
import socket
|
||||
|
||||
|
||||
CONTENT = b"""\
|
||||
HTTP/1.0 200 OK
|
||||
|
||||
Hello #%d from MicroPython!
|
||||
"""
|
||||
|
||||
def main(use_stream=False):
|
||||
s = socket.socket()
|
||||
|
||||
# Binding to all interfaces - server will be accessible to other hosts!
|
||||
ai = socket.getaddrinfo("0.0.0.0", 8080)
|
||||
print("Bind address info:", ai)
|
||||
addr = ai[0][4]
|
||||
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(addr)
|
||||
s.listen(5)
|
||||
print("Listening, connect your browser to http://<this_host>:8080/")
|
||||
|
||||
counter = 0
|
||||
while True:
|
||||
res = s.accept()
|
||||
client_s = res[0]
|
||||
client_addr = res[1]
|
||||
print("Client address:", client_addr)
|
||||
print("Client socket:", client_s)
|
||||
print("Request:")
|
||||
if use_stream:
|
||||
# MicroPython socket objects support stream (aka file) interface
|
||||
# directly.
|
||||
print(client_s.read(4096))
|
||||
client_s.write(CONTENT % counter)
|
||||
else:
|
||||
print(client_s.recv(4096))
|
||||
client_s.send(CONTENT % counter)
|
||||
client_s.close()
|
||||
counter += 1
|
||||
print()
|
||||
|
||||
|
||||
main()
|
||||
@@ -1,32 +0,0 @@
|
||||
try:
|
||||
import usocket as _socket
|
||||
except:
|
||||
import _socket
|
||||
|
||||
|
||||
s = _socket.socket()
|
||||
|
||||
if 1:
|
||||
ai = _socket.getaddrinfo("google.com", 80)
|
||||
print("Address infos:", ai)
|
||||
addr = ai[0][4]
|
||||
else:
|
||||
# Deprecated ways to construct connection address
|
||||
addr = _socket.sockaddr_in()
|
||||
addr.sin_family = 2
|
||||
#addr.sin_addr = (0x0100 << 16) + 0x007f
|
||||
#addr.sin_addr = (0x7f00 << 16) + 0x0001
|
||||
#addr.sin_addr = _socket.inet_aton("127.0.0.1")
|
||||
addr.sin_addr = _socket.gethostbyname("google.com")
|
||||
addr.sin_port = _socket.htons(80)
|
||||
|
||||
print("Connect address:", addr)
|
||||
s.connect(addr)
|
||||
|
||||
if 0:
|
||||
# MicroPython rawsocket module supports file interface directly
|
||||
s.write("GET / HTTP/1.0\n\n")
|
||||
print(s.readall())
|
||||
else:
|
||||
s.send(b"GET / HTTP/1.0\n\n")
|
||||
print(s.recv(4096))
|
||||
@@ -1,41 +0,0 @@
|
||||
try:
|
||||
import usocket as socket
|
||||
except:
|
||||
import socket
|
||||
|
||||
|
||||
CONTENT = """\
|
||||
HTTP/1.0 200 OK
|
||||
|
||||
Hello #{} from MicroPython!
|
||||
"""
|
||||
|
||||
s = socket.socket()
|
||||
|
||||
ai = socket.getaddrinfo("127.0.0.1", 8080)
|
||||
print("Bind address info:", ai)
|
||||
addr = ai[0][4]
|
||||
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(addr)
|
||||
s.listen(5)
|
||||
print("Listening, connect your browser to http://127.0.0.1:8080/")
|
||||
|
||||
counter = 0
|
||||
while True:
|
||||
res = s.accept()
|
||||
client_s = res[0]
|
||||
client_addr = res[1]
|
||||
print("Client address:", client_addr)
|
||||
print("Client socket:", client_s)
|
||||
print("Request:")
|
||||
if 0:
|
||||
# MicroPython rawsocket module supports file interface directly
|
||||
print(client_s.read(4096))
|
||||
#print(client_s.readall())
|
||||
client_s.write(CONTENT.format(counter))
|
||||
else:
|
||||
print(client_s.recv(4096))
|
||||
client_s.send(bytes(CONTENT.format(counter), "ascii"))
|
||||
client_s.close()
|
||||
counter += 1
|
||||
@@ -26,13 +26,15 @@
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#if MICROPY_FSUSERMOUNT
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "py/nlr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "lib/fatfs/ff.h"
|
||||
#include "fsusermount.h"
|
||||
|
||||
STATIC mp_obj_t pyb_mount(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
fs_user_mount_t *fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, bool mkfs) {
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
|
||||
{ MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
|
||||
@@ -51,67 +53,150 @@ STATIC mp_obj_t pyb_mount(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *
|
||||
if (device == mp_const_none) {
|
||||
// umount
|
||||
FRESULT res = FR_NO_FILESYSTEM;
|
||||
if (MP_STATE_PORT(fs_user_mount) != NULL) {
|
||||
res = f_mount(NULL, MP_STATE_PORT(fs_user_mount)->str, 0);
|
||||
m_del_obj(fs_user_mount_t, MP_STATE_PORT(fs_user_mount));
|
||||
MP_STATE_PORT(fs_user_mount) = NULL;
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
|
||||
fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
|
||||
if (vfs != NULL && !memcmp(mnt_str, vfs->str, mnt_len + 1)) {
|
||||
res = f_mount(NULL, vfs->str, 0);
|
||||
if (vfs->flags & FSUSER_FREE_OBJ) {
|
||||
m_del_obj(fs_user_mount_t, vfs);
|
||||
}
|
||||
MP_STATE_PORT(fs_user_mount)[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (res != FR_OK) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't umount"));
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
// mount
|
||||
if (MP_STATE_PORT(fs_user_mount) != NULL) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "device already mounted"));
|
||||
size_t i = 0;
|
||||
for (; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
|
||||
if (MP_STATE_PORT(fs_user_mount)[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount))) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "too many devices mounted"));
|
||||
}
|
||||
|
||||
// create new object
|
||||
MP_STATE_PORT(fs_user_mount) = m_new_obj(fs_user_mount_t);
|
||||
MP_STATE_PORT(fs_user_mount)->str = mnt_str;
|
||||
MP_STATE_PORT(fs_user_mount)->len = mnt_len;
|
||||
fs_user_mount_t *vfs;
|
||||
MP_STATE_PORT(fs_user_mount)[i] = vfs = m_new_obj(fs_user_mount_t);
|
||||
vfs->str = mnt_str;
|
||||
vfs->len = mnt_len;
|
||||
vfs->flags = FSUSER_FREE_OBJ;
|
||||
|
||||
// load block protocol methods
|
||||
mp_load_method(device, MP_QSTR_readblocks, MP_STATE_PORT(fs_user_mount)->readblocks);
|
||||
mp_load_method_maybe(device, MP_QSTR_writeblocks, MP_STATE_PORT(fs_user_mount)->writeblocks);
|
||||
mp_load_method_maybe(device, MP_QSTR_sync, MP_STATE_PORT(fs_user_mount)->sync);
|
||||
mp_load_method(device, MP_QSTR_count, MP_STATE_PORT(fs_user_mount)->count);
|
||||
mp_load_method(device, MP_QSTR_readblocks, vfs->readblocks);
|
||||
mp_load_method_maybe(device, MP_QSTR_writeblocks, vfs->writeblocks);
|
||||
mp_load_method_maybe(device, MP_QSTR_ioctl, vfs->u.ioctl);
|
||||
if (vfs->u.ioctl[0] != MP_OBJ_NULL) {
|
||||
// device supports new block protocol, so indicate it
|
||||
vfs->flags |= FSUSER_HAVE_IOCTL;
|
||||
} else {
|
||||
// no ioctl method, so assume the device uses the old block protocol
|
||||
mp_load_method_maybe(device, MP_QSTR_sync, vfs->u.old.sync);
|
||||
mp_load_method(device, MP_QSTR_count, vfs->u.old.count);
|
||||
}
|
||||
|
||||
// Read-only device indicated by writeblocks[0] == MP_OBJ_NULL.
|
||||
// User can specify read-only device by:
|
||||
// 1. readonly=True keyword argument
|
||||
// 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already)
|
||||
if (args[0].u_bool) {
|
||||
MP_STATE_PORT(fs_user_mount)->writeblocks[0] = MP_OBJ_NULL;
|
||||
vfs->writeblocks[0] = MP_OBJ_NULL;
|
||||
}
|
||||
|
||||
// mount the block device
|
||||
FRESULT res = f_mount(&MP_STATE_PORT(fs_user_mount)->fatfs, MP_STATE_PORT(fs_user_mount)->str, 1);
|
||||
|
||||
// mount the block device (if mkfs, only pre-mount)
|
||||
FRESULT res = f_mount(&vfs->fatfs, vfs->str, !mkfs);
|
||||
// check the result
|
||||
if (res == FR_OK) {
|
||||
if (mkfs) {
|
||||
goto mkfs;
|
||||
}
|
||||
} else if (res == FR_NO_FILESYSTEM && args[1].u_bool) {
|
||||
res = f_mkfs(MP_STATE_PORT(fs_user_mount)->str, 1, 0);
|
||||
mkfs:
|
||||
res = f_mkfs(vfs->str, 1, 0);
|
||||
if (res != FR_OK) {
|
||||
mkfs_error:
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mkfs"));
|
||||
}
|
||||
if (mkfs) {
|
||||
// If requested to only mkfs, unmount pre-mounted device
|
||||
res = f_mount(NULL, vfs->str, 0);
|
||||
if (res != FR_OK) {
|
||||
goto mkfs_error;
|
||||
}
|
||||
MP_STATE_PORT(fs_user_mount)[i] = NULL;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mount"));
|
||||
}
|
||||
|
||||
/*
|
||||
if (MP_STATE_PORT(fs_user_mount)->writeblocks[0] == MP_OBJ_NULL) {
|
||||
if (vfs->writeblocks[0] == MP_OBJ_NULL) {
|
||||
printf("mounted read-only");
|
||||
} else {
|
||||
printf("mounted read-write");
|
||||
}
|
||||
DWORD nclst;
|
||||
FATFS *fatfs;
|
||||
f_getfree(MP_STATE_PORT(fs_user_mount)->str, &nclst, &fatfs);
|
||||
printf(" on %s with %u bytes free\n", MP_STATE_PORT(fs_user_mount)->str, (uint)(nclst * fatfs->csize * 512));
|
||||
f_getfree(vfs->str, &nclst, &fatfs);
|
||||
printf(" on %s with %u bytes free\n", vfs->str, (uint)(nclst * fatfs->csize * 512));
|
||||
*/
|
||||
return vfs;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t fatfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
fatfs_mount_mkfs(n_args, pos_args, kw_args, false);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(fsuser_mount_obj, 2, fatfs_mount);
|
||||
|
||||
STATIC mp_obj_t fatfs_umount(mp_obj_t bdev_or_path_in) {
|
||||
size_t i = 0;
|
||||
if (MP_OBJ_IS_STR(bdev_or_path_in)) {
|
||||
mp_uint_t mnt_len;
|
||||
const char *mnt_str = mp_obj_str_get_data(bdev_or_path_in, &mnt_len);
|
||||
for (; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
|
||||
fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
|
||||
if (vfs != NULL && !memcmp(mnt_str, vfs->str, mnt_len + 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
|
||||
fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
|
||||
if (vfs != NULL && bdev_or_path_in == vfs->readblocks[1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount))) {
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EINVAL)));
|
||||
}
|
||||
|
||||
fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
|
||||
FRESULT res = f_mount(NULL, vfs->str, 0);
|
||||
if (vfs->flags & FSUSER_FREE_OBJ) {
|
||||
m_del_obj(fs_user_mount_t, vfs);
|
||||
}
|
||||
MP_STATE_PORT(fs_user_mount)[i] = NULL;
|
||||
if (res != FR_OK) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't umount"));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_mount_obj, 2, pyb_mount);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(fsuser_umount_obj, fatfs_umount);
|
||||
|
||||
STATIC mp_obj_t fatfs_mkfs(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
fatfs_mount_mkfs(n_args, pos_args, kw_args, true);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(fsuser_mkfs_obj, 2, fatfs_mkfs);
|
||||
|
||||
#endif // MICROPY_FSUSERMOUNT
|
||||
|
||||
@@ -24,14 +24,38 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// these are the values for fs_user_mount_t.flags
|
||||
#define FSUSER_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func
|
||||
#define FSUSER_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount
|
||||
#define FSUSER_HAVE_IOCTL (0x0004) // new protocol with ioctl
|
||||
|
||||
// constants for block protocol ioctl
|
||||
#define BP_IOCTL_INIT (1)
|
||||
#define BP_IOCTL_DEINIT (2)
|
||||
#define BP_IOCTL_SYNC (3)
|
||||
#define BP_IOCTL_SEC_COUNT (4)
|
||||
#define BP_IOCTL_SEC_SIZE (5)
|
||||
|
||||
typedef struct _fs_user_mount_t {
|
||||
mp_obj_base_t base;
|
||||
const char *str;
|
||||
mp_uint_t len;
|
||||
uint16_t len; // length of str
|
||||
uint16_t flags;
|
||||
mp_obj_t readblocks[4];
|
||||
mp_obj_t writeblocks[4];
|
||||
mp_obj_t sync[2];
|
||||
mp_obj_t count[2];
|
||||
// new protocol uses just ioctl, old uses sync (optional) and count
|
||||
union {
|
||||
mp_obj_t ioctl[4];
|
||||
struct {
|
||||
mp_obj_t sync[2];
|
||||
mp_obj_t count[2];
|
||||
} old;
|
||||
} u;
|
||||
FATFS fatfs;
|
||||
} fs_user_mount_t;
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ(pyb_mount_obj);
|
||||
fs_user_mount_t *fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, bool mkfs);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ(fsuser_mount_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ(fsuser_umount_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ(fsuser_mkfs_obj);
|
||||
|
||||
@@ -79,7 +79,7 @@ STATIC mp_obj_t machine_mem_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t va
|
||||
} else {
|
||||
// store
|
||||
uintptr_t addr = MICROPY_MACHINE_MEM_GET_WRITE_ADDR(index, self->elem_size);
|
||||
uint32_t val = mp_obj_get_int(value);
|
||||
uint32_t val = mp_obj_get_int_truncated(value);
|
||||
switch (self->elem_size) {
|
||||
case 1: (*(uint8_t*)addr) = val; break;
|
||||
case 2: (*(uint16_t*)addr) = val; break;
|
||||
|
||||
304
extmod/modlwip.c
304
extmod/modlwip.c
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "py/nlr.h"
|
||||
#include "py/objlist.h"
|
||||
@@ -43,6 +44,14 @@
|
||||
//#include "lwip/raw.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
// For compatibilily with older lwIP versions.
|
||||
#ifndef ip_set_option
|
||||
#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt))
|
||||
#endif
|
||||
#ifndef ip_reset_option
|
||||
#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt))
|
||||
#endif
|
||||
|
||||
#ifdef MICROPY_PY_LWIP_SLIP
|
||||
#include "netif/slipif.h"
|
||||
#include "lwip/sio.h"
|
||||
@@ -152,6 +161,9 @@ STATIC const mp_obj_type_t lwip_slip_type = {
|
||||
|
||||
// Extension to lwIP error codes
|
||||
#define _ERR_BADF -16
|
||||
// TODO: We just know that change happened somewhere between 1.4.0 and 1.4.1,
|
||||
// investigate in more detail.
|
||||
#if LWIP_VERSION < 0x01040100
|
||||
static const int error_lookup_table[] = {
|
||||
0, /* ERR_OK 0 No error, everything OK. */
|
||||
ENOMEM, /* ERR_MEM -1 Out of memory error. */
|
||||
@@ -161,6 +173,28 @@ static const int error_lookup_table[] = {
|
||||
EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
|
||||
EINVAL, /* ERR_VAL -6 Illegal value. */
|
||||
EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
|
||||
|
||||
ECONNABORTED, /* ERR_ABRT -8 Connection aborted. */
|
||||
ECONNRESET, /* ERR_RST -9 Connection reset. */
|
||||
ENOTCONN, /* ERR_CLSD -10 Connection closed. */
|
||||
ENOTCONN, /* ERR_CONN -11 Not connected. */
|
||||
EIO, /* ERR_ARG -12 Illegal argument. */
|
||||
EADDRINUSE, /* ERR_USE -13 Address in use. */
|
||||
-1, /* ERR_IF -14 Low-level netif error */
|
||||
EALREADY, /* ERR_ISCONN -15 Already connected. */
|
||||
EBADF, /* _ERR_BADF -16 Closed socket (null pcb) */
|
||||
};
|
||||
#else
|
||||
static const int error_lookup_table[] = {
|
||||
0, /* ERR_OK 0 No error, everything OK. */
|
||||
ENOMEM, /* ERR_MEM -1 Out of memory error. */
|
||||
ENOBUFS, /* ERR_BUF -2 Buffer error. */
|
||||
EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
|
||||
EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
|
||||
EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
|
||||
EINVAL, /* ERR_VAL -6 Illegal value. */
|
||||
EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
|
||||
|
||||
EADDRINUSE, /* ERR_USE -8 Address in use. */
|
||||
EALREADY, /* ERR_ISCONN -9 Already connected. */
|
||||
ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */
|
||||
@@ -171,6 +205,7 @@ static const int error_lookup_table[] = {
|
||||
-1, /* ERR_IF -15 Low-level netif error */
|
||||
EBADF, /* _ERR_BADF -16 Closed socket (null pcb) */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*******************************************************************************/
|
||||
// The socket object provided by lwip.socket.
|
||||
@@ -210,8 +245,11 @@ typedef struct _lwip_socket_obj_t {
|
||||
} lwip_socket_obj_t;
|
||||
|
||||
static inline void poll_sockets(void) {
|
||||
// TODO: Allow to override by ports
|
||||
#ifdef MICROPY_EVENT_POLL_HOOK
|
||||
MICROPY_EVENT_POLL_HOOK;
|
||||
#else
|
||||
mp_hal_delay_ms(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
@@ -250,9 +288,19 @@ STATIC err_t _lwip_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
// By default, a child socket of listen socket is created with recv
|
||||
// handler which discards incoming pbuf's. We don't want to do that,
|
||||
// so set this handler which requests lwIP to keep pbuf's and deliver
|
||||
// them later. We cannot cache pbufs in child socket on Python side,
|
||||
// until it is created in accept().
|
||||
STATIC err_t _lwip_tcp_recv_unaccepted(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
// Callback for incoming tcp connections.
|
||||
STATIC err_t _lwip_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
|
||||
lwip_socket_obj_t *socket = (lwip_socket_obj_t*)arg;
|
||||
tcp_recv(newpcb, _lwip_tcp_recv_unaccepted);
|
||||
|
||||
if (socket->incoming.connection != NULL) {
|
||||
// We need to handle this better. This single-level structure makes the
|
||||
@@ -280,19 +328,6 @@ STATIC err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf *p, err
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
STATIC uint8_t lwip_dns_returned;
|
||||
STATIC uint8_t lwip_dns_result[4];
|
||||
|
||||
// Callback for incoming DNS requests. Just set our results.
|
||||
STATIC void _lwip_dns_incoming(const char *name, ip_addr_t *addr, void *callback_arg) {
|
||||
if (addr != NULL) {
|
||||
lwip_dns_returned = 1;
|
||||
memcpy(lwip_dns_result, addr, sizeof(lwip_dns_result));
|
||||
} else {
|
||||
lwip_dns_returned = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
// Functions for socket send/recieve operations. Socket send/recv and friends call
|
||||
// these to do the work.
|
||||
@@ -347,7 +382,7 @@ STATIC mp_uint_t lwip_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
|
||||
}
|
||||
} else {
|
||||
while (socket->incoming.pbuf == NULL) {
|
||||
mp_hal_delay_ms(100);
|
||||
poll_sockets();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,6 +404,35 @@ STATIC mp_uint_t lwip_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
|
||||
// Helper function for send/sendto to handle TCP packets
|
||||
STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) {
|
||||
u16_t available = tcp_sndbuf(socket->pcb.tcp);
|
||||
|
||||
if (available == 0) {
|
||||
// Non-blocking socket
|
||||
if (socket->timeout == 0) {
|
||||
*_errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
// Assume that STATE_PEER_CLOSED may mean half-closed connection, where peer closed it
|
||||
// sending direction, but not receiving. Consequently, check for both STATE_CONNECTED
|
||||
// and STATE_PEER_CLOSED as normal conditions and still waiting for buffers to be sent.
|
||||
// If peer fully closed socket, we would have socket->state set to ERR_RST (connection
|
||||
// reset) by error callback.
|
||||
// Avoid sending too small packets, so wait until at least 16 bytes available
|
||||
while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) {
|
||||
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
|
||||
*_errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
poll_sockets();
|
||||
}
|
||||
|
||||
if (socket->state < 0) {
|
||||
*_errno = error_lookup_table[-socket->state];
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
u16_t write_len = MIN(available, len);
|
||||
|
||||
err_t err = tcp_write(socket->pcb.tcp, buf, write_len, TCP_WRITE_FLAG_COPY);
|
||||
@@ -383,20 +447,32 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui
|
||||
|
||||
// Helper function for recv/recvfrom to handle TCP packets
|
||||
STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) {
|
||||
|
||||
if (socket->state == STATE_PEER_CLOSED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (socket->incoming.pbuf == NULL) {
|
||||
|
||||
// Non-blocking socket
|
||||
if (socket->timeout == 0) {
|
||||
*_errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mp_uint_t start = mp_hal_ticks_ms();
|
||||
while (socket->incoming.pbuf == NULL) {
|
||||
while (socket->state == STATE_CONNECTED && socket->incoming.pbuf == NULL) {
|
||||
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
|
||||
*_errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
poll_sockets();
|
||||
}
|
||||
if (socket->state == STATE_PEER_CLOSED) {
|
||||
if (socket->incoming.pbuf == NULL) {
|
||||
// socket closed and no data left in buffer
|
||||
return 0;
|
||||
}
|
||||
} else if (socket->state != STATE_CONNECTED) {
|
||||
assert(socket->state < 0);
|
||||
*_errno = error_lookup_table[-socket->state];
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct pbuf *p = socket->incoming.pbuf;
|
||||
@@ -424,8 +500,14 @@ STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
|
||||
|
||||
STATIC const mp_obj_type_t lwip_socket_type;
|
||||
|
||||
STATIC void lwip_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
lwip_socket_obj_t *self = self_in;
|
||||
mp_printf(print, "<socket state=%d timeout=%d incoming=%p remaining=%d>", self->state, self->timeout,
|
||||
self->incoming.pbuf, self->leftover_count);
|
||||
}
|
||||
|
||||
// FIXME: Only supports two arguments at present
|
||||
STATIC mp_obj_t lwip_socket_make_new(mp_obj_t type_in, mp_uint_t n_args,
|
||||
STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, 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);
|
||||
|
||||
@@ -587,7 +669,7 @@ STATIC mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
|
||||
}
|
||||
} else {
|
||||
while (socket->incoming.connection == NULL) {
|
||||
mp_hal_delay_ms(100);
|
||||
poll_sockets();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,7 +752,7 @@ STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
|
||||
}
|
||||
} else {
|
||||
while (socket->state == STATE_CONNECTING) {
|
||||
mp_hal_delay_ms(100);
|
||||
poll_sockets();
|
||||
}
|
||||
}
|
||||
if (socket->state == STATE_CONNECTED) {
|
||||
@@ -694,16 +776,20 @@ STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_connect_obj, lwip_socket_connect);
|
||||
|
||||
STATIC void lwip_socket_check_connected(lwip_socket_obj_t *socket) {
|
||||
if (socket->pcb.tcp == NULL) {
|
||||
// not connected
|
||||
int _errno = error_lookup_table[-socket->state];
|
||||
socket->state = _ERR_BADF;
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno)));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t lwip_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
|
||||
lwip_socket_obj_t *socket = self_in;
|
||||
int _errno;
|
||||
|
||||
if (socket->pcb.tcp == NULL) {
|
||||
// not connected
|
||||
_errno = error_lookup_table[-(socket->state)];
|
||||
socket->state = _ERR_BADF;
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno)));
|
||||
}
|
||||
lwip_socket_check_connected(socket);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
|
||||
@@ -731,12 +817,7 @@ STATIC mp_obj_t lwip_socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
|
||||
lwip_socket_obj_t *socket = self_in;
|
||||
int _errno;
|
||||
|
||||
if (socket->pcb.tcp == NULL) {
|
||||
// not connected
|
||||
_errno = error_lookup_table[-(socket->state)];
|
||||
socket->state = _ERR_BADF;
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno)));
|
||||
}
|
||||
lwip_socket_check_connected(socket);
|
||||
|
||||
mp_int_t len = mp_obj_get_int(len_in);
|
||||
vstr_t vstr;
|
||||
@@ -769,12 +850,7 @@ STATIC mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t
|
||||
lwip_socket_obj_t *socket = self_in;
|
||||
int _errno;
|
||||
|
||||
if (socket->pcb.tcp == NULL) {
|
||||
// not connected
|
||||
_errno = error_lookup_table[-(socket->state)];
|
||||
socket->state = _ERR_BADF;
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno)));
|
||||
}
|
||||
lwip_socket_check_connected(socket);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ);
|
||||
@@ -805,12 +881,7 @@ STATIC mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
|
||||
lwip_socket_obj_t *socket = self_in;
|
||||
int _errno;
|
||||
|
||||
if (socket->pcb.tcp == NULL) {
|
||||
// not connected
|
||||
_errno = error_lookup_table[-(socket->state)];
|
||||
socket->state = _ERR_BADF;
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno)));
|
||||
}
|
||||
lwip_socket_check_connected(socket);
|
||||
|
||||
mp_int_t len = mp_obj_get_int(len_in);
|
||||
vstr_t vstr;
|
||||
@@ -864,6 +935,70 @@ STATIC mp_obj_t lwip_socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_settimeout_obj, lwip_socket_settimeout);
|
||||
|
||||
STATIC mp_obj_t lwip_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) {
|
||||
lwip_socket_obj_t *socket = self_in;
|
||||
bool val = mp_obj_is_true(flag_in);
|
||||
if (val) {
|
||||
socket->timeout = -1;
|
||||
} else {
|
||||
socket->timeout = 0;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_setblocking_obj, lwip_socket_setblocking);
|
||||
|
||||
STATIC mp_obj_t lwip_socket_setsockopt(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
(void)n_args; // always 4
|
||||
lwip_socket_obj_t *socket = args[0];
|
||||
mp_int_t val = mp_obj_get_int(args[3]);
|
||||
switch (mp_obj_get_int(args[2])) {
|
||||
case SOF_REUSEADDR:
|
||||
// Options are common for UDP and TCP pcb's.
|
||||
if (val) {
|
||||
ip_set_option(socket->pcb.tcp, SOF_REUSEADDR);
|
||||
} else {
|
||||
ip_reset_option(socket->pcb.tcp, SOF_REUSEADDR);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Warning: lwip.setsockopt() not implemented\n");
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_setsockopt_obj, 4, 4, lwip_socket_setsockopt);
|
||||
|
||||
STATIC mp_obj_t lwip_socket_makefile(mp_uint_t n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
return args[0];
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_makefile_obj, 1, 3, lwip_socket_makefile);
|
||||
|
||||
STATIC mp_uint_t lwip_socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
lwip_socket_obj_t *socket = self_in;
|
||||
|
||||
switch (socket->type) {
|
||||
case MOD_NETWORK_SOCK_STREAM:
|
||||
return lwip_tcp_receive(socket, buf, size, errcode);
|
||||
case MOD_NETWORK_SOCK_DGRAM:
|
||||
return lwip_udp_receive(socket, buf, size, NULL, NULL, errcode);
|
||||
}
|
||||
// Unreachable
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t lwip_socket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
lwip_socket_obj_t *socket = self_in;
|
||||
|
||||
switch (socket->type) {
|
||||
case MOD_NETWORK_SOCK_STREAM:
|
||||
return lwip_tcp_send(socket, buf, size, errcode);
|
||||
case MOD_NETWORK_SOCK_DGRAM:
|
||||
return lwip_udp_send(socket, buf, size, NULL, 0, errcode);
|
||||
}
|
||||
// Unreachable
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
|
||||
STATIC const mp_map_elem_t lwip_socket_locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&lwip_socket_close_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&lwip_socket_close_obj },
|
||||
@@ -876,13 +1011,27 @@ STATIC const mp_map_elem_t lwip_socket_locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&lwip_socket_sendto_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&lwip_socket_recvfrom_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_settimeout), (mp_obj_t)&lwip_socket_settimeout_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_setblocking), (mp_obj_t)&lwip_socket_setblocking_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&lwip_socket_setsockopt_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_makefile), (mp_obj_t)&lwip_socket_makefile_obj },
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_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 },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(lwip_socket_locals_dict, lwip_socket_locals_dict_table);
|
||||
|
||||
STATIC const mp_stream_p_t lwip_socket_stream_p = {
|
||||
.read = lwip_socket_read,
|
||||
.write = lwip_socket_write,
|
||||
};
|
||||
|
||||
STATIC const mp_obj_type_t lwip_socket_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_socket,
|
||||
.print = lwip_socket_print,
|
||||
.make_new = lwip_socket_make_new,
|
||||
.stream_p = &lwip_socket_stream_p,
|
||||
.locals_dict = (mp_obj_t)&lwip_socket_locals_dict,
|
||||
};
|
||||
|
||||
@@ -937,41 +1086,59 @@ STATIC mp_obj_t mod_lwip_callback() {
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(mod_lwip_callback_obj, mod_lwip_callback);
|
||||
|
||||
typedef struct _getaddrinfo_state_t {
|
||||
volatile int status;
|
||||
volatile ip_addr_t ipaddr;
|
||||
} getaddrinfo_state_t;
|
||||
|
||||
// Callback for incoming DNS requests.
|
||||
STATIC void lwip_getaddrinfo_cb(const char *name, ip_addr_t *ipaddr, void *arg) {
|
||||
getaddrinfo_state_t *state = arg;
|
||||
if (ipaddr != NULL) {
|
||||
state->status = 1;
|
||||
state->ipaddr = *ipaddr;
|
||||
} else {
|
||||
// error
|
||||
state->status = -2;
|
||||
}
|
||||
}
|
||||
|
||||
// lwip.getaddrinfo
|
||||
STATIC mp_obj_t lwip_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);
|
||||
|
||||
ip_addr_t result;
|
||||
lwip_dns_returned = 0;
|
||||
getaddrinfo_state_t state;
|
||||
state.status = 0;
|
||||
|
||||
switch (dns_gethostbyname(host, &result, _lwip_dns_incoming, NULL)) {
|
||||
case ERR_OK: {
|
||||
break;
|
||||
}
|
||||
case ERR_INPROGRESS: {
|
||||
while(!lwip_dns_returned) {
|
||||
mp_hal_delay_ms(100);
|
||||
}
|
||||
if (lwip_dns_returned == 2) {
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOENT)));
|
||||
err_t ret = dns_gethostbyname(host, (ip_addr_t*)&state.ipaddr, lwip_getaddrinfo_cb, &state);
|
||||
switch (ret) {
|
||||
case ERR_OK:
|
||||
// cached
|
||||
state.status = 1;
|
||||
break;
|
||||
case ERR_INPROGRESS:
|
||||
while (state.status == 0) {
|
||||
poll_sockets();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOENT)));
|
||||
}
|
||||
default:
|
||||
state.status = ret;
|
||||
}
|
||||
|
||||
if (state.status < 0) {
|
||||
// TODO: CPython raises gaierror, we raise with native lwIP negative error
|
||||
// values, to differentiate from normal errno's at least in such way.
|
||||
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(state.status)));
|
||||
}
|
||||
|
||||
uint8_t out_ip[NETUTILS_IPV4ADDR_BUFSIZE];
|
||||
memcpy(out_ip, lwip_dns_result, sizeof(lwip_dns_result));
|
||||
mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL);
|
||||
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET);
|
||||
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM);
|
||||
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0);
|
||||
tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_);
|
||||
tuple->items[4] = netutils_format_inet_addr(out_ip, port, NETUTILS_BIG);
|
||||
tuple->items[4] = netutils_format_inet_addr((uint8_t*)&state.ipaddr, port, NETUTILS_BIG);
|
||||
return mp_obj_new_list(1, (mp_obj_t*)&tuple);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_getaddrinfo_obj, lwip_getaddrinfo);
|
||||
@@ -995,6 +1162,9 @@ STATIC const mp_map_elem_t mp_module_lwip_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_STREAM), MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_DGRAM), MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_DGRAM) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_RAW), MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_RAW) },
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOL_SOCKET), MP_OBJ_NEW_SMALL_INT(1) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_SO_REUSEADDR), MP_OBJ_NEW_SMALL_INT(SOF_REUSEADDR) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_lwip_globals, mp_module_lwip_globals_table);
|
||||
|
||||
@@ -281,18 +281,15 @@ STATIC mp_obj_t uctypes_struct_sizeof(mp_obj_t obj_in) {
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_sizeof_obj, uctypes_struct_sizeof);
|
||||
|
||||
STATIC inline mp_obj_t get_unaligned(uint val_type, void *p, int big_endian) {
|
||||
mp_int_t val = mp_binary_get_int(GET_SCALAR_SIZE(val_type), val_type & 1, big_endian, p);
|
||||
if (val_type == UINT32) {
|
||||
return mp_obj_new_int_from_uint(val);
|
||||
} else {
|
||||
return mp_obj_new_int(val);
|
||||
}
|
||||
STATIC inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
|
||||
char struct_type = big_endian ? '>' : '<';
|
||||
static const char type2char[16] = "BbHhIiQq------fd";
|
||||
return mp_binary_get_val(struct_type, type2char[val_type], &p);
|
||||
}
|
||||
|
||||
STATIC inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) {
|
||||
char struct_type = big_endian ? '>' : '<';
|
||||
static const char type2char[8] = "BbHhIiQq";
|
||||
static const char type2char[16] = "BbHhIiQq------fd";
|
||||
mp_binary_set_val(struct_type, type2char[val_type], val, &p);
|
||||
}
|
||||
|
||||
@@ -352,6 +349,17 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
|
||||
}
|
||||
|
||||
STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
if (val_type == FLOAT32 || val_type == FLOAT64) {
|
||||
mp_float_t v = mp_obj_get_float(val);
|
||||
if (val_type == FLOAT32) {
|
||||
((float*)p)[index] = v;
|
||||
} else {
|
||||
((double*)p)[index] = v;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
mp_int_t v = mp_obj_get_int(val);
|
||||
switch (val_type) {
|
||||
case UINT8:
|
||||
@@ -395,7 +403,7 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set
|
||||
offset &= VALUE_MASK(VAL_TYPE_BITS);
|
||||
//printf("scalar type=%d offset=%x\n", val_type, offset);
|
||||
|
||||
if (val_type <= INT64) {
|
||||
if (val_type <= INT64 || val_type == FLOAT32 || val_type == FLOAT64) {
|
||||
// printf("size=%d\n", GET_SCALAR_SIZE(val_type));
|
||||
if (self->flags == LAYOUT_NATIVE) {
|
||||
if (set_val == MP_OBJ_NULL) {
|
||||
@@ -689,6 +697,11 @@ STATIC const mp_rom_map_elem_t mp_module_uctypes_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_BF_POS), MP_ROM_INT(17) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_BF_LEN), MP_ROM_INT(22) },
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
{ MP_ROM_QSTR(MP_QSTR_FLOAT32), MP_ROM_INT(TYPE2SMALLINT(FLOAT32, 4)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_FLOAT64), MP_ROM_INT(TYPE2SMALLINT(FLOAT64, 4)) },
|
||||
#endif
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_PTR), MP_ROM_INT(TYPE2SMALLINT(PTR, AGG_TYPE_BITS)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ARRAY), MP_ROM_INT(TYPE2SMALLINT(ARRAY, AGG_TYPE_BITS)) },
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ STATIC void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t
|
||||
mp_printf(print, "<re %p>", self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
|
||||
STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
Subject subj;
|
||||
@@ -116,12 +116,12 @@ STATIC mp_obj_t re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
|
||||
}
|
||||
|
||||
STATIC mp_obj_t re_match(size_t n_args, const mp_obj_t *args) {
|
||||
return re_exec(true, n_args, args);
|
||||
return ure_exec(true, n_args, args);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_match_obj, 2, 4, re_match);
|
||||
|
||||
STATIC mp_obj_t re_search(size_t n_args, const mp_obj_t *args) {
|
||||
return re_exec(false, n_args, args);
|
||||
return ure_exec(false, n_args, args);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_search_obj, 2, 4, re_search);
|
||||
|
||||
@@ -211,7 +211,7 @@ STATIC mp_obj_t mod_re_exec(bool is_anchored, uint n_args, const mp_obj_t *args)
|
||||
mp_obj_t self = mod_re_compile(1, args);
|
||||
|
||||
const mp_obj_t args2[] = {self, args[1]};
|
||||
mp_obj_t match = re_exec(is_anchored, 2, args2);
|
||||
mp_obj_t match = ure_exec(is_anchored, 2, args2);
|
||||
return match;
|
||||
}
|
||||
|
||||
|
||||
266
extmod/modwebsocket.c
Normal file
266
extmod/modwebsocket.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Paul Sokolovsky
|
||||
*
|
||||
* 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/nlr.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "extmod/modwebsocket.h"
|
||||
|
||||
#if MICROPY_PY_WEBSOCKET
|
||||
|
||||
enum { FRAME_HEADER, FRAME_OPT, PAYLOAD };
|
||||
|
||||
enum { BLOCKING_WRITE = 0x80 };
|
||||
|
||||
typedef struct _mp_obj_websocket_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t sock;
|
||||
uint32_t msg_sz;
|
||||
byte mask[4];
|
||||
byte state;
|
||||
byte to_recv;
|
||||
byte mask_pos;
|
||||
byte buf_pos;
|
||||
byte buf[6];
|
||||
byte opts;
|
||||
// Copy of current frame's flags
|
||||
byte ws_flags;
|
||||
} mp_obj_websocket_t;
|
||||
|
||||
STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 1, 2, false);
|
||||
mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t);
|
||||
o->base.type = type;
|
||||
o->sock = args[0];
|
||||
o->state = FRAME_HEADER;
|
||||
o->to_recv = 2;
|
||||
o->mask_pos = 0;
|
||||
o->buf_pos = 0;
|
||||
o->opts = FRAME_TXT;
|
||||
if (n_args > 1 && args[1] == mp_const_true) {
|
||||
o->opts |= BLOCKING_WRITE;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_websocket_t *self = self_in;
|
||||
const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock, MP_STREAM_OP_READ);
|
||||
while (1) {
|
||||
if (self->to_recv != 0) {
|
||||
mp_uint_t out_sz = stream_p->read(self->sock, self->buf + self->buf_pos, self->to_recv, errcode);
|
||||
if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
|
||||
return out_sz;
|
||||
}
|
||||
self->buf_pos += out_sz;
|
||||
self->to_recv -= out_sz;
|
||||
if (self->to_recv != 0) {
|
||||
*errcode = EAGAIN;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (self->state) {
|
||||
case FRAME_HEADER: {
|
||||
// TODO: Split frame handling below is untested so far, so conservatively disable it
|
||||
assert(self->buf[0] & 0x80);
|
||||
|
||||
// "Control frames MAY be injected in the middle of a fragmented message."
|
||||
// So, they must be processed before data frames (and not alter
|
||||
// self->ws_flags)
|
||||
if ((self->buf[0] & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
|
||||
// TODO: implement
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) {
|
||||
// Preserve previous frame type
|
||||
self->ws_flags = (self->ws_flags & FRAME_OPCODE_MASK) | (self->buf[0] & ~FRAME_OPCODE_MASK);
|
||||
} else {
|
||||
self->ws_flags = self->buf[0];
|
||||
}
|
||||
|
||||
// Reset mask in case someone will use "simplified" protocol
|
||||
// without masks.
|
||||
memset(self->mask, 0, sizeof(self->mask));
|
||||
|
||||
int to_recv = 0;
|
||||
size_t sz = self->buf[1] & 0x7f;
|
||||
if (sz == 126) {
|
||||
// Msg size is next 2 bytes
|
||||
to_recv += 2;
|
||||
} else if (sz == 127) {
|
||||
// Msg size is next 2 bytes
|
||||
assert(0);
|
||||
}
|
||||
if (self->buf[1] & 0x80) {
|
||||
// Next 4 bytes is mask
|
||||
to_recv += 4;
|
||||
}
|
||||
|
||||
self->buf_pos = 0;
|
||||
self->to_recv = to_recv;
|
||||
self->msg_sz = sz; // May be overriden by FRAME_OPT
|
||||
if (to_recv != 0) {
|
||||
self->state = FRAME_OPT;
|
||||
} else {
|
||||
self->state = PAYLOAD;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case FRAME_OPT: {
|
||||
if ((self->buf_pos & 3) == 2) {
|
||||
// First two bytes are message length
|
||||
self->msg_sz = (self->buf[0] << 8) | self->buf[1];
|
||||
}
|
||||
if (self->buf_pos >= 4) {
|
||||
// Last 4 bytes is mask
|
||||
memcpy(self->mask, self->buf + self->buf_pos - 4, 4);
|
||||
}
|
||||
self->buf_pos = 0;
|
||||
self->state = PAYLOAD;
|
||||
continue;
|
||||
}
|
||||
|
||||
case PAYLOAD: {
|
||||
size_t sz = MIN(size, self->msg_sz);
|
||||
mp_uint_t out_sz = stream_p->read(self->sock, buf, sz, errcode);
|
||||
if (out_sz == MP_STREAM_ERROR) {
|
||||
return out_sz;
|
||||
}
|
||||
|
||||
sz = out_sz;
|
||||
for (byte *p = buf; sz--; p++) {
|
||||
*p ^= self->mask[self->mask_pos++ & 3];
|
||||
}
|
||||
|
||||
self->msg_sz -= out_sz;
|
||||
if (self->msg_sz == 0) {
|
||||
self->state = FRAME_HEADER;
|
||||
self->to_recv = 2;
|
||||
self->mask_pos = 0;
|
||||
self->buf_pos = 0;
|
||||
}
|
||||
return out_sz;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_websocket_t *self = self_in;
|
||||
assert(size < 0x10000);
|
||||
byte header[4] = {0x80 | (self->opts & FRAME_OPCODE_MASK)};
|
||||
int hdr_sz;
|
||||
if (size < 126) {
|
||||
header[1] = size;
|
||||
hdr_sz = 2;
|
||||
} else {
|
||||
header[1] = 126;
|
||||
header[2] = size >> 8;
|
||||
header[3] = size & 0xff;
|
||||
hdr_sz = 4;
|
||||
}
|
||||
|
||||
mp_obj_t dest[3];
|
||||
if (self->opts & BLOCKING_WRITE) {
|
||||
mp_load_method(self->sock, MP_QSTR_setblocking, dest);
|
||||
dest[2] = mp_const_true;
|
||||
mp_call_method_n_kw(1, 0, dest);
|
||||
}
|
||||
|
||||
mp_uint_t out_sz = mp_stream_writeall(self->sock, header, hdr_sz, errcode);
|
||||
if (out_sz != MP_STREAM_ERROR) {
|
||||
out_sz = mp_stream_writeall(self->sock, buf, size, errcode);
|
||||
}
|
||||
|
||||
if (self->opts & BLOCKING_WRITE) {
|
||||
dest[2] = mp_const_false;
|
||||
mp_call_method_n_kw(1, 0, dest);
|
||||
}
|
||||
|
||||
return out_sz;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
switch (request) {
|
||||
case MP_STREAM_GET_DATA_OPTS:
|
||||
return self->ws_flags & FRAME_OPCODE_MASK;
|
||||
case MP_STREAM_SET_DATA_OPTS: {
|
||||
int cur = self->opts & FRAME_OPCODE_MASK;
|
||||
self->opts = (self->opts & ~FRAME_OPCODE_MASK) | (arg & FRAME_OPCODE_MASK);
|
||||
return cur;
|
||||
}
|
||||
default:
|
||||
*errcode = EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC const mp_map_elem_t websocket_locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ioctl), (mp_obj_t)&mp_stream_ioctl_obj },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table);
|
||||
|
||||
STATIC const mp_stream_p_t websocket_stream_p = {
|
||||
.read = websocket_read,
|
||||
.write = websocket_write,
|
||||
.ioctl = websocket_ioctl,
|
||||
};
|
||||
|
||||
STATIC const mp_obj_type_t websocket_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_websocket,
|
||||
.make_new = websocket_make_new,
|
||||
.stream_p = &websocket_stream_p,
|
||||
.locals_dict = (mp_obj_t)&websocket_locals_dict,
|
||||
};
|
||||
|
||||
STATIC const mp_map_elem_t websocket_module_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_websocket) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_websocket), (mp_obj_t)&websocket_type },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(websocket_module_globals, websocket_module_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_websocket = {
|
||||
.base = { &mp_type_module },
|
||||
.name = MP_QSTR_websocket,
|
||||
.globals = (mp_obj_dict_t*)&websocket_module_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_WEBSOCKET
|
||||
5
extmod/modwebsocket.h
Normal file
5
extmod/modwebsocket.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#define FRAME_OPCODE_MASK 0x0f
|
||||
enum {
|
||||
FRAME_CONT, FRAME_TXT, FRAME_BIN,
|
||||
FRAME_CLOSE = 0x8, FRAME_PING, FRAME_PONG
|
||||
};
|
||||
@@ -458,6 +458,7 @@ int tinf_uncompress(void *dest, unsigned int *destLen,
|
||||
|
||||
d.destStart = (unsigned char *)dest;
|
||||
d.destRemaining = *destLen;
|
||||
d.destSize = *destLen;
|
||||
|
||||
res = tinf_uncompress_dyn(&d);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user