chipKIT® Development Platform

Inspired by Arduino™

Inizialize 2 pwm at different frequencies

Created Fri, 16 Aug 2013 17:54:23 +0000 by Pentium


Pentium

Fri, 16 Aug 2013 17:54:23 +0000

Hi there, I would like to generate two different pwm signals, at two different frequecies so one will be 6 times more than the other. I' ve seen a great page in the wiki so I discovered about timer2 and 3. I' ve written a analogWrite2 function which works pretty nice using the other output comparator and timer3. Here the code:

//*********************************************************************
void analogWrite2(uint8_t pin, int val, unsigned int timerPeriod, unsigned short preScaler)
{
	uint16_t	timer;
	uint8_t		pwm_mask;
	p32_oc *	ocp;

	/* Check if pin number is in valid range.
	*/
	if (pin >= NUM_DIGITAL_PINS_EXTENDED)
	{
		return 0;
	}

#if (OPT_BOARD_ANALOG_WRITE != 0)
	/* Peform any board specific processing.
	*/
int	_board_analogWrite(uint8_t pin, int val);

	if (_board_analogWrite(pin, val) != 0)
	{
		return;
	}
#endif		// OPT_BOARD_ANALOG_WRITE

	/* Determine if this is actually a PWM capable pin or not.
	** The value in timer will be the output compare number associated with
	** the pin, or NOT_ON_TIMER if no OC is connected to the pin.
	** The values 0 or >=255 have the side effect of turning off PWM on
	** pins that are PWM capable.
	*/
	timer = digitalPinToTimerOC(pin) >> _BN_TIMER_OC;
	
	if ((timer == NOT_ON_TIMER) || (val == 0) || (val >= 110000))
	{
		/* We're going to be setting the pin to a steady state.
		** Make sure it is set as a digital output. And then set
		** it LOW or HIGH depending on the value requested to be
		** written. The digitalWrite function has the side effect
		** of turning off PWM on the pin if it happens to be a
		** PWM capable pin.
		*/
		pinMode(pin, OUTPUT);
	    if (val < 128)
	    {
	        digitalWrite(pin, LOW);
	    }
	    else
	    {
	        digitalWrite(pin, HIGH);
	    }
	}

	else
	{
		/* It's a PWM capable pin. Timer 3 is used for the time base
		** for analog output, so if no PWM are currently active then
		** Timer 3 needs to be initialized
		*/
	    if (pwm_active2 == 0)
	    {
			switch(preScaler)
			{
				case 1:
				T3CON = TBCON_PS_1;
				break;
				
				case 2:
				T3CON = TBCON_PS_2;
				break;
				
				case 4:
				T3CON = TBCON_PS_4;
				break;
				
				case 8:
				T3CON = TBCON_PS_8;
				break;

				case 16:
				T3CON = TBCON_PS_16;
				break;

				case 32:
				T3CON = TBCON_PS_32;
				break;

				case 64:
				T3CON = TBCON_PS_64;
				break;

				case 256:
				T3CON = TBCON_PS_256;
				break;
				
				default:
				T3CON = TBCON_PS_256;
				break;
			}			
			TMR3 = 0;
			PR3 = timerPeriod;
			T3CONSET = TBCON_ON;
	    }

		/* Generate bit mask for this output compare.
		*/
		pwm_mask = (1 << (timer - (_TIMER_OC2 >> _BN_TIMER_OC)));

		/* Obtain a pointer to the output compare being being used
		** NOTE: as of 11/15/2011 All existing PIC32 devices
		** (PIC32MX1XX/2XX/3XX/4XX/5XX/6XX/7XX) have the output compares
		** in consecutive locations. The base address is _OCMP1_BASE_ADDRESS
		** and the distance between their addresses is 0x200.
		*/
		ocp = (p32_oc *)(_OCMP2_BASE_ADDRESS + (0x200 * (timer - (_TIMER_OC2 >> _BN_TIMER_OC))));

		/* If the requested PWM isn't active, init its output compare. Enabling
		** the output compare takes over control of pin direction and forces the
		** pin to be an output.
		*/
		int dutyDiv= pow(2, (int)log2(timerPeriod));
		if ((pwm_active2 & pwm_mask) == 0) 
		{
#if defined(__PIC32MX1XX__) || defined(__PIC32MX2XX__)
			volatile uint32_t *	pps;
				
			/* On devices with peripheral pin select, it is necessary to connect
			** the output compare to the pin.
			*/
			pps = ppsOutputRegister(timerOCtoDigitalPin(timer));
			*pps = ppsOutputSelect(timerOCtoOutputSelect(timer));
#endif
			ocp->ocxR.reg   = ((timerPeriod*val)/dutyDiv);
			ocp->ocxCon.reg = OCCON_SRC_TIMER3 | OCCON_PWM_FAULT_DISABLE;
			ocp->ocxCon.set = OCCON_ON;

	        pwm_active2 |= pwm_mask;
	    }

		/* Set the duty cycle register for the requested output compare
		*/
		ocp->ocxRs.reg = ((timerPeriod*val)/dutyDiv);
	}
}

Basicly I put the pwm timer period arg (which is cpuclock/prescaler/desidered frequency) and the prescaler (for timer 2 are 1 8 16 32 64 128 256) and I get a pretty clean pwm signal with a dutyDiv resolution. So for example...

FQR: 10 kHz with 1:1 prescaler I think should be 12-bit resolution.

I' m trying to generate two phased PWM, one is generated by my function (and it works fine, with my scope I can see it' s stable) the other one doesn' t! I tried to use both tone (which uses timer1) and standard analogwrite (editing the PR2 period) but they won' t at all to be phased. Can anyone help me? Thank you, regards

p.s. if you find some errors in my code please comments!