/*
	BL17PWM2.C
	
	Use with BL1700 boards.
		
	This program demonstrates how to use all seven outputs using the
	DMA-driven PWM.

	Instructions:
	Connect Bank B outputs, 0-6, to LEDs or some device to get a visual.
	Install jumper on J8, pins 4-6.
	Compile and run this program.
*/

#define RUNKERNEL 1
#define _PWMDMAPort _OUT_BANKS
#use vdriver.lib
#use srtk.lib
#use eziobl17.lib

#define NUMCHAN 7
#define CLKRATE 2
#define PWMLEN	14
#define PWMOVR	14

#define PWM_0OFF		0x00
#define PWM_0ON		0x01
#define PWM_1OFF		0x02
#define PWM_1ON		0x03
#define PWM_2OFF		0x04
#define PWM_2ON		0x05
#define PWM_3OFF		0x06
#define PWM_3ON		0x07
#define PWM_4OFF		0x08
#define PWM_4ON		0x09
#define PWM_5OFF		0x0A
#define PWM_5ON		0x0B
#define PWM_6OFF		0x0C
#define PWM_6ON		0x0D
#define PWM_NOOP		0x0E

#define PWMWVLEN 64

char PWMBuffer[2][(PWMLEN+PWMOVR+1)*256];

struct _pwmTask {
	//	information particular to each PWM channel
	char onChar, offChar;	//	values to send to enable and disable the output
	char offset;				//	offset of first byte from the buffer
	char dim;					//	on duty cycle
	int sign;					//	to increase or decrease the duty cycle
	CoData cd;					//	codata associated with the PWM control
	unsigned long peakTime;	//	
} pwmTask[NUMCHAN];

#funcchain _srtk_hightask _eioBrdAORf

void pwmStep(char *pBufStart, struct _pwmTask *pT) {
	auto CoData *pCD;

	pCD = &pT->cd;
	costate pCD always_on {
		if (pT->dim != PWMWVLEN)	//	if not on all the time
			dmapwmSetBuf(pBufStart+pT->offset+(pT->dim%PWMWVLEN)*NUMCHAN,	//	offset to first byte
				PWMLEN+PWMOVR,
				PWMWVLEN*NUMCHAN,
				(!pT->dim)?pT->offChar:PWM_NOOP);
		pT->dim += pT->sign;
		if (pT->dim == 0) {
			pT->sign = 1;
			pT->peakTime = MS_TIMER;
		}
		else if (pT->dim == PWMWVLEN) pT->sign = -1;
		dmapwmSetBuf(pBufStart+pT->offset+(pT->dim%PWMWVLEN)*NUMCHAN,
			PWMLEN+PWMOVR,
			PWMWVLEN*NUMCHAN,
			(pT->dim != PWMWVLEN)?pT->onChar:pT->offChar);
		waitfor(IntervalMs((PWMWVLEN-pT->dim)/CLKRATE));
	}
}

void initPWM(
	unsigned i,
	char onChar,
	char offChar,
	char offset,
	char dim,
	int sign,
	char *pBufStart,
	char step) {
	pwmTask[i].onChar = onChar;
	pwmTask[i].offChar = offChar;
	pwmTask[i].offset = offset;
	pwmTask[i].dim = dim;
	pwmTask[i].sign = sign;
	CoBegin(&pwmTask[i].cd);
	dmapwmSetBuf(pBufStart+offset,PWMLEN+PWMOVR,PWMWVLEN*NUMCHAN,offChar);
	if (dim != PWMWVLEN)
		dmapwmSetBuf(pBufStart+offset+NUMCHAN*dim,PWMLEN+PWMOVR,PWMWVLEN*NUMCHAN,onChar);
}

main() {
	auto char dim;
	auto char *pBufStart;
	auto int sign;
	auto int activeBuf;
	auto int i;

	pBufStart = dmapwmBufBeg(PWMBuffer[0]);

	memset(PWMBuffer[0],PWM_NOOP,sizeof(PWMBuffer[0]));
	activeBuf = 0;
	for (i = 0; i < 2; ++i) {
		initPWM(0,PWM_0ON,PWM_0OFF,0,64,-1,dmapwmBufBeg(PWMBuffer[i]),NUMCHAN);	
		initPWM(1,PWM_1ON,PWM_1OFF,1,20,1,dmapwmBufBeg(PWMBuffer[i]),NUMCHAN);	
		initPWM(2,PWM_2ON,PWM_2OFF,2,0,1,dmapwmBufBeg(PWMBuffer[i]),NUMCHAN);
		initPWM(3,PWM_3ON,PWM_3OFF,3,64,-1,dmapwmBufBeg(PWMBuffer[i]),NUMCHAN);	
		initPWM(4,PWM_4ON,PWM_4OFF,4,20,1,dmapwmBufBeg(PWMBuffer[i]),NUMCHAN);	
		initPWM(5,PWM_5ON,PWM_5OFF,5,0,1,dmapwmBufBeg(PWMBuffer[i]),NUMCHAN);
		initPWM(6,PWM_6ON,PWM_6OFF,6,64,-1,dmapwmBufBeg(PWMBuffer[i]),NUMCHAN);	
	}
	VdInit();
	init_srtkernel();
	dmapwmInit((unsigned)((xmadr(PWMBuffer[0])+255)>>8),
		PWMLEN,PWMOVR,_PWMDMAPort,CLKRATE);
	while (inport(BCR0H)) {
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[0]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[1]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[2]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[3]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[4]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[5]);
		pwmStep(dmapwmBufBeg(PWMBuffer[!activeBuf]),&pwmTask[6]);
		dmapwmSwBuf((unsigned)((xmadr(PWMBuffer[!activeBuf])+255) >> 8));
		memcpy(PWMBuffer[activeBuf],PWMBuffer[!activeBuf],sizeof(PWMBuffer[0]));
		activeBuf = !activeBuf;
		runwatch();
	}
}