/******************************************************************************
* Copyright 2013-2014 Espressif Systems (Wuxi)
*
* FileName: pwm.c
*
* Description: pwm driver
*
* Modification history:
* 2014/5/1, v1.0 create this file.
*******************************************************************************/
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "user_interface.h"
#include "pwm.h"
#define pwm_dbg_printf //os_printf
struct pwm_single_param {
uint16 gpio_set;
uint16 gpio_clear;
uint32 h_time;
};
struct pwm_param pwm;
LOCAL bool update_flg = 0; //update finished flag
LOCAL bool init_flg = 0; //first update flag
LOCAL uint8 pwm_chn_num = 0;
LOCAL uint8 pwm_out_io_num[8] = {0}; //each channel gpio number
LOCAL bool pwm_stop_flag = 0; //start/stop frc1 timer
LOCAL struct pwm_single_param local_single[8 + 1]; //local_single param, on-changing
LOCAL uint8 local_channel = 0; //local_channel value
LOCAL struct pwm_single_param run_pwm_single[2][8 + 1]; //running para, two sets
LOCAL uint8 run_pwm_channel[2];
LOCAL uint8 run_pwm_toggle = 0;
LOCAL struct pwm_single_param *pwm_single;
LOCAL uint8 pwm_channel;
LOCAL uint8 pwm_current_channel = 0; //current pwm channel in pwm_tim1_intr_handler
LOCAL uint16 pwm_gpio = 0; //all pwm gpio bits
static u32 last_gpio_hdl_frc1_tick = 0x7fffff;
//XXX: 0xffffffff/(80000000/16)=35A
//IMPORTANT: t mus be u32 type. u16, 450~800, err!!!!!!!!!!!
#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)
#define FRC1_ENABLE_TIMER BIT7
//TIMER PREDIVED MODE
typedef enum {
DIVDED_BY_1 = 0, //timer clock
DIVDED_BY_16 = 4, //divided by 16
DIVDED_BY_256 = 8, //divided by 256
} TIMER_PREDIVED_MODE;
typedef enum { //timer interrupt mode
TM_LEVEL_INT = 1, // level interrupt
TM_EDGE_INT = 0, //edge interrupt
} TIMER_INT_MODE;
// sort all channels' h_time,small to big
LOCAL 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;
os_memcpy(&tmp, &pwm[i], sizeof(struct pwm_single_param));
os_memcpy(&pwm[i], &pwm[i - 1], sizeof(struct pwm_single_param));
while (tmp.h_time < pwm[j].h_time) {
os_memcpy(&pwm[j + 1], &pwm[j], sizeof(struct pwm_single_param));
j--;
if (j < 0) {
break;
}
}
os_memcpy(&pwm[j + 1], &tmp, sizeof(struct pwm_single_param));
}
}
}
#define LOW_LIMIT (6)
void ICACHE_FLASH_ATTR
pwm_start(void)
{
uint8 i, j;
// step 1: init PWM_CHANNEL+1 channels param
for (i = 0; i < pwm_chn_num; i++)
{
//uint32 us = pwm.period * pwm.duty[i] / PWM_DEPTH; //calc single channel us time
//local_single[i].h_time = US_TO_RTC_TIMER_TICKS(us); //calc h_time to write FRC1_LOAD_ADDRESS
local_single[i].h_time = (pwm.period * pwm.duty[i]*5)/PWM_DEPTH; //calc h_time to write FRC1_LOAD_ADDRESS
if(local_single[i].h_time<LOW_LIMIT)
local_single[i].h_time = LOW_LIMIT;
local_single[i].gpio_set = 0; //don't set gpio
local_single[i].gpio_clear = 1 << pwm_out_io_num[i]; //clear single channel gpio
}
local_single[pwm_chn_num].h_time = US_TO_RTC_TIMER_TICKS(pwm.period); //calc pwm.period channel us time
local_single[pwm_chn_num].gpio_set = pwm_gpio; //set all channels' gpio
local_single[pwm_chn_num].gpio_clear = 0; //don't clear gpio
pwm_dbg_printf("init\n");
for (i = 0; i < 4; i++)
{
pwm_dbg_printf("set:%x ",local_single[i].gpio_set);
pwm_dbg_printf("clr:%x ",local_single[i].gpio_clear);
pwm_dbg_printf("htm:%u ",local_single[i].h_time);
pwm_dbg_printf("\n");
}
// step 2: sort, small to big
pwm_insert_sort(local_single, pwm_chn_num + 1); //time sort small to big,
local_channel = pwm_chn_num + 1; //local channel number is PWM_CHANNEL+1
pwm_dbg_printf("sort\n");
for (i = 0; i < local_channel; i++)
{
pwm_dbg_printf("set:%x ",local_single[i].gpio_set);
pwm_dbg_printf("clr:%x ",local_single[i].gpio_clear);
pwm_dbg_printf("htm:%u ",local_single[i].h_time);
pwm_dbg_printf("\n");
}
// step 2.5: low limit of duty diff is 6.
for (i =1 ; i <= pwm_chn_num; i++)
{
u32 tmp = local_single[i].h_time - local_single[i - 1].h_time;
if ( tmp < 6 )
{
if ( tmp < 3 )
local_single[i].h_time = local_single[i - 1].h_time;
else
local_single[i].h_time = local_single[i - 1].h_time+6;
}
}
pwm_dbg_printf("remove low limit\n");
for (i = 0; i < local_channel; i++)
{
pwm_dbg_printf("set:%x ",local_single[i].gpio_set);
pwm_dbg_printf("clr:%x ",local_single[i].gpio_clear);
pwm_dbg_printf("htm:%u ",local_single[i].h_time);
pwm_dbg_printf("\n");
}
// step 3: combine same duty channels
for (i = pwm_chn_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;
//copy channel j param to channel j-1 param
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_printf("combine\n");
for (i = 0; i < local_channel; i++)
{
pwm_dbg_printf("set:%x ",local_single[i].gpio_set);
pwm_dbg_printf("clr:%x ",local_single[i].gpio_clear);
pwm_dbg_printf("htm:%u ",local_single[i].h_time);
pwm_dbg_printf("\n");
}
// step 4: calc 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;
//copy channel i param to channel i-1 param
for (i = 1; i < local_channel; i++)
{
os_memcpy(&local_single[i - 1], &local_single[i], sizeof(struct pwm_single_param));
}
local_channel--;
}
//local OR saved ?
pwm_dbg_printf("remove 1st 0\n");
for (i = 0; i < local_channel; i++)
{
pwm_dbg_printf("set:%x ",local_single[i].gpio_set);
pwm_dbg_printf("clr:%x ",local_single[i].gpio_clear);
pwm_dbg_printf("htm:%u ",local_single[i].h_time);
pwm_dbg_printf("\n");
}