This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C123GH6PM: How to use the PWM timer output for a led strip driver?

Part Number: TM4C123GH6PM

I like to write a driver for the parallax ws2812b led stripe. The D_in signal line is used to send the led colour information bit wise. There are 3 possible High-Low pattern:

// single line protocol
// GRB @ 8Bit
// MSB first LSB last send
// led_x = <8Bit green><8Bit red><8Bit blue>
// 1 bit:
//    - [0] T0H, T0L
//    - [1] T1H, T1L
// timings:
//    T0H = 1 for 0.4us
//    T0L = 0 for 0.8us
//    T1H = 1 for 0.45us
//    T1L = 0 for 0.85us
//
// reset:
// H -> L for 50us

I like to realize that through the PF4 pin. So I can use the PWM but I may use the T2CCP0 pin function.

The led strip has 30 rgb leds. I have enough memory. So I can easily allocate 24 * 30 = 720Bytes. Each byte represents the pwm pattern.

Ideally I like to use the DMA to transfer the data in the background.

How I have to setup ?Timer2? to generate different pwms? What is your advice on that? Maybe only pin toggling is better?

  • HI,

      I think you can do it either using PWM or bit-bang (pin toggling). PWM gives you more control than bit-bang. Each time one PWM is generated, you generate an interrupt and in the ISR you can load a different PWM duty cycle per your ws2812B requirement. If you use bit-bang you need to create software delay to realize the different high and low phase of the signal. If you have other functions going on like an interrupt, your software delay will then be off. I will go with PWM method. 

  • I ended up with the PWM solution. So thanks for the advice.

    I like to optimize a bit. Is it possible to use the DMA controller to update the timer match register? My current code is copying the match byte to the register manually at the isr. For better understanding a code listing

    static void ws2812b_timer2_A_16_32_isr(void) {
    	HWREG(TIMER2_BASE + TIMER_O_ICR) = HWREG(TIMER2_BASE + TIMER_O_MIS); //TimerIntClear(TIMER0_BASE, TimerIntStatus(TIMER0_BASE,1));
    
    	if(ws2812b.idx >= LED_BYTE_NUM){
    		TimerDisable(TIMER2_BASE, TIMER_A);
    		GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_4);
    		GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_4, 0);
    		return;
    	}
    
    	HWREG(TIMER2_BASE + TIMER_O_TAMATCHR) = ws2812b.ledBytes[ws2812b.idx];
    	ws2812b.idx++;
    }
    
    void init(void) {
    
    	const uint32_t Period = 100 ; //800Khz - 1.25uS
    
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    	SysCtlDelay(3);
    
    	GPIOPinConfigure(GPIO_PF4_T2CCP0);
    	GPIOPinTypeTimer(GPIO_PORTF_BASE, GPIO_PIN_4);
    
    	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
    	SysCtlDelay(3);
    	TimerConfigure(TIMER2_BASE, TIMER_CFG_SPLIT_PAIR|TIMER_CFG_A_PWM);
    	TimerLoadSet(TIMER2_BASE, TIMER_A, Period -1);
    
    	//This should make so the match register value only updates after a timeout (after the end of the PWM cycle)
    	HWREG(TIMER2_BASE+TIMER_TAMR_TAMRSU) =1;
    
    	TimerControlEvent(TIMER2_BASE,TIMER_A,TIMER_EVENT_POS_EDGE);
    	TimerIntEnable(TIMER2_BASE,TIMER_CAPA_EVENT);
    }
    


  • Hi Stefan,

      Yes, it is possible. Please refer to the datasheet for details. I think you need to store the different PWM duty cycles to control the LED in a buffer first. Each time a PWM period expires, it generates an uDMA request to uDMA module. uDMA will then transfer the duty cycle value to the match register one at a time. 

    11.3.5 DMA Operation
    The timers each have a dedicated ìDMA channel and can provide a request signal to the ìDMA
    controller. The request is a burst type and occurs whenever a timer raw interrupt condition occurs.
    The arbitration size of the ìDMA transfer should be set to the amount of data that should be
    transferred whenever a timer event occurs.
    For example, to transfer 256 items, 8 items at a time every 10 ms, configure a timer to generate a
    periodic timeout at 10 ms. Configure the ìDMA transfer for a total of 256 items, with a burst size of
    8 items. Each time the timer times out, the ìDMA controller transfers 8 items, until all 256 items
    have been transferred.
    No other special steps are needed to enable Timers for ìDMA operation. Refer to “Micro Direct
    Memory Access (ìDMA)” on page 585 for more details about programming the ìDMA controller.