static int gpio_pulse_cancel(lua_State *L) { pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse"); if (active_pulser != pulser) { return 0; } // Shut off the timer platform_hw_timer_close(TIMER_OWNER); int rc = gpio_pulse_push_state(L, active_pulser); active_pulser = NULL; int pulser_ref = active_pulser_ref; active_pulser_ref = LUA_NOREF; luaL_unref(L, LUA_REGISTRYINDEX, pulser_ref); return rc; }
// Returns FALSE if we cannot start bool 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]); // First we need to make sure that the interrupt handler is running // out of the same set of params as we expect while (!pwm_timer_down && pwm_toggle != pwm_current_toggle) { os_delay_us(100); } if (pwm_timer_down) { pwm_toggle = pwm_current_toggle; } uint8_t new_toggle = pwm_toggle ^ 0x01; struct pwm_single_param *local_single = pwm_single_toggle[new_toggle]; uint8 *local_channel = &pwm_channel_toggle[new_toggle]; // 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 (or nearly the same duty). If there is // under 2 us between pwm outputs, then treat them as the same. for (i = pwm_channel_num; i > 0; i--) { if (local_single[i].h_time <= local_single[i - 1].h_time + US_TO_RTC_TIMER_TICKS(2)) { 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++) { os_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++) { os_memcpy(&local_single[i - 1], &local_single[i], sizeof(struct pwm_single_param)); } (*local_channel)--; } // Make the new ones active pwm_toggle = new_toggle; // if timer is down, need to set gpio and start timer if (pwm_timer_down == 1) { pwm_channel = local_channel; pwm_single = local_single; pwm_current_toggle = pwm_toggle; // 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_DBG("Need to setup timer\n"); if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, FALSE)) { return FALSE; } pwm_timer_down = 0; platform_hw_timer_set_func(TIMER_OWNER, pwm_tim1_intr_handler, 0); platform_hw_timer_arm_ticks(TIMER_OWNER, local_single[0].h_time); } else { PWM_DBG("Timer left idle\n"); platform_hw_timer_close(TIMER_OWNER); } } else { // ensure that all outputs are outputs gpio_output_set(0, 0, pwm_gpio, 0); } #ifdef PWM_DBG_PIN // Enable as output gpio_output_set(0, 0, 1 << PWM_DBG_PIN, 0); #endif 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); return TRUE; }
static void ICACHE_RAM_ATTR gpio_pulse_timeout(os_param_t p) { (void) p; uint32_t now = system_get_time(); int delay; if (active_pulser) { delay = active_pulser->pending_delay; if (delay > 0) { if (delay > 1200000) { delay = 1000000; } active_pulser->pending_delay -= delay; platform_hw_timer_arm_us(TIMER_OWNER, delay); return; } } do { active_pulser->active_pos = active_pulser->entry_pos; if (!active_pulser || active_pulser->entry_pos >= active_pulser->entry_count) { if (active_pulser) { active_pulser->steps++; } platform_hw_timer_close(TIMER_OWNER); task_post_low(tasknumber, (task_param_t)0); return; } active_pulser->steps++; pulse_entry_t *entry = active_pulser->entry + active_pulser->entry_pos; // Yes, this means that there is more skew on D0 than on other pins.... if (entry->gpio_set & 0x10000) { gpio16_output_set(1); } GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, entry->gpio_set); GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, entry->gpio_clr); if (entry->gpio_clr & 0x10000) { gpio16_output_set(0); } int16_t stop = active_pulser->stop_pos; if (stop == -2 || stop == active_pulser->entry_pos) { platform_hw_timer_close(TIMER_OWNER); task_post_low(tasknumber, (task_param_t)0); return; } if (entry->loop) { if (entry->count_left == 0) { entry->count_left = entry->count + 1; } if (--entry->count_left >= 1) { active_pulser->entry_pos = entry->loop - 1; // zero offset } else { active_pulser->entry_pos++; } } else { active_pulser->entry_pos++; } delay = entry->delay; int delay_offset = 0; if (entry->delay_min != -1) { int offset = active_pulser->next_adjust; active_pulser->next_adjust = 0; delay_offset = ((0x7fffffff & (now - active_pulser->desired_end_time)) << 1) >> 1; delay -= delay_offset; delay += offset; //dbg_printf("%d(et %d diff %d): Delay was %d us, offset = %d, delay_offset = %d, new delay = %d, range=%d..%d\n", // now, active_pulser->desired_end_time, now - active_pulser->desired_end_time, // entry->delay, offset, delay_offset, delay, entry->delay_min, entry->delay_max); if (delay < entry->delay_min) { // we can't delay as little as 'delay', so we need to adjust // the next period as well. active_pulser->next_adjust = (entry->delay - entry->delay_min) + offset; delay = entry->delay_min; } else if (delay > entry->delay_max) { // we can't delay as much as 'delay', so we need to adjust // the next period as well. active_pulser->next_adjust = (entry->delay - entry->delay_max) + offset; delay = entry->delay_max; } } active_pulser->desired_end_time += delay + delay_offset; active_pulser->expected_end_time = system_get_time() + delay; } while (delay < 3);