diff --git a/boards.txt b/boards.txt index 9c4af2f52..d32a47e85 100644 --- a/boards.txt +++ b/boards.txt @@ -64,10 +64,10 @@ generic.menu.ssl.all=All SSL ciphers (most compatible) generic.menu.ssl.all.build.sslflags= generic.menu.ssl.basic=Basic SSL ciphers (lower ROM use) generic.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +generic.menu.waveform.pwm=Locked PWM +generic.menu.waveform.pwm.build.waveform= generic.menu.waveform.phase=Locked Phase generic.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -generic.menu.waveform.pwm=Locked PWM -generic.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM generic.menu.ResetMethod.nodemcu=dtr (aka nodemcu) generic.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset generic.menu.ResetMethod.ck=no dtr (aka ck) @@ -537,10 +537,10 @@ esp8285.menu.ssl.all=All SSL ciphers (most compatible) esp8285.menu.ssl.all.build.sslflags= esp8285.menu.ssl.basic=Basic SSL ciphers (lower ROM use) esp8285.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +esp8285.menu.waveform.pwm=Locked PWM +esp8285.menu.waveform.pwm.build.waveform= esp8285.menu.waveform.phase=Locked Phase esp8285.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -esp8285.menu.waveform.pwm=Locked PWM -esp8285.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM esp8285.menu.ResetMethod.nodemcu=dtr (aka nodemcu) esp8285.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset esp8285.menu.ResetMethod.ck=no dtr (aka ck) @@ -880,10 +880,10 @@ gen4iod.menu.ssl.all=All SSL ciphers (most compatible) gen4iod.menu.ssl.all.build.sslflags= gen4iod.menu.ssl.basic=Basic SSL ciphers (lower ROM use) gen4iod.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +gen4iod.menu.waveform.pwm=Locked PWM +gen4iod.menu.waveform.pwm.build.waveform= gen4iod.menu.waveform.phase=Locked Phase gen4iod.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -gen4iod.menu.waveform.pwm=Locked PWM -gen4iod.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM gen4iod.upload.resetmethod=--before default_reset --after hard_reset gen4iod.menu.FlashMode.dout=DOUT (compatible) gen4iod.menu.FlashMode.dout.build.flash_mode=dout @@ -1138,10 +1138,10 @@ huzzah.menu.ssl.all=All SSL ciphers (most compatible) huzzah.menu.ssl.all.build.sslflags= huzzah.menu.ssl.basic=Basic SSL ciphers (lower ROM use) huzzah.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +huzzah.menu.waveform.pwm=Locked PWM +huzzah.menu.waveform.pwm.build.waveform= huzzah.menu.waveform.phase=Locked Phase huzzah.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -huzzah.menu.waveform.pwm=Locked PWM -huzzah.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM huzzah.upload.resetmethod=--before default_reset --after hard_reset huzzah.build.flash_mode=qio huzzah.build.flash_flags=-DFLASHMODE_QIO @@ -1329,10 +1329,10 @@ wifi_slot.menu.ssl.all=All SSL ciphers (most compatible) wifi_slot.menu.ssl.all.build.sslflags= wifi_slot.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wifi_slot.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wifi_slot.menu.waveform.pwm=Locked PWM +wifi_slot.menu.waveform.pwm.build.waveform= wifi_slot.menu.waveform.phase=Locked Phase wifi_slot.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -wifi_slot.menu.waveform.pwm=Locked PWM -wifi_slot.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM wifi_slot.upload.resetmethod=--before default_reset --after hard_reset wifi_slot.menu.FlashFreq.40=40MHz wifi_slot.menu.FlashFreq.40.build.flash_freq=40 @@ -1646,10 +1646,10 @@ arduino-esp8266.menu.ssl.all=All SSL ciphers (most compatible) arduino-esp8266.menu.ssl.all.build.sslflags= arduino-esp8266.menu.ssl.basic=Basic SSL ciphers (lower ROM use) arduino-esp8266.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +arduino-esp8266.menu.waveform.pwm=Locked PWM +arduino-esp8266.menu.waveform.pwm.build.waveform= arduino-esp8266.menu.waveform.phase=Locked Phase arduino-esp8266.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -arduino-esp8266.menu.waveform.pwm=Locked PWM -arduino-esp8266.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM arduino-esp8266.upload.resetmethod=--before no_reset --after soft_reset arduino-esp8266.build.flash_mode=qio arduino-esp8266.build.flash_flags=-DFLASHMODE_QIO @@ -1838,10 +1838,10 @@ espmxdevkit.menu.ssl.all=All SSL ciphers (most compatible) espmxdevkit.menu.ssl.all.build.sslflags= espmxdevkit.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espmxdevkit.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espmxdevkit.menu.waveform.pwm=Locked PWM +espmxdevkit.menu.waveform.pwm.build.waveform= espmxdevkit.menu.waveform.phase=Locked Phase espmxdevkit.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -espmxdevkit.menu.waveform.pwm=Locked PWM -espmxdevkit.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM espmxdevkit.upload.resetmethod=--before default_reset --after hard_reset espmxdevkit.build.flash_mode=dout espmxdevkit.build.flash_flags=-DFLASHMODE_DOUT @@ -2070,10 +2070,10 @@ oak.menu.ssl.all=All SSL ciphers (most compatible) oak.menu.ssl.all.build.sslflags= oak.menu.ssl.basic=Basic SSL ciphers (lower ROM use) oak.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +oak.menu.waveform.pwm=Locked PWM +oak.menu.waveform.pwm.build.waveform= oak.menu.waveform.phase=Locked Phase oak.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -oak.menu.waveform.pwm=Locked PWM -oak.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM oak.upload.resetmethod=--before no_reset --after soft_reset oak.build.flash_mode=dio oak.build.flash_flags=-DFLASHMODE_DIO @@ -2270,10 +2270,10 @@ espduino.menu.ssl.all=All SSL ciphers (most compatible) espduino.menu.ssl.all.build.sslflags= espduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espduino.menu.waveform.pwm=Locked PWM +espduino.menu.waveform.pwm.build.waveform= espduino.menu.waveform.phase=Locked Phase espduino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -espduino.menu.waveform.pwm=Locked PWM -espduino.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM espduino.build.flash_mode=dio espduino.build.flash_flags=-DFLASHMODE_DIO espduino.build.flash_freq=40 @@ -2460,10 +2460,10 @@ espectro.menu.ssl.all=All SSL ciphers (most compatible) espectro.menu.ssl.all.build.sslflags= espectro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espectro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espectro.menu.waveform.pwm=Locked PWM +espectro.menu.waveform.pwm.build.waveform= espectro.menu.waveform.phase=Locked Phase espectro.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -espectro.menu.waveform.pwm=Locked PWM -espectro.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM espectro.upload.resetmethod=--before default_reset --after hard_reset espectro.build.flash_mode=dio espectro.build.flash_flags=-DFLASHMODE_DIO @@ -2651,10 +2651,10 @@ espino.menu.ssl.all=All SSL ciphers (most compatible) espino.menu.ssl.all.build.sslflags= espino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espino.menu.waveform.pwm=Locked PWM +espino.menu.waveform.pwm.build.waveform= espino.menu.waveform.phase=Locked Phase espino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -espino.menu.waveform.pwm=Locked PWM -espino.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM espino.menu.ResetMethod.nodemcu=dtr (aka nodemcu) espino.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset espino.menu.ResetMethod.ck=no dtr (aka ck) @@ -2845,10 +2845,10 @@ espresso_lite_v1.menu.ssl.all=All SSL ciphers (most compatible) espresso_lite_v1.menu.ssl.all.build.sslflags= espresso_lite_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espresso_lite_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espresso_lite_v1.menu.waveform.pwm=Locked PWM +espresso_lite_v1.menu.waveform.pwm.build.waveform= espresso_lite_v1.menu.waveform.phase=Locked Phase espresso_lite_v1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -espresso_lite_v1.menu.waveform.pwm=Locked PWM -espresso_lite_v1.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM espresso_lite_v1.build.flash_mode=dio espresso_lite_v1.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v1.build.flash_freq=40 @@ -3039,10 +3039,10 @@ espresso_lite_v2.menu.ssl.all=All SSL ciphers (most compatible) espresso_lite_v2.menu.ssl.all.build.sslflags= espresso_lite_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espresso_lite_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espresso_lite_v2.menu.waveform.pwm=Locked PWM +espresso_lite_v2.menu.waveform.pwm.build.waveform= espresso_lite_v2.menu.waveform.phase=Locked Phase espresso_lite_v2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -espresso_lite_v2.menu.waveform.pwm=Locked PWM -espresso_lite_v2.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM espresso_lite_v2.build.flash_mode=dio espresso_lite_v2.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v2.build.flash_freq=40 @@ -3243,10 +3243,10 @@ sonoff.menu.ssl.all=All SSL ciphers (most compatible) sonoff.menu.ssl.all.build.sslflags= sonoff.menu.ssl.basic=Basic SSL ciphers (lower ROM use) sonoff.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +sonoff.menu.waveform.pwm=Locked PWM +sonoff.menu.waveform.pwm.build.waveform= sonoff.menu.waveform.phase=Locked Phase sonoff.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -sonoff.menu.waveform.pwm=Locked PWM -sonoff.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM sonoff.upload.resetmethod=--before no_reset --after soft_reset sonoff.build.flash_mode=dout sonoff.build.flash_flags=-DFLASHMODE_DOUT @@ -3474,10 +3474,10 @@ inventone.menu.ssl.all=All SSL ciphers (most compatible) inventone.menu.ssl.all.build.sslflags= inventone.menu.ssl.basic=Basic SSL ciphers (lower ROM use) inventone.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +inventone.menu.waveform.pwm=Locked PWM +inventone.menu.waveform.pwm.build.waveform= inventone.menu.waveform.phase=Locked Phase inventone.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -inventone.menu.waveform.pwm=Locked PWM -inventone.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM inventone.upload.resetmethod=--before default_reset --after hard_reset inventone.build.flash_mode=dio inventone.build.flash_flags=-DFLASHMODE_DIO @@ -3665,10 +3665,10 @@ d1_mini.menu.ssl.all=All SSL ciphers (most compatible) d1_mini.menu.ssl.all.build.sslflags= d1_mini.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1_mini.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_mini.menu.waveform.pwm=Locked PWM +d1_mini.menu.waveform.pwm.build.waveform= d1_mini.menu.waveform.phase=Locked Phase d1_mini.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -d1_mini.menu.waveform.pwm=Locked PWM -d1_mini.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM d1_mini.upload.resetmethod=--before default_reset --after hard_reset d1_mini.build.flash_mode=dio d1_mini.build.flash_flags=-DFLASHMODE_DIO @@ -3856,10 +3856,10 @@ d1_mini_lite.menu.ssl.all=All SSL ciphers (most compatible) d1_mini_lite.menu.ssl.all.build.sslflags= d1_mini_lite.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1_mini_lite.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_mini_lite.menu.waveform.pwm=Locked PWM +d1_mini_lite.menu.waveform.pwm.build.waveform= d1_mini_lite.menu.waveform.phase=Locked Phase d1_mini_lite.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -d1_mini_lite.menu.waveform.pwm=Locked PWM -d1_mini_lite.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM d1_mini_lite.upload.resetmethod=--before default_reset --after hard_reset d1_mini_lite.build.flash_mode=dout d1_mini_lite.build.flash_flags=-DFLASHMODE_DOUT @@ -4087,10 +4087,10 @@ d1_mini_pro.menu.ssl.all=All SSL ciphers (most compatible) d1_mini_pro.menu.ssl.all.build.sslflags= d1_mini_pro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1_mini_pro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_mini_pro.menu.waveform.pwm=Locked PWM +d1_mini_pro.menu.waveform.pwm.build.waveform= d1_mini_pro.menu.waveform.phase=Locked Phase d1_mini_pro.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -d1_mini_pro.menu.waveform.pwm=Locked PWM -d1_mini_pro.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM d1_mini_pro.upload.resetmethod=--before default_reset --after hard_reset d1_mini_pro.build.flash_mode=dio d1_mini_pro.build.flash_flags=-DFLASHMODE_DIO @@ -4261,10 +4261,10 @@ d1.menu.ssl.all=All SSL ciphers (most compatible) d1.menu.ssl.all.build.sslflags= d1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1.menu.waveform.pwm=Locked PWM +d1.menu.waveform.pwm.build.waveform= d1.menu.waveform.phase=Locked Phase d1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -d1.menu.waveform.pwm=Locked PWM -d1.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM d1.upload.resetmethod=--before default_reset --after hard_reset d1.build.flash_mode=dio d1.build.flash_flags=-DFLASHMODE_DIO @@ -4452,10 +4452,10 @@ nodemcu.menu.ssl.all=All SSL ciphers (most compatible) nodemcu.menu.ssl.all.build.sslflags= nodemcu.menu.ssl.basic=Basic SSL ciphers (lower ROM use) nodemcu.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +nodemcu.menu.waveform.pwm=Locked PWM +nodemcu.menu.waveform.pwm.build.waveform= nodemcu.menu.waveform.phase=Locked Phase nodemcu.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -nodemcu.menu.waveform.pwm=Locked PWM -nodemcu.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM nodemcu.upload.resetmethod=--before default_reset --after hard_reset nodemcu.build.flash_mode=qio nodemcu.build.flash_flags=-DFLASHMODE_QIO @@ -4643,10 +4643,10 @@ nodemcuv2.menu.ssl.all=All SSL ciphers (most compatible) nodemcuv2.menu.ssl.all.build.sslflags= nodemcuv2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) nodemcuv2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +nodemcuv2.menu.waveform.pwm=Locked PWM +nodemcuv2.menu.waveform.pwm.build.waveform= nodemcuv2.menu.waveform.phase=Locked Phase nodemcuv2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -nodemcuv2.menu.waveform.pwm=Locked PWM -nodemcuv2.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM nodemcuv2.upload.resetmethod=--before default_reset --after hard_reset nodemcuv2.build.flash_mode=dio nodemcuv2.build.flash_flags=-DFLASHMODE_DIO @@ -4838,10 +4838,10 @@ modwifi.menu.ssl.all=All SSL ciphers (most compatible) modwifi.menu.ssl.all.build.sslflags= modwifi.menu.ssl.basic=Basic SSL ciphers (lower ROM use) modwifi.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +modwifi.menu.waveform.pwm=Locked PWM +modwifi.menu.waveform.pwm.build.waveform= modwifi.menu.waveform.phase=Locked Phase modwifi.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -modwifi.menu.waveform.pwm=Locked PWM -modwifi.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM modwifi.upload.resetmethod=--before no_reset --after soft_reset modwifi.build.flash_mode=qio modwifi.build.flash_flags=-DFLASHMODE_QIO @@ -5049,10 +5049,10 @@ phoenix_v1.menu.ssl.all=All SSL ciphers (most compatible) phoenix_v1.menu.ssl.all.build.sslflags= phoenix_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) phoenix_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +phoenix_v1.menu.waveform.pwm=Locked PWM +phoenix_v1.menu.waveform.pwm.build.waveform= phoenix_v1.menu.waveform.phase=Locked Phase phoenix_v1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -phoenix_v1.menu.waveform.pwm=Locked PWM -phoenix_v1.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM phoenix_v1.build.flash_mode=dio phoenix_v1.build.flash_flags=-DFLASHMODE_DIO phoenix_v1.build.flash_freq=40 @@ -5243,10 +5243,10 @@ phoenix_v2.menu.ssl.all=All SSL ciphers (most compatible) phoenix_v2.menu.ssl.all.build.sslflags= phoenix_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) phoenix_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +phoenix_v2.menu.waveform.pwm=Locked PWM +phoenix_v2.menu.waveform.pwm.build.waveform= phoenix_v2.menu.waveform.phase=Locked Phase phoenix_v2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -phoenix_v2.menu.waveform.pwm=Locked PWM -phoenix_v2.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM phoenix_v2.build.flash_mode=dio phoenix_v2.build.flash_flags=-DFLASHMODE_DIO phoenix_v2.build.flash_freq=40 @@ -5437,10 +5437,10 @@ eduinowifi.menu.ssl.all=All SSL ciphers (most compatible) eduinowifi.menu.ssl.all.build.sslflags= eduinowifi.menu.ssl.basic=Basic SSL ciphers (lower ROM use) eduinowifi.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +eduinowifi.menu.waveform.pwm=Locked PWM +eduinowifi.menu.waveform.pwm.build.waveform= eduinowifi.menu.waveform.phase=Locked Phase eduinowifi.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -eduinowifi.menu.waveform.pwm=Locked PWM -eduinowifi.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM eduinowifi.upload.resetmethod=--before default_reset --after hard_reset eduinowifi.build.flash_mode=dio eduinowifi.build.flash_flags=-DFLASHMODE_DIO @@ -5628,10 +5628,10 @@ wiolink.menu.ssl.all=All SSL ciphers (most compatible) wiolink.menu.ssl.all.build.sslflags= wiolink.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wiolink.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wiolink.menu.waveform.pwm=Locked PWM +wiolink.menu.waveform.pwm.build.waveform= wiolink.menu.waveform.phase=Locked Phase wiolink.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -wiolink.menu.waveform.pwm=Locked PWM -wiolink.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM wiolink.upload.resetmethod=--before default_reset --after hard_reset wiolink.build.flash_mode=qio wiolink.build.flash_flags=-DFLASHMODE_QIO @@ -5819,10 +5819,10 @@ blynk.menu.ssl.all=All SSL ciphers (most compatible) blynk.menu.ssl.all.build.sslflags= blynk.menu.ssl.basic=Basic SSL ciphers (lower ROM use) blynk.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +blynk.menu.waveform.pwm=Locked PWM +blynk.menu.waveform.pwm.build.waveform= blynk.menu.waveform.phase=Locked Phase blynk.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -blynk.menu.waveform.pwm=Locked PWM -blynk.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM blynk.upload.resetmethod=--before default_reset --after hard_reset blynk.build.flash_mode=qio blynk.build.flash_flags=-DFLASHMODE_QIO @@ -6010,10 +6010,10 @@ thing.menu.ssl.all=All SSL ciphers (most compatible) thing.menu.ssl.all.build.sslflags= thing.menu.ssl.basic=Basic SSL ciphers (lower ROM use) thing.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +thing.menu.waveform.pwm=Locked PWM +thing.menu.waveform.pwm.build.waveform= thing.menu.waveform.phase=Locked Phase thing.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -thing.menu.waveform.pwm=Locked PWM -thing.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM thing.upload.resetmethod=--before no_reset --after soft_reset thing.build.flash_mode=qio thing.build.flash_flags=-DFLASHMODE_QIO @@ -6201,10 +6201,10 @@ thingdev.menu.ssl.all=All SSL ciphers (most compatible) thingdev.menu.ssl.all.build.sslflags= thingdev.menu.ssl.basic=Basic SSL ciphers (lower ROM use) thingdev.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +thingdev.menu.waveform.pwm=Locked PWM +thingdev.menu.waveform.pwm.build.waveform= thingdev.menu.waveform.phase=Locked Phase thingdev.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -thingdev.menu.waveform.pwm=Locked PWM -thingdev.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM thingdev.upload.resetmethod=--before default_reset --after hard_reset thingdev.build.flash_mode=dio thingdev.build.flash_flags=-DFLASHMODE_DIO @@ -6392,10 +6392,10 @@ esp210.menu.ssl.all=All SSL ciphers (most compatible) esp210.menu.ssl.all.build.sslflags= esp210.menu.ssl.basic=Basic SSL ciphers (lower ROM use) esp210.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +esp210.menu.waveform.pwm=Locked PWM +esp210.menu.waveform.pwm.build.waveform= esp210.menu.waveform.phase=Locked Phase esp210.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -esp210.menu.waveform.pwm=Locked PWM -esp210.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM esp210.upload.resetmethod=--before no_reset --after soft_reset esp210.build.flash_mode=qio esp210.build.flash_flags=-DFLASHMODE_QIO @@ -6583,10 +6583,10 @@ espinotee.menu.ssl.all=All SSL ciphers (most compatible) espinotee.menu.ssl.all.build.sslflags= espinotee.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espinotee.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +espinotee.menu.waveform.pwm=Locked PWM +espinotee.menu.waveform.pwm.build.waveform= espinotee.menu.waveform.phase=Locked Phase espinotee.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -espinotee.menu.waveform.pwm=Locked PWM -espinotee.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM espinotee.upload.resetmethod=--before default_reset --after hard_reset espinotee.build.flash_mode=qio espinotee.build.flash_flags=-DFLASHMODE_QIO @@ -6774,10 +6774,10 @@ wifiduino.menu.ssl.all=All SSL ciphers (most compatible) wifiduino.menu.ssl.all.build.sslflags= wifiduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wifiduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wifiduino.menu.waveform.pwm=Locked PWM +wifiduino.menu.waveform.pwm.build.waveform= wifiduino.menu.waveform.phase=Locked Phase wifiduino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -wifiduino.menu.waveform.pwm=Locked PWM -wifiduino.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM wifiduino.upload.resetmethod=--before default_reset --after hard_reset wifiduino.build.flash_mode=dio wifiduino.build.flash_flags=-DFLASHMODE_DIO @@ -6982,10 +6982,10 @@ wifinfo.menu.ssl.all=All SSL ciphers (most compatible) wifinfo.menu.ssl.all.build.sslflags= wifinfo.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wifinfo.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wifinfo.menu.waveform.pwm=Locked PWM +wifinfo.menu.waveform.pwm.build.waveform= wifinfo.menu.waveform.phase=Locked Phase wifinfo.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -wifinfo.menu.waveform.pwm=Locked PWM -wifinfo.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM wifinfo.upload.resetmethod=--before default_reset --after hard_reset wifinfo.build.flash_mode=qio wifinfo.build.flash_flags=-DFLASHMODE_QIO @@ -7220,10 +7220,10 @@ cw01.menu.ssl.all=All SSL ciphers (most compatible) cw01.menu.ssl.all.build.sslflags= cw01.menu.ssl.basic=Basic SSL ciphers (lower ROM use) cw01.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +cw01.menu.waveform.pwm=Locked PWM +cw01.menu.waveform.pwm.build.waveform= cw01.menu.waveform.phase=Locked Phase cw01.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE -cw01.menu.waveform.pwm=Locked PWM -cw01.menu.waveform.pwm.build.waveform=-DWAVEFORM_LOCKED_PWM cw01.upload.resetmethod=--before default_reset --after hard_reset cw01.menu.CrystalFreq.26=26 MHz cw01.menu.CrystalFreq.40=40 MHz diff --git a/cores/esp8266/Tone.cpp b/cores/esp8266/Tone.cpp index 601b1df51..8deeb63d0 100644 --- a/cores/esp8266/Tone.cpp +++ b/cores/esp8266/Tone.cpp @@ -30,6 +30,13 @@ static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, uint32_t durat return; } +#ifndef WAVEFORM_LOCKED_PHASE + // Stop any analogWrites (PWM) because they are a different generator + _stopPWM(_pin); +#endif + // If there's another Tone or startWaveform on this pin + // it will be changed on-the-fly (no need to stop it) + pinMode(_pin, OUTPUT); high = std::max(high, (uint32_t)microsecondsToClockCycles(25)); // new 20KHz maximum tone frequency, diff --git a/cores/esp8266/core_esp8266_waveform.h b/cores/esp8266/core_esp8266_waveform.h index e24819a65..4c5ce4fed 100644 --- a/cores/esp8266/core_esp8266_waveform.h +++ b/cores/esp8266/core_esp8266_waveform.h @@ -1,93 +1,7 @@ -/* - esp8266_waveform - General purpose waveform generation and control, - supporting outputs on all pins in parallel. - - Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. - Copyright (c) 2020 Dirk O. Kaar. - - The core idea is to have a programmable waveform generator with a unique - high and low period (defined in microseconds or CPU clock cycles). TIMER1 is - set to 1-shot mode and is always loaded with the time until the next edge - of any live waveforms. - - Up to one waveform generator per pin supported. - - Each waveform generator is synchronized to the ESP clock cycle counter, not the - timer. This allows for removing interrupt jitter and delay as the counter - always increments once per 80MHz clock. Changes to a waveform are - contiguous and only take effect on the next waveform transition, - allowing for smooth transitions. - - This replaces older tone(), analogWrite(), and the Servo classes. - - Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount() - clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 - cycles (which may be 2 CPU clock cycles @ 160MHz). - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ +// Wrapper to include both versions of the waveform generator #ifdef WAVEFORM_LOCKED_PHASE - -#include - -#ifndef __ESP8266_WAVEFORM_H -#define __ESP8266_WAVEFORM_H - -#ifdef __cplusplus -extern "C" { + #include "core_esp8266_waveform_phase.h" +#else + #include "core_esp8266_waveform_pwm.h" #endif - -// Start or change a waveform of the specified high and low times on specific pin. -// If runtimeUS > 0 then automatically stop it after that many usecs, relative to the next -// full period. -// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, -// the new waveform is started at phaseOffsetUS phase offset, in microseconds, to that. -// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio -// under load, for applications where frequency or duty cycle must not change, leave false. -// Returns true or false on success or failure. -int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, - uint32_t runTimeUS = 0, int8_t alignPhase = -1, uint32_t phaseOffsetUS = 0, bool autoPwm = false); -// Start or change a waveform of the specified high and low CPU clock cycles on specific pin. -// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles, relative to the next -// full period. -// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, -// the new waveform is started at phaseOffsetCcys phase offset, in CPU clock cycles, to that. -// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio -// under load, for applications where frequency or duty cycle must not change, leave false. -// Returns true or false on success or failure. -int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCcys, uint32_t timeLowCcys, - uint32_t runTimeCcys = 0, int8_t alignPhase = -1, uint32_t phaseOffsetCcys = 0, bool autoPwm = false); -// Stop a waveform, if any, on the specified pin. -// Returns true or false on success or failure. -int stopWaveform(uint8_t pin); - -// Add a callback function to be called on *EVERY* timer1 trigger. The -// callback returns the number of microseconds until the next desired call. -// However, since it is called every timer1 interrupt, it may be called -// again before this period. It should therefore use the ESP Cycle Counter -// to determine whether or not to perform an operation. -// Pass in NULL to disable the callback and, if no other waveforms being -// generated, stop the timer as well. -// Make sure the CB function has the ICACHE_RAM_ATTR decorator. -void setTimer1Callback(uint32_t (*fn)()); - -#ifdef __cplusplus -} -#endif - -#endif // __ESP8266_WAVEFORM_H - -#endif // WAVEFORM_LOCKED_PHASE diff --git a/cores/esp8266/core_esp8266_waveform.cpp b/cores/esp8266/core_esp8266_waveform_phase.cpp similarity index 99% rename from cores/esp8266/core_esp8266_waveform.cpp rename to cores/esp8266/core_esp8266_waveform_phase.cpp index 4bd0f1c01..62e3ad3dd 100644 --- a/cores/esp8266/core_esp8266_waveform.cpp +++ b/cores/esp8266/core_esp8266_waveform_phase.cpp @@ -41,7 +41,7 @@ #ifdef WAVEFORM_LOCKED_PHASE -#include "core_esp8266_waveform.h" +#include "core_esp8266_waveform_phase.h" #include #include "ets_sys.h" #include diff --git a/cores/esp8266/core_esp8266_waveform_phase.h b/cores/esp8266/core_esp8266_waveform_phase.h new file mode 100644 index 000000000..e24819a65 --- /dev/null +++ b/cores/esp8266/core_esp8266_waveform_phase.h @@ -0,0 +1,93 @@ +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + Copyright (c) 2020 Dirk O. Kaar. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds or CPU clock cycles). TIMER1 is + set to 1-shot mode and is always loaded with the time until the next edge + of any live waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP clock cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount() + clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 + cycles (which may be 2 CPU clock cycles @ 160MHz). + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifdef WAVEFORM_LOCKED_PHASE + +#include + +#ifndef __ESP8266_WAVEFORM_H +#define __ESP8266_WAVEFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Start or change a waveform of the specified high and low times on specific pin. +// If runtimeUS > 0 then automatically stop it after that many usecs, relative to the next +// full period. +// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, +// the new waveform is started at phaseOffsetUS phase offset, in microseconds, to that. +// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio +// under load, for applications where frequency or duty cycle must not change, leave false. +// Returns true or false on success or failure. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, + uint32_t runTimeUS = 0, int8_t alignPhase = -1, uint32_t phaseOffsetUS = 0, bool autoPwm = false); +// Start or change a waveform of the specified high and low CPU clock cycles on specific pin. +// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles, relative to the next +// full period. +// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, +// the new waveform is started at phaseOffsetCcys phase offset, in CPU clock cycles, to that. +// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio +// under load, for applications where frequency or duty cycle must not change, leave false. +// Returns true or false on success or failure. +int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCcys, uint32_t timeLowCcys, + uint32_t runTimeCcys = 0, int8_t alignPhase = -1, uint32_t phaseOffsetCcys = 0, bool autoPwm = false); +// Stop a waveform, if any, on the specified pin. +// Returns true or false on success or failure. +int stopWaveform(uint8_t pin); + +// Add a callback function to be called on *EVERY* timer1 trigger. The +// callback returns the number of microseconds until the next desired call. +// However, since it is called every timer1 interrupt, it may be called +// again before this period. It should therefore use the ESP Cycle Counter +// to determine whether or not to perform an operation. +// Pass in NULL to disable the callback and, if no other waveforms being +// generated, stop the timer as well. +// Make sure the CB function has the ICACHE_RAM_ATTR decorator. +void setTimer1Callback(uint32_t (*fn)()); + +#ifdef __cplusplus +} +#endif + +#endif // __ESP8266_WAVEFORM_H + +#endif // WAVEFORM_LOCKED_PHASE diff --git a/cores/esp8266/core_esp8266_waveform_pwm.cpp b/cores/esp8266/core_esp8266_waveform_pwm.cpp new file mode 100644 index 000000000..aea88e15b --- /dev/null +++ b/cores/esp8266/core_esp8266_waveform_pwm.cpp @@ -0,0 +1,626 @@ +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds or CPU clock cycles). TIMER1 + is set to 1-shot mode and is always loaded with the time until the next + edge of any live waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP clock cycle counter, not + the timer. This allows for removing interrupt jitter and delay as the + counter always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "cycles" is used, it means ESP.getCycleCount() + clock cycle count, or an interval measured in CPU clock cycles, but not + TIMER1 cycles (which may be 2 CPU clock cycles @ 160MHz). + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef WAVEFORM_LOCKED_PHASE + +#include +#include "ets_sys.h" +#include "core_esp8266_waveform_pwm.h" +#include "user_interface.h" +extern "C" { + +// Maximum delay between IRQs +#define MAXIRQUS (10000) + +// Waveform generator can create tones, PWM, and servos +typedef struct { + uint32_t nextServiceCycle; // ESP cycle timer when a transition required + uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop + uint32_t timeHighCycles; // Actual running waveform period (adjusted using desiredCycles) + uint32_t timeLowCycles; // + uint32_t desiredHighCycles; // Ideal waveform period to drive the error signal + uint32_t desiredLowCycles; // + uint32_t lastEdge; // Cycle when this generator last changed +} Waveform; + +class WVFState { +public: + Waveform waveform[17]; // State of all possible pins + uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code + uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code + + // Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine + uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin + uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation + + uint32_t waveformToChange = 0; // Mask of pin to change. One bit set in main app, cleared when effected in the NMI + uint32_t waveformNewHigh = 0; + uint32_t waveformNewLow = 0; + + uint32_t (*timer1CB)() = NULL; + + // Optimize the NMI inner loop by keeping track of the min and max GPIO that we + // are generating. In the common case (1 PWM) these may be the same pin and + // we can avoid looking at the other pins. + uint16_t startPin = 0; + uint16_t endPin = 0; +}; +static WVFState wvfState; + + +// Ensure everything is read/written to RAM +#define MEMBARRIER() { __asm__ volatile("" ::: "memory"); } + +// Non-speed critical bits +#pragma GCC optimize ("Os") + +// Interrupt on/off control +static ICACHE_RAM_ATTR void timer1Interrupt(); +static bool timerRunning = false; + +static __attribute__((noinline)) void initTimer() { + if (!timerRunning) { + timer1_disable(); + ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); + ETS_FRC_TIMER1_NMI_INTR_ATTACH(timer1Interrupt); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); + timerRunning = true; + timer1_write(microsecondsToClockCycles(10)); + } +} + +static ICACHE_RAM_ATTR void forceTimerInterrupt() { + if (T1L > microsecondsToClockCycles(10)) { + T1L = microsecondsToClockCycles(10); + } +} + +// PWM implementation using special purpose state machine +// +// Keep an ordered list of pins with the delta in cycles between each +// element, with a terminal entry making up the remainder of the PWM +// period. With this method sum(all deltas) == PWM period clock cycles. +// +// At t=0 set all pins high and set the timeout for the 1st edge. +// On interrupt, if we're at the last element reset to t=0 state +// Otherwise, clear that pin down and set delay for next element +// and so forth. + +constexpr int maxPWMs = 8; + +// PWM machine state +typedef struct PWMState { + uint32_t mask; // Bitmask of active pins + uint32_t cnt; // How many entries + uint32_t idx; // Where the state machine is along the list + uint8_t pin[maxPWMs + 1]; + uint32_t delta[maxPWMs + 1]; + uint32_t nextServiceCycle; // Clock cycle for next step + struct PWMState *pwmUpdate; // Set by main code, cleared by ISR +} PWMState; + +static PWMState pwmState; +static uint32_t _pwmFreq = 1000; +static uint32_t _pwmPeriod = microsecondsToClockCycles(1000000UL) / _pwmFreq; + + +// If there are no more scheduled activities, shut down Timer 1. +// Otherwise, do nothing. +static ICACHE_RAM_ATTR void disableIdleTimer() { + if (timerRunning && !wvfState.waveformEnabled && !pwmState.cnt && !wvfState.timer1CB) { + ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); + timer1_disable(); + timer1_isr_init(); + timerRunning = false; + } +} + +// Notify the NMI that a new PWM state is available through the mailbox. +// Wait for mailbox to be emptied (either busy or delay() as needed) +static ICACHE_RAM_ATTR void _notifyPWM(PWMState *p, bool idle) { + p->pwmUpdate = nullptr; + pwmState.pwmUpdate = p; + MEMBARRIER(); + forceTimerInterrupt(); + while (pwmState.pwmUpdate) { + if (idle) { + delay(0); + } + MEMBARRIER(); + } +} + +static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range); + +// Called when analogWriteFreq() changed to update the PWM total period +void _setPWMFreq(uint32_t freq) { + _pwmFreq = freq; + + // Convert frequency into clock cycles + uint32_t cc = microsecondsToClockCycles(1000000UL) / freq; + + // Simple static adjustment to bring period closer to requested due to overhead + // Empirically determined as a constant PWM delay and a function of the number of PWMs +#if F_CPU == 80000000 + cc -= ((microsecondsToClockCycles(pwmState.cnt) * 13) >> 4) + 110; +#else + cc -= ((microsecondsToClockCycles(pwmState.cnt) * 10) >> 4) + 75; +#endif + + if (cc == _pwmPeriod) { + return; // No change + } + + _pwmPeriod = cc; + + if (pwmState.cnt) { + PWMState p; // The working copy since we can't edit the one in use + p.mask = 0; + p.cnt = 0; + for (uint32_t i = 0; i < pwmState.cnt; i++) { + auto pin = pwmState.pin[i]; + _addPWMtoList(p, pin, wvfState.waveform[pin].desiredHighCycles, wvfState.waveform[pin].desiredLowCycles); + } + // Update and wait for mailbox to be emptied + initTimer(); + _notifyPWM(&p, true); + disableIdleTimer(); + } +} + +// Helper routine to remove an entry from the state machine +// and clean up any marked-off entries +static void _cleanAndRemovePWM(PWMState *p, int pin) { + uint32_t leftover = 0; + uint32_t in, out; + for (in = 0, out = 0; in < p->cnt; in++) { + if ((p->pin[in] != pin) && (p->mask & (1<pin[in]))) { + p->pin[out] = p->pin[in]; + p->delta[out] = p->delta[in] + leftover; + leftover = 0; + out++; + } else { + leftover += p->delta[in]; + p->mask &= ~(1<pin[in]); + } + } + p->cnt = out; + // Final pin is never used: p->pin[out] = 0xff; + p->delta[out] = p->delta[in] + leftover; +} + + +// Disable PWM on a specific pin (i.e. when a digitalWrite or analogWrite(0%/100%)) +ICACHE_RAM_ATTR bool _stopPWM(int pin) { + if (!((1<= _pwmPeriod) { + cc = _pwmPeriod - 1; + } + + if (p.cnt == 0) { + // Starting up from scratch, special case 1st element and PWM period + p.pin[0] = pin; + p.delta[0] = cc; + // Final pin is never used: p.pin[1] = 0xff; + p.delta[1] = _pwmPeriod - cc; + } else { + uint32_t ttl = 0; + uint32_t i; + // Skip along until we're at the spot to insert + for (i=0; (i <= p.cnt) && (ttl + p.delta[i] < cc); i++) { + ttl += p.delta[i]; + } + // Shift everything out by one to make space for new edge + for (int32_t j = p.cnt; j >= (int)i; j--) { + p.pin[j + 1] = p.pin[j]; + p.delta[j + 1] = p.delta[j]; + } + int off = cc - ttl; // The delta from the last edge to the one we're inserting + p.pin[i] = pin; + p.delta[i] = off; // Add the delta to this new pin + p.delta[i + 1] -= off; // And subtract it from the follower to keep sum(deltas) constant + } + p.cnt++; + p.mask |= 1<= maxPWMs) { + return false; // No space left + } + + // Sanity check for all-on/off + uint32_t cc = (_pwmPeriod * val) / range; + if ((cc == 0) || (cc >= _pwmPeriod)) { + digitalWrite(pin, cc ? HIGH : LOW); + return true; + } + + _addPWMtoList(p, pin, val, range); + + // Set mailbox and wait for ISR to copy it over + initTimer(); + _notifyPWM(&p, true); + disableIdleTimer(); + + // Potentially recalculate the PWM period if we've added another pin + _setPWMFreq(_pwmFreq); + + return true; +} + +// Start up a waveform on a pin, or change the current one. Will change to the new +// waveform smoothly on next low->high transition. For immediate change, stopWaveform() +// first, then it will immediately begin. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) { + return startWaveformClockCycles(pin, microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), microsecondsToClockCycles(runTimeUS)); +} + +int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles) { + if ((pin > 16) || isFlashInterfacePin(pin)) { + return false; + } + Waveform *wave = &wvfState.waveform[pin]; + wave->expiryCycle = runTimeCycles ? ESP.getCycleCount() + runTimeCycles : 0; + if (runTimeCycles && !wave->expiryCycle) { + wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it + } + + _stopPWM(pin); // Make sure there's no PWM live here + + uint32_t mask = 1<timeHighCycles = timeHighCycles; + wave->desiredHighCycles = timeHighCycles; + wave->timeLowCycles = timeLowCycles; + wave->desiredLowCycles = timeLowCycles; + wave->lastEdge = 0; + wave->nextServiceCycle = ESP.getCycleCount() + microsecondsToClockCycles(1); + wvfState.waveformToEnable |= mask; + MEMBARRIER(); + initTimer(); + forceTimerInterrupt(); + while (wvfState.waveformToEnable) { + delay(0); // Wait for waveform to update + // No mem barrier here, the call to a global function implies global state updated + } + } + + return true; +} + + +// Set a callback. Pass in NULL to stop it +void setTimer1Callback(uint32_t (*fn)()) { + wvfState.timer1CB = fn; + if (fn) { + initTimer(); + forceTimerInterrupt(); + } + disableIdleTimer(); +} + +// Stops a waveform on a pin +int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { + // Can't possibly need to stop anything if there is no timer active + if (!timerRunning) { + return false; + } + // If user sends in a pin >16 but <32, this will always point to a 0 bit + // If they send >=32, then the shift will result in 0 and it will also return false + uint32_t mask = 1<> 0) +#endif + +// When the time to the next edge is greater than this, RTI and set another IRQ to minimize CPU usage +#define MINIRQTIME microsecondsToClockCycles(4) + +static ICACHE_RAM_ATTR void timer1Interrupt() { + // Flag if the core is at 160 MHz, for use by adjust() + bool turbo = (*(uint32_t*)0x3FF00014) & 1 ? true : false; + + uint32_t nextEventCycle = GetCycleCountIRQ() + microsecondsToClockCycles(MAXIRQUS); + uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14); + + if (wvfState.waveformToEnable || wvfState.waveformToDisable) { + // Handle enable/disable requests from main app + wvfState.waveformEnabled = (wvfState.waveformEnabled & ~wvfState.waveformToDisable) | wvfState.waveformToEnable; // Set the requested waveforms on/off + wvfState.waveformState &= ~wvfState.waveformToEnable; // And clear the state of any just started + wvfState.waveformToEnable = 0; + wvfState.waveformToDisable = 0; + // No mem barrier. Globals must be written to RAM on ISR exit. + // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) + wvfState.startPin = __builtin_ffs(wvfState.waveformEnabled) - 1; + // Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one) + wvfState.endPin = 32 - __builtin_clz(wvfState.waveformEnabled); + } else if (!pwmState.cnt && pwmState.pwmUpdate) { + // Start up the PWM generator by copying from the mailbox + pwmState.cnt = 1; + pwmState.idx = 1; // Ensure copy this cycle, cause it to start at t=0 + pwmState.nextServiceCycle = GetCycleCountIRQ(); // Do it this loop! + // No need for mem barrier here. Global must be written by IRQ exit + } + + bool done = false; + if (wvfState.waveformEnabled || pwmState.cnt) { + do { + nextEventCycle = GetCycleCountIRQ() + microsecondsToClockCycles(MAXIRQUS); + + // PWM state machine implementation + if (pwmState.cnt) { + int32_t cyclesToGo; + do { + cyclesToGo = pwmState.nextServiceCycle - GetCycleCountIRQ(); + if (cyclesToGo < 0) { + if (pwmState.idx == pwmState.cnt) { // Start of pulses, possibly copy new + if (pwmState.pwmUpdate) { + // Do the memory copy from temp to global and clear mailbox + pwmState = *(PWMState*)pwmState.pwmUpdate; + } + GPOS = pwmState.mask; // Set all active pins high + if (pwmState.mask & (1<<16)) { + GP16O = 1; + } + pwmState.idx = 0; + } else { + do { + // Drop the pin at this edge + if (pwmState.mask & (1<expiryCycle) { + int32_t expiryToGo = wave->expiryCycle - now; + if (expiryToGo < 0) { + // Done, remove! + if (i == 16) { + GP16O = 0; + } + GPOC = mask; + wvfState.waveformEnabled &= ~mask; + continue; + } + } + + // Check for toggles + int32_t cyclesToGo = wave->nextServiceCycle - now; + if (cyclesToGo < 0) { + uint32_t nextEdgeCycles; + uint32_t desired = 0; + uint32_t *timeToUpdate; + wvfState.waveformState ^= mask; + if (wvfState.waveformState & mask) { + if (i == 16) { + GP16O = 1; + } + GPOS = mask; + + if (wvfState.waveformToChange & mask) { + // Copy over next full-cycle timings + wave->timeHighCycles = wvfState.waveformNewHigh; + wave->desiredHighCycles = wvfState.waveformNewHigh; + wave->timeLowCycles = wvfState.waveformNewLow; + wave->desiredLowCycles = wvfState.waveformNewLow; + wave->lastEdge = 0; + wvfState.waveformToChange = 0; + } + if (wave->lastEdge) { + desired = wave->desiredLowCycles; + timeToUpdate = &wave->timeLowCycles; + } + nextEdgeCycles = wave->timeHighCycles; + } else { + if (i == 16) { + GP16O = 0; + } + GPOC = mask; + desired = wave->desiredHighCycles; + timeToUpdate = &wave->timeHighCycles; + nextEdgeCycles = wave->timeLowCycles; + } + if (desired) { + desired = adjust(desired); + int32_t err = desired - (now - wave->lastEdge); + if (abs(err) < desired) { // If we've lost > the entire phase, ignore this error signal + err /= 2; + *timeToUpdate += err; + } + } + nextEdgeCycles = adjust(nextEdgeCycles); + wave->nextServiceCycle = now + nextEdgeCycles; + wave->lastEdge = now; + } + nextEventCycle = earliest(nextEventCycle, wave->nextServiceCycle); + } + + // Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur + uint32_t now = GetCycleCountIRQ(); + int32_t cycleDeltaNextEvent = nextEventCycle - now; + int32_t cyclesLeftTimeout = timeoutCycle - now; + done = (cycleDeltaNextEvent > MINIRQTIME) || (cyclesLeftTimeout < 0); + } while (!done); + } // if (wvfState.waveformEnabled) + + if (wvfState.timer1CB) { + nextEventCycle = earliest(nextEventCycle, GetCycleCountIRQ() + wvfState.timer1CB()); + } + + int32_t nextEventCycles = nextEventCycle - GetCycleCountIRQ(); + + if (nextEventCycles < MINIRQTIME) { + nextEventCycles = MINIRQTIME; + } + nextEventCycles -= DELTAIRQ; + + // Do it here instead of global function to save time and because we know it's edge-IRQ + T1L = nextEventCycles >> (turbo ? 1 : 0); +} + +}; + +#endif diff --git a/cores/esp8266/core_esp8266_waveform_pwm.h b/cores/esp8266/core_esp8266_waveform_pwm.h new file mode 100644 index 000000000..8d60e4e7c --- /dev/null +++ b/cores/esp8266/core_esp8266_waveform_pwm.h @@ -0,0 +1,87 @@ +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds or CPU clock cycles). TIMER1 is + set to 1-shot mode and is always loaded with the time until the next edge + of any live waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP clock cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "cycles" is used, it means ESP.getCycleCount() + clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 + cycles (which may be 2 CPU clock cycles @ 160MHz). + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef WAVEFORM_LOCKED_PHASE + +#include + +#ifndef __ESP8266_WAVEFORM_H +#define __ESP8266_WAVEFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Start or change a waveform of the specified high and low times on specific pin. +// If runtimeUS > 0 then automatically stop it after that many usecs. +// Returns true or false on success or failure. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS); +// Start or change a waveform of the specified high and low CPU clock cycles on specific pin. +// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles. +// Returns true or false on success or failure. +int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles); +// Stop a waveform, if any, on the specified pin. +// Returns true or false on success or failure. +int stopWaveform(uint8_t pin); + +// Add a callback function to be called on *EVERY* timer1 trigger. The +// callback returns the number of microseconds until the next desired call. +// However, since it is called every timer1 interrupt, it may be called +// again before this period. It should therefore use the ESP Cycle Counter +// to determine whether or not to perform an operation. +// Pass in NULL to disable the callback and, if no other waveforms being +// generated, stop the timer as well. +// Make sure the CB function has the ICACHE_RAM_ATTR decorator. +void setTimer1Callback(uint32_t (*fn)()); + + + +// Internal-only calls, not for applications +extern void _setPWMFreq(uint32_t freq); +extern bool _stopPWM(int pin); +extern bool _setPWM(int pin, uint32_t val, uint32_t range); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 9c15703e0..539f5448b 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -82,7 +82,10 @@ extern void __pinMode(uint8_t pin, uint8_t mode) { } extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { - stopWaveform(pin); + stopWaveform(pin); // Disable any tone +#ifndef WAVEFORM_LOCKED_PHASE + _stopPWM(pin); // ...and any analogWrite +#endif if(pin < 16){ if(val) GPOS = (1 << pin); else GPOC = (1 << pin); diff --git a/cores/esp8266/core_esp8266_wiring_pwm.cpp b/cores/esp8266/core_esp8266_wiring_pwm.cpp index 565cac7d8..a456c965c 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.cpp +++ b/cores/esp8266/core_esp8266_wiring_pwm.cpp @@ -73,11 +73,39 @@ extern void __analogWrite(uint8_t pin, int val) { } } +#else // !WAVEFORM_LOCKED_PHASE + +extern void __analogWriteFreq(uint32_t freq) { + if (freq < 100) { + freq = 100; + } else if (freq > 60000) { + freq = 60000; + } else { + freq = freq; + } + _setPWMFreq(freq); +} + +extern void __analogWrite(uint8_t pin, int val) { + if (pin > 16) { + return; + } + + if (val < 0) { + val = 0; + } else if (val > analogScale) { + val = analogScale; + } + + // Per the Arduino docs at https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/ + // val: the duty cycle: between 0 (always off) and 255 (always on). + // So if val = 0 we have digitalWrite(LOW), if we have val==range we have digitalWrite(HIGH) + pinMode(pin, OUTPUT); + _setPWM(pin, val, analogScale); +} + #endif // WAVEFORM_LOCKED_PHASE -#ifdef WAVEFORM_LOCKED_PWM - -#endif // WAVEFORM_LOCKED_PWM extern void __analogWriteRange(uint32_t range) { if ((range >= 15) && (range <= 65535)) { diff --git a/libraries/Servo/src/Servo.cpp b/libraries/Servo/src/Servo.cpp index 09d87c0f4..d24ea3379 100644 --- a/libraries/Servo/src/Servo.cpp +++ b/libraries/Servo/src/Servo.cpp @@ -95,7 +95,11 @@ void Servo::detach() { if (_attached) { _servoMap &= ~(1 << _pin); +#ifdef WAVEFORM_LOCKED_PHASE startWaveform(_pin, 0, REFRESH_INTERVAL, 1); +#else + // TODO - timeHigh == 0 is illegal in _PWM code branch. Do nothing for now. +#endif delay(REFRESH_INTERVAL / 1000); // long enough to complete active period under all circumstances. stopWaveform(_pin); _attached = false; diff --git a/libraries/SoftwareSerial b/libraries/SoftwareSerial index 6d520c259..4c08ee8d2 160000 --- a/libraries/SoftwareSerial +++ b/libraries/SoftwareSerial @@ -1 +1 @@ -Subproject commit 6d520c259cad4457ccdbee362c16f7fa3b504b06 +Subproject commit 4c08ee8d2cb7b5b27eb4f86797694cbac94aa5c9 diff --git a/tools/boards.txt.py b/tools/boards.txt.py index 816c9c00a..d0fcc4585 100755 --- a/tools/boards.txt.py +++ b/tools/boards.txt.py @@ -1471,10 +1471,10 @@ def led (name, default, ledList): def waveform (): return { 'waveform': collections.OrderedDict([ + ('.menu.waveform.pwm', 'Locked PWM'), + ('.menu.waveform.pwm.build.waveform', ''), ('.menu.waveform.phase', 'Locked Phase'), ('.menu.waveform.phase.build.waveform', '-DWAVEFORM_LOCKED_PHASE'), - ('.menu.waveform.pwm', 'Locked PWM'), - ('.menu.waveform.pwm.build.waveform', '-DWAVEFORM_LOCKED_PWM'), ]) } diff --git a/tools/platformio-build.py b/tools/platformio-build.py index c735247f3..6620b3740 100644 --- a/tools/platformio-build.py +++ b/tools/platformio-build.py @@ -242,9 +242,7 @@ else: # if "PIO_FRAMEWORK_ARDUINO_WAVEFORM_LOCKED_PWM" in flatten_cppdefines: env.Append(CPPDEFINES=[("WAVEFORM_LOCKED_PWM", 1)]) -# PIO_FRAMEWORK_ARDUINO_WAVEFORM_LOCKED_PHASE (defaults) -else: - env.Append(CPPDEFINES=[("WAVEFORM_LOCKED_PHASE", 1)]) +# PIO_FRAMEWORK_ARDUINO_WAVEFORM_LOCKED_PHASE will be used by default # # VTables