11

I am using STM32F2 controller and I am interfacing with an ST7036 LCD display via 8 bit parallel interface.

The datasheet says there should be a 20 nano second delay between address hold and setup time.

How do I generate a 20 nanosecond delay in C?

Turn
  • 6,656
  • 32
  • 41
pranathi
  • 383
  • 4
  • 6
  • 16
  • Have you tried the `nanosleep()` function ? Note : you need to include ``to use it. –  Nov 14 '12 at 12:56
  • You do not need to make ns delays. These are min delays by datasheet, but you can make more. Also, why don't you want to use SPI or I2C? That is much more simple and you can send data in one packet. So you will free up controller for other tasks. – Bulkin Feb 02 '18 at 07:46

3 Answers3

15

Use stopwatch_delay(4) below to accomplish approximately 24ns of delay. It uses the STM32's DWT_CYCCNT register, which is specifically designed to count actual clock ticks, located at address 0xE0001004.

To verify the delay accuracy (see main), you can call STOPWATCH_START, run stopwatch_delay(ticks), then call STOPWATCH_STOP and verify with CalcNanosecondsFromStopwatch(m_nStart, m_nStop). Adjust ticks as needed.

uint32_t m_nStart;               //DEBUG Stopwatch start cycle counter value
uint32_t m_nStop;                //DEBUG Stopwatch stop cycle counter value

#define DEMCR_TRCENA    0x01000000

/* Core Debug registers */
#define DEMCR           (*((volatile uint32_t *)0xE000EDFC))
#define DWT_CTRL        (*(volatile uint32_t *)0xe0001000)
#define CYCCNTENA       (1<<0)
#define DWT_CYCCNT      ((volatile uint32_t *)0xE0001004)
#define CPU_CYCLES      *DWT_CYCCNT
#define CLK_SPEED         168000000 // EXAMPLE for CortexM4, EDIT as needed

#define STOPWATCH_START { m_nStart = *((volatile unsigned int *)0xE0001004);}
#define STOPWATCH_STOP  { m_nStop = *((volatile unsigned int *)0xE0001004);}


static inline void stopwatch_reset(void)
{
    /* Enable DWT */
    DEMCR |= DEMCR_TRCENA; 
    *DWT_CYCCNT = 0;             
    /* Enable CPU cycle counter */
    DWT_CTRL |= CYCCNTENA;
}

static inline uint32_t stopwatch_getticks()
{
    return CPU_CYCLES;
}

static inline void stopwatch_delay(uint32_t ticks)
{
    uint32_t end_ticks = ticks + stopwatch_getticks();
    while(1)
    {
            if (stopwatch_getticks() >= end_ticks)
                    break;
    }
}

uint32_t CalcNanosecondsFromStopwatch(uint32_t nStart, uint32_t nStop)
{
    uint32_t nDiffTicks;
    uint32_t nSystemCoreTicksPerMicrosec;

    // Convert (clk speed per sec) to (clk speed per microsec)
    nSystemCoreTicksPerMicrosec = CLK_SPEED / 1000000;

    // Elapsed ticks
    nDiffTicks = nStop - nStart;

    // Elapsed nanosec = 1000 * (ticks-elapsed / clock-ticks in a microsec)
    return 1000 * nDiffTicks / nSystemCoreTicksPerMicrosec;
} 

void main(void)
{
    int timeDiff = 0;
    stopwatch_reset();

    // =============================================
    // Example: use a delay, and measure how long it took
    STOPWATCH_START;
    stopwatch_delay(168000); // 168k ticks is 1ms for 168MHz core
    STOPWATCH_STOP;

    timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
    printf("My delay measured to be %d nanoseconds\n", timeDiff);

    // =============================================
    // Example: measure function duration in nanosec
    STOPWATCH_START;
    // run_my_function() => do something here
    STOPWATCH_STOP;

    timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
    printf("My function took %d nanoseconds\n", timeDiff);
}
bunkerdive
  • 2,031
  • 1
  • 25
  • 28
  • 1
    Are you sure this would work? An instruction cycle would be around 5ns..Obviously the code uses more than 5 instructions. So the minimum time would be 25ns... The delay used in hardware, however, could be much less than 25ns. – richieqianle Jul 31 '14 at 02:16
  • Yep. The code is to be modified as needed. One could surely just use the minimum of pieces needed, or ideally a user of this code would run `__no_operation()` one thousand times in a loop inside `main()` (e.g. where `run_my_function()` is) to get the nano-second stopwatch for 1000 runs, and then just divide that number by 1000 to see how long one single pipelined `__no_operation()` call takes on the system in question...and then use as desired. – bunkerdive Jul 31 '14 at 02:50
  • 1
    Just a comment, 1000 NOP/1000 may not be equal to 1 NOP. Great explanation anyway! – richieqianle Jul 31 '14 at 03:56
  • True, it only approximates a **pipelined** NOP. So yea, the fewer NOPs used, the more the measurement will drift from reality (to some small degree). – bunkerdive Jul 31 '14 at 14:05
  • 1
    Some remarks: 1) if `CPU_CYCLES` is a free-running timer, this will not work when it gets near `0xFFFFFFFF`, because `end_ticks` will overflow and then `stopwatch_getticks() >= end_ticks` will immediately exit. 2) `1000 * nDiffTicks` will overflow if you leave the stopwatch running for more than `26ms` (if I calculated correctly). 3) It's unclear why your `STOPWATCH_START`/`STOPWATCH_STOP` macros don't use `DWT_CYCCNT` since it's already defined. 4) For a general solution, `stopwatch_reset` is a bad idea since it prevents you from using the stopwatch from multiple interrupts. – vgru Dec 02 '19 at 13:24
9

The first specification I found of Stm32f2 assumes a clock frequency of 120 MHz. That's about 8ns per clock cycle. You would need about three single cycle instructions between successive write or read/write operations. In C, a++; will probably do (if a is located in stack).

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
  • Yeah - exactly - all the answers but this one give solutions that would take 100x more time than required... 20ns is just a few cycles, a few NOPs in assembly will be more than adequate... – Freddie Chopin Nov 23 '14 at 19:32
  • Wouldn't it be good to verify the delay using the cycle count register specifically designed for that purpose though, regardless of what delay method is used? Otherwise, I guess it could be verified with an oscilloscope and some digital pins. – bunkerdive Apr 21 '17 at 04:17
  • The `stopwatch_delay()` function above accomplishes this perfectly for me, and can be verified or used for different delay lengths. – bunkerdive Apr 21 '17 at 04:31
1

You should look into the FSMC peripheral available in your chip. While the configuration might be complicated, especially if you're not dropping in a memory part that it was designed for, you might find that your parallel interfaced device maps pretty well to one of the memory interface modes.

These sorts of external memory controllers must have a bunch of configurable timing options to support the range of different memory chips out there so you'll be able to guarantee the timings required by your datasheet.

The nice benefit of being able to do this is your LCD will then seem like any old memory mapped peripheral, abstracting away the lower level interfacing details.

asund
  • 11
  • 1