Generating 44KHz in ROBOTC

This code is really for Kevin, but others may be interested in how it was done.

Disclaimer, this code uses direct access to the cortex registers and has the potential to cause damage if modified without care.

So the general idea is to use a timer to generate a square wave output on one of the digital ports. I’m using timer 8 here as it “appears” to be unused, even though it is enabled by default. I would use timer 1 but it may be used internally to ROBOTC for something. Timer 1 is enabled and is counting uS (although they configured it wrong and it actually counts every 0.9863 uS), it also has an interrupt enabled which causes an interrupt to the cpu every 65.536mS, so anyway I’m avoiding timer 1 in ROBOTC for now.

I set timer 8 to count at it’s maximum frequency (72MHz) and set auto reload for 1636 counts (ie. at a 44KHz rate). I then use the compare register 1 to generate a 50% duty cycle waveform that is then enabled to be output from digital port 3.

I also had a play to see if an accurate uS timer could be created, well, it’s not that good so if you were hoping to create a sort of IR remote waveform that may not work.

Here is a scope trace showing the 44KHz square wave.

[ATTACH]7764[/ATTACH]

and my quick and dirty attempt to modulate that.

[ATTACH]7763[/ATTACH]

I just enabled or disabled the timer to modulate it, there are better ways but I’m feeling tired and lazy today.

Here is the C code.

#pragma config(Sensor, dgtl12, ,               sensorDigitalOut)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

#include "digitalPwm.h"

#define FREQ_PERIOD     1636    // 44KHz time

/*-----------------------------------------------------------------------------*/
/* use timer_1, which is counting in uS (well almost) as a delay               */
/*-----------------------------------------------------------------------------*/

void
wait1usec( int time )
{
    TIM_TypeDef  *TIM1  = _TIM1;
    
    long    a = TIM1->CNT;
    long    dur;
    
    // fixed overhead for calling this function
    // needs tuning
    time = time - 50;
    if(time < 0)
        return;
    
    hogCPU();
    while(1) {
        // get latest time
        dur = TIM1->CNT - a;
        // timer will overflow at 0xFFFF
        if(dur < 0 )
            dur += 0x10000;
        
        if(dur > time)        
            {
            releaseCPU();
            return;
            }
        }
}

/*-----------------------------------------------------------------------------*/
/*  Enable digital output to be Alternate function                             */
/*                                                                             */
/*  CFG  = 10, Alternate output Push-Pull.                                     */
/*  MODE = 01, Output 10MHz                                                    */
/*-----------------------------------------------------------------------------*/

void
digitalWaveEnable( int channel )
{
    uint32_t    gpio_tmp;
    uint32_t    mask, data;
    GPIO_TypeDef *GPIOC = _GPIOC;
    TIM_TypeDef *TIM8   = _TIM8;

    switch( channel )
        {
        case    dgtl3:
            TIM8->CCR1  = FREQ_PERIOD/2;
            mask = GPIO_CRL_MODE6  | GPIO_CRL_CNF6;
            data = GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6_0;
            break;
        default:
            return; // error
            break;
        }

    // digital output becomes alternate function output
    gpio_tmp = GPIOC->CRL;
    gpio_tmp &= ~mask;
    gpio_tmp |= data;
    GPIOC->CRL = gpio_tmp;
}


/*-----------------------------------------------------------------------------*/
/*  Initialize Timer 8 for 44KHz generation on digital channel 3               */
/*-----------------------------------------------------------------------------*/
void
digitalWaveInit( int channel )
{
    // Access low level registers
    RCC_TypeDef  *RCC   = _RCC;
    TIM_TypeDef  *TIM8  = _TIM8;

    // only init once (after power on)
    if( TIM8->CR1 != (TIM_CR1_ARPE | TIM_CR1_CEN) )
        {
        writeDebugStreamLine( "Timer 8 Initialize" );

        // enable timer clock
        RCC->APB2ENR |= RCC_APB2ENR_TIM8EN;
        // reset timer
        RCC->APB2RSTR |= RCC_APB2RSTR_TIM8RST;                                                  \
        RCC->APB2RSTR = 0;                                                        \

        // Disable
        TIM8->CR1  = 0;         // Timer disabled.
        TIM8->DIER = 0;         // All IRQs disabled.
        TIM8->SR   = 0;         // Clear eventual pending IRQs.

        TIM8->PSC  = 0;             // +1 for div, this sets 72MHz clock
        TIM8->ARR  = FREQ_PERIOD;   // Reload every FREQ_PERIOD cycles
        TIM8->CR2  = 0;

        // Set compare mode registers
        TIM8->CCMR1 = TIM_CCMR1_OC1PE | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;
        
        TIM8->CCMR2 = 0;

        // enable compare
        TIM8->CCER = TIM_CCER_CC1E;

        // Set compare reg value to default 50% duty cycle
        TIM8->CCR1  = FREQ_PERIOD/2;
        TIM8->CCR2  = 0;
        TIM8->CCR3  = 0;
        TIM8->CCR4  = 0;

        // enable timer
        TIM8->CR1   = TIM_CR1_ARPE | TIM_CR1_CEN;

        // enable outputs
        TIM8->BDTR  = TIM_BDTR_MOE;
        }

    // Now enable pwm on this channel
    digitalWaveEnable( channel );
}


task main()
{
   TIM_TypeDef  *TIM8  = _TIM8;
   digitalWaveInit( dgtl3 );

    while(1)
        {
        SensorValue dgtl12 ] = 1;
        TIM8->CR1 |= TIM_CR1_CEN;
        wait1usec( 640 );
        
        TIM8->CR1 &= ~TIM_CR1_CEN;
        SensorValue dgtl12 ] = 0;
        wait1usec( 200 );
        
        wait1Msec(1);
        }
}

I will send you the header file with all the register definitions, the code is no use to anyone without that so I decided not to post here.
wave_44k_1.jpg
wave_44k_2.jpg

1 Like

Thanks James,

As I reviewed my project, I really need to modulate at 56Khz which I believe would required a reload value of 1286 if I got the math right.

Cheers Kb

1 Like