I would like to make a stopwatch by embedded C to program on ST STM32F407VG.
I know how I should write a clock program to make a hundredth of sec,but I know it will not be accurate,when I use a delay of 10ms.
what should I do
While the STM32 is endowed with a number of hardware timers, all Cortex-M devices have a common SYSTICK timer, so the simplest and most portable method is to use that.
The following creates a 1 millisecond period free-running counter msTicks:
#include "stm32f4xx.h"
#include <stdint.h>
static volatile uint32_t msTicks = 0; // Milliseconds
int main(void)
{
SysTick_Config(SystemCoreClock / 1000) ; // 1ms interrupt
for(;;)
{
... // your code here
}
}
// SYSTICK interrupt handler
void SysTick_Handler(void)
{
msTicks++ ;
}
Then your stopwatch can be implemented with two functions thus:
static uint32_t start_time ;
void start( void )
{
start_time = msTicks ;
}
uint32_t getMillisecondsSinceStart( void )
{
return msTicks - start_time ;
}
For example:
bool running = false ; // needs <stdbool.h> included
uint32_t time_millisec = 0 ;
for(;;)
{
if( startButtonPressed() )
{
running = true ;
start() ;
}
if( stopButtonPressed() )
{
running = false ;
}
if( running )
{
time_millisec = getMillisecondsSinceStart() ;
}
displayTime( time_millisec ) ;
}
Accuracy will be entirely dependent on whatever is driving the core clock - normally an external crystal or oscillator. The internal RC oscillator will be less precise - it is factory trimmed to +/- 1%. Your particular part also has a 32KHz RC oscillator for driving the RTC that can be calibrated, but that cannot be used to drive the core clock (systick), and would be less portable, less precise and more complex that the method described above.
The best option is to use the internal timer modules of that microcontroller. About which you can explore in the datasheet.
Related
My TI Tiva ARM program is not working on TM4C123G. Using board EK TM4C123GXL. Program doesn't blink on board RGB LEDs. I am able to run other example program to check the LEDs, this program is from textbook TI TIVA ARM PROGRAMMING FOR EMBEDDED SYSTEMS. Need help to debug this program. Thanks
Code:
/* p2.4.c: Toggling a single LED
/* This program turns on the red LED and toggles the blue LED 0.5 sec on and 0.5 sec off. */
#include "TM4C123GH6PM.h"
void delayMs(int n);
int main(void)
{
/* enable clock to GPIOF at clock gating control register */
SYSCTL->RCGCGPIO |= 0x20;
/* enable the GPIO pins for the LED (PF3, 2 1) as output */
GPIOF->DIR = 0x0E;
/* enable the GPIO pins for digital function */
GPIOF->DEN = 0x0E;
/* turn on red LED only and leave it on */
GPIOF->DATA = 0x02;
while(1)
{
GPIOF->DATA |= 4; /* turn on blue LED */
delayMs(500);
GPIOF->DATA &= ~4; /* turn off blue LED */
delayMs(500);
}
}
/* delay n milliseconds (16 MHz CPU clock) */
void delayMs(int n)
{
int i, j;
for(i = 0 ; i < n; i++)
for(j = 0; j < 3180; j++)
{} /* do nothing for 1 ms */
}
I am using KEIL IDE-Version: µVision V5.36.0.0
Tool Version Numbers:
Toolchain: MDK-Lite Version: 5.36.0.0
Toolchain Path: G:\Keil_v5\ARM\ARMCLANG\Bin
C Compiler: ArmClang.exe V6.16
Assembler: Armasm.exe V6.16
Linker/Locator: ArmLink.exe V6.16
Library Manager: ArmAr.exe V6.16
Hex Converter: FromElf.exe V6.16
CPU DLL: SARMCM3.DLL V5.36.0.0
Dialog DLL: TCM.DLL V1.53.0.0
Target DLL: lmidk-agdi.dll V???
Dialog DLL: TCM.DLL V1.53.0.0
If you must use a counting-loop delay, you must at least declare the control variables volatile to be sure they will not be optimised away:
volatile int i, j;
However it would be far better to avoid implementing delays that rely on instruction cycles and the compiler's code generation. The Cortex-M core has a SYSTICK clock that provides accurate timing that does not rely on the compiler's code generation, changes to the system clock, or porting to different Cortex-M devices.
For example:
volatile uint32_t msTicks = 0 ;
void SysTick_Handler(void)
{
msTicks++;
}
void delayMs( uint32_t n )
{
uint32_t start = msTicks ;
while( msTicks - start < n )
{
// wait
}
}
int main (void)
{
// Init SYSTICK interrupt interval to 1ms
SysTick_Config( SystemCoreClock / 1000 ) ;
...
}
A busy-wait delay has limitations that make it unsuitable for all but the most trivial programs. During the delay the processor is tied up-doing nothing useful. Using the SYSTICK and msTicks defined as above you a better solution might be:
uint32_t blink_interval = 1000u ;
uint32_t next_toggle = msTicks + blink_interval ;
for(;;)
{
// If time to toggle LED ...
if( (int32_t)(msTicks - next_toggle) <= 0 )
{
// Toggle LED and advance toggle time
GPIOF->DATA ^= 4 ;
next_toggle += blink_interval ;
}
// Do other work while blinking LED
...
}
Note also the use of the bitwise-XOR operator to toggle the LED. That could be used to simplify you original loop:
while(1)
{
GPIOF->DATA ^= 4 ;
delayMs( 500 ) ;
}
It looks like some issue with the compiler version optimization. Reverted back to v5.
I have the following code for a task that requires me to use the following code to use the Timer_A0 module in the CCS IDE to control the speeds of the motors for a robot that uses the MSP432P401R launchpad as the control unit. How do I initialize the Timer_A0 module in the C programming code below?:
#include "driverlib.h"
#include "mechrev.h"
/* Define macros and function prototypes if needed */
#define BTN1_PIN GPIO_PORT_P1,GPIO_PIN1
#define BTN2_PIN GPIO_PORT_P1,GPIO_PIN4
#define ENB1_PIN GPIO_PORT_P1,GPIO_PIN6
#define ENB2_PIN GPIO_PORT_P1,GPIO_PIN7
#define PWM1_PIN GPIO_PORT_P2,GPIO_PIN4
#define PWM2_PIN GPIO_PORT_P2,GPIO_PIN5
#define PWM3_PIN GPIO_PORT_P2,GPIO_PIN6
#define PWM4_PIN GPIO_PORT_P2,GPIO_PIN7
#define BMP0_PIN GPIO_PORT_P4,GPIO_PIN0
#define BMP7_PIN GPIO_PORT_P4,GPIO_PIN7
#define BMP2_PIN GPIO_PORT_P4,GPIO_PIN2
#define BMP6_PIN GPIO_PORT_P4,GPIO_PIN6
#define BMP3_PIN GPIO_PORT_P4,GPIO_PIN3
#define BMP5_PIN GPIO_PORT_P4,GPIO_PIN5
#define BTN3_PIN GPIO_PORT_P4, GPIO_PIN0 | GPIO_PIN7
#define BTN4_PIN GPIO_PORT_P4, GPIO_PIN2 | GPIO_PIN6
#define BTN5_PIN GPIO_PORT_P4, GPIO_PIN3 | GPIO_PIN5
/* Define configuration structs if needed */
/* Declare global variables if needed */
int i = 0;
uint32_t counter = 0;
/* Main program */
void main(void)
{
/* Stop Watchdog Timer */
WDT_A_holdTimer();
/* Call the mechrev_setup function included in the mechrev.h header file */
mechrev_setup();
/* Initialize GPIOs P1.1 and P1.4 for PushButtons (S1 and S2 switches) */
MAP_GPIO_setAsInputPinWithPullUpResistor(BTN1_PIN);
MAP_GPIO_setAsInputPinWithPullUpResistor(BTN2_PIN);
/* Initialize GPIOs P1.6 and P1.7 for Motor Driver IC Enable Pins */
MAP_GPIO_setAsOutputPin(ENB1_PIN);
MAP_GPIO_setAsOutputPin(ENB2_PIN);
/* Initialize GPIOs P2.4, P2.5, P2.6 and P2.7 for PWM functionality */
MAP_GPIO_setAsInputPin(PWM1_PIN);
MAP_GPIO_setAsInputPin(PWM2_PIN);
MAP_GPIO_setAsInputPin(PWM3_PIN);
MAP_GPIO_setAsInputPin(PWM4_PIN);
/* Initialize Timer A0 to generate PWM signals */
(Timer_A0 module needs to be initialized here)
/* Declare local variables if needed */
/* Call the initialization grading macro */
MACRO_LAB4_INIT();
while(1)
{
/* Design a Polling process to detect PushButtons press and adjust the PWM duty cycles
accordingly */
if (MAP_GPIO_getInputPinValue(BTN1_PIN) == GPIO_INPUT_PIN_LOW)
{
if(i == 1)
{
TA0CCR1 = 999;
TA0CCR2 = 0;
TA0CCR3 = 999;
TA0CCR4 = 0;
}
if(i == 2)
{
TA0CCR1 = 1998;
TA0CCR2 = 0;
TA0CCR3 = 1998;
TA0CCR4 = 0;
}
if(i == 3)
{
TA0CCR1 = 3000;
TA0CCR2 = 0;
TA0CCR3 = 3000;
TA0CCR4 = 0;
}
for (i=0; i<10000; i++); // switch debouncing
}
else if (MAP_GPIO_getInputPinValue(BTN2_PIN) == GPIO_INPUT_PIN_LOW)
{
if(i == 1)
{
TA0CCR1 = 0;
TA0CCR2 = 999;
TA0CCR3 = 0;
TA0CCR4 = 999;
}
if(i == 2)
{
TA0CCR1 = 0;
TA0CCR2 = 1998;
TA0CCR3 = 0;
TA0CCR4 = 1998;
}
if(i == 3)
{
TA0CCR1 = 0;
TA0CCR2 = 3000;
TA0CCR3 = 0;
TA0CCR4 = 3000;
}
for (i=0; i<10000; i++); // switch debouncing
}
else
{
TA0CCR1 = 0;
TA0CCR2 = 0;
TA0CCR3 = 0;
TA0CCR4 = 0;
}
/* Note: Call the event grading macro after changing PWMs */
MACRO_LAB4_EVENT();
}
}
void PORT4_IRQHandler(void)
{
/* Check the interrupt status */
uint32_t status;
status = MAP_GPIO_getEnabledInterruptStatus(GPIO_PORT_P4);
if (status)
{
if(MAP_GPIO_getInputPinValue(BMP0_PIN) == GPIO_INPUT_PIN_LOW ||
MAP_GPIO_getInputPinValue(BMP7_PIN) == GPIO_INPUT_PIN_LOW)
{
i = 1;
counter++;
}
else if (MAP_GPIO_getInputPinValue(BMP2_PIN) == GPIO_INPUT_PIN_LOW ||
MAP_GPIO_getInputPinValue(BMP6_PIN) == GPIO_INPUT_PIN_LOW)
{
i = 2;
counter++;
}
else if (MAP_GPIO_getInputPinValue(BMP3_PIN) == GPIO_INPUT_PIN_LOW ||
MAP_GPIO_getInputPinValue(BMP5_PIN) == GPIO_INPUT_PIN_LOW)
{
i = 3;
counter++;
}
}
else
{
counter++;
}
MACRO_LAB3_EVENT();
/* Clear the PORT4 interrupt flag */
MAP_GPIO_clearInterruptFlag(GPIO_PORT_P4, status);
}
Before you can configure the timer itself, you need to initialize a struct that you will use to configure the timer (there are slight differences in the values needed for upmode, updownmode, continuous mode). You can download the Driverlib Manual MSP432 DriverLib for MSP432 devices which contains detailed use of the timers.
Another very helpful tutorial is Interrupts, Timers & Debugging
You are mixing both Driverlib names and register-level names in your code. That's fine, just make sure you keep the nomenclature straight and don't confuse register access names with user variables, etc..
Now, to configure your timer Timer_A0, you need to initialize a structure to use configuring the time. An example for configuring the timer in up mode would be:
/* TimerA0 UpMode Configuration Parameter */
const Timer_A_UpModeConfig upConfigTA0 =
{
TIMER_A_CLOCKSOURCE_ACLK, /* ACLK Clock 32 KHz (uint_fast16_t clockSource) */
TIMER_A_CLOCKSOURCE_DIVIDER_1, /* Rollover in 1 sec (uint_fast16_t clockSourceDivider) */
ACLKMAX, /* 32767 ticks (uint_fast16_t timerPeriod) */
TIMER_A_TAIE_INTERRUPT_DISABLE, /* Rollover TAIE (uint_fast16_t timerInterruptEnable_TAIE) */
TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE, /* CCR0 CCR0IE (uint_fast16_t captureCompareInterruptEnable_CCR0_CCIE) */
TIMER_A_DO_CLEAR /* Clear Timer (uint_fast16_t timerClear) */
};
Above the timer is configured to use ACLK (32KHz), but you can use SMCLK as well.
Within your code you will need to configure the timer using the struct above. For example:
/* Configuring TimerA0 for Up Mode */
MAP_Timer_A_configureUpMode (TIMER_A0_BASE, &upConfigTA0);
You can also configure/set any additional timer interrupts you plan to use, e.g.
/* Set Capture/Compre Register 3 */
MAP_Timer_A_setCompareValue (TIMER_A0_BASE, MPUCCR, MPUCCRTICKS);
/* enable CCRn compare interrupt */
MAP_Timer_A_enableCaptureCompareInterrupt (TIMER_A0_BASE, MPUCCR);
At this point you have your timer and interrupts configured and read to use, but the timer is not yet started. Before the timer and interrupts are active, you must start the timer itself. Generally, you do this right before you enter you program loop (the while (1) { ... } loop). That way you can configure other peripherals without having your interrupts starting to fire until you complete all your configuration. After everything is configured, start the timer:
/* Starting the Timer_A0 in up mode (just before while loop) */
MAP_Timer_A_startCounter (TIMER_A0_BASE, TIMER_A_UP_MODE);
With every timer, you have two primary interrupt service routines provided by driverlib. The first handles CCR0 and is named TA0_0_IRQHandler() for Timer_A0 (or TA1_0_IRQHandler for Timer_A1, etc..)). It's declaration is:
/**
* Timer A0 CCR0 Interrupt
*/
void TA0_0_IRQHandler(void)
{
...
}
All other interrupts associated with the timer use TA0_N_IRQHandler(). For instance for capture/compare registers CCR1 - CCR5 and the TAIE rollover interrupt (one clock-cycle after CCR0). It has a declaration of
/**
* Timer A0 Interrupt Handler (remaining interrupts)
*/
void TA0_N_IRQHandler(void)
{
...
}
Since you are looking to control a robot arm, I presume you will be using the timer for PWM control. The tutorial covers that fairly well.
I placed complete example using Timer_A to driver PWM on pastebin at MSP432 PWM Control of Tri-Color LED. That shows the use of upmode to drive PWM.
DO NOT OVERLOAD THE INTERRUPT FUNCTIONS WITH FUNCTION CALLS OR COMPUTATIONALLY INTENSIVE CODE. They are effectively signal-handlers. You want to avoid time consuming processing in the ISR (Interrupt Service Request) functions. Instead, the best approach is to set a flag in the interrupt function to complete processing in your program loop, e.g.
/* Starting the Timer_A0 in up mode */
MAP_Timer_A_startCounter (TIMER_A0_BASE, TIMER_A_UP_MODE);
while (1)
{
if (btn1pressed) { /* respond to button presses by calling button handlers */
btn1_handler();
}
if (btn2pressed) {
btn2_handler();
}
if (getmpudata) { /* retrieve data from MPU9250 */
get_MPU_data();
}
if (getmagdata) { /* retrieve data from AK8963 */
get_MAG_data();
}
...
Above, each of the variable names contained in the if (...) statements are simply bool variables set in the interrupt functions that tell your program it's time to process whatever function they trigger. Processing is done in main() not in the interrupt functions. The reason being that heavy computation in the interrupt function can cause the interrupt to fire again before your processing is complete (your code takes more time than the time between interrupts). That is a sure-fire way to cause your program to crater.
The driverlib user's guide is a goldmine for configuring all the peripherals. See if you can get your timer working and look at the tutorial link I provided above. Let me know if you have further questions.
Simply, I want to implement a delay function using stm32 timers, like the one in AVR microcontrollers "Normal mode". Can anybody help ? I just can't find that in the stm32 datasheet! It only supports PWM, input capture, output compare and one-pulse mode output!
N.B: I forgot to mention that I'm using stm32F401 microcontroller
You have very special timer for this purpose called SysTick. Set it to overflow every 1ms. In its handler
static volatile uint32_t counter;
void SysTick_Handler(void)
{
counter++;
}
inline uint32_t __attribute__((always_inline)) GetCounter(void)
{
return counter;
}
void Dealy(uint32_t ms)
{
uint32_t tickstart = GetCounter();
while((GetCounter() - tickstart) < ms);
}
If you want to be able to delay by periods shorter than milli-seconds
you can set up a timer to auto-reload and use the internal clock.
The internal clock frequency set to the timer is shown in the cube MX
clock tab. On my NUCLEO-F446RE most timers get 84MHz or 42MHz.
Timer 3 gets 42 MHz by default.
So if I set the pre scaler to 42 and the count period to maximum (0xFFFF
if 16 bit) I will have a cycling clock that changes every micro-second.
I can then, using a property of twos complement maths, simply subtract the
old time from the new to get the period.
void wait_us (int16_t delay) {
int16_t t1= htim3.Instance->CNT;
while (( htim3.Instance->CNT - t1 ) < delay) {
asm ("\t nop");
}
}
This is an old trick from PIC18 C coding
Im trying to create an embedded c code to control a dc motor with the PIC32MX460F512L microcontroller. Ive Configured the system clock at 80MHz, and the peripheral clock at 10MHz, Am using Timer 1 for pulsing the PWM with a given duty cycle, and Timer 2 for measuring the motor run time. I have a header file(includes.h) that contains system configuration information eg clock. Ive created most of the functions but some are a bit challenging. For example, initializing the LEDS and the functions for forward, backward movements and stop, I wanted the dc motor to run in forward direction for 4 sec at 70% duty cycle, then stop for 1 sec then reverse for 3 sec at 50% duty cycle and then stop for 1 sec and then forward again for 3 sec at 40% duty cycle, stop for 1 sec and finally forward for 5 sec at 20% duty cycle. Any suggestions for the forward, stop, and reverse functions
#include <stdio.h>
#include <stdlib.h>
#include <includes.h>
void main()
{
// Setting up PIC modules such as Timers, IOs OCs,Interrupts, ...
InitializeIO();
InitializeLEDs();
InitializeTimers();
while(1) {
WaitOnBtn1();
Forward(4.0,70);
Stop(1.0);
Backward(3.0,50);
Stop(2);
Forward(3.0,40);
Stop(1.0);
Backward(2.0,20);
LEDsOFF();
}
return;
}
void InitializeIO(){
TRISAbits.TRISA6 = 1;
TRISAbits.TRISA7 = 1;
TRISGbits.TRISG12 = 0;
TRISGbits.TRISB13 = 0;
LATGbits.LATB12 = 0;
LATGbits.LATB13 = 0;
return;
}
void InitializeLEDs(){
//code to initialize LEDS
}
void InitializeTimers(){
// Initialize Timer1
T1CON = 0x0000; // Set Timer1 Control to zeros
T1CONbits.TCKPS=3; // prescale by 256
T1CONbits.ON = 1; // Turn on Timer
PR1= 0xFFFF; // Period of Timer1 to be full
TMR1 = 0; // Initialize Timer1 to zero
// Initialize Timer2
T2CON = 0;
T2CONbits.TCKPS = 7; // prescale by 256
T2CONbits.T32 = 1; // use 32 bits timer
T2CONbits.ON = 1;
PR2 = 0xFFFFFFFF; // Period is set for 32 bits
TMR2 = 0;
}
void WaitOnBtn1(){
// wait on Btn1 indefinitely
while(PORTAbits.RA6 == 0);
// Turn On LED1 indicating it is Btn1 is Pushed
LATBbits.LATB10 = 1;
return;
}
void Forward(float Sec, int D){
int RunTime = (int)(Sec*39000); // convert the total
time to number of Tics
TMR2 = 0;
//LEDs
LATGbits.LATG12 = 1; // forward Direction
LATBbits.LATB12 = 0;
LATBbits.LATB13 = 0;
LATBbits.LATB11 = 1;
// Keep on firing the PWM as long as Run time is not
elapsed
while (TMR2 < RunTime){
PWM(D);
}
return;
}
void PWM(int D){
TMR1 = 0;
int Period = 400;
while (TMR1< Period) {
if (TMR1 < Period*D/100){
LATGbits.LATG13 = 1;
}
else{
LATGbits.LATG13 = 0;
}
}
Functions, not methods, to be precise.
So what is exactly the question?
What I can say from a quick look on a source code:
LEDs initialisation should be done as you did in InitializeIO() function. Simply set proper TRISx bits to 0 to configure LED pins as output.
For the PWM and motor control functions you should take some time and try to understand how builtin PWM peripheral works. It is a part of OC (Output Compare) and it is very easy to use. Please, take look on following link http://ww1.microchip.com/downloads/en/DeviceDoc/61111E.pdf
and this one for the minimal implementation using builtin peripheral libraries https://electronics.stackexchange.com/questions/69232/pic32-pwm-minimal-example
Basically you need to set up OC registers to "make" OC module acts like PWM. You need to allocate one of the timers to work with OC module (it will be used for base PWM frequency) and that's it.
All you need after that is to set PWM duty cycle value by setting timer PRx register, you don't need to swap bits like in your PWM routine.
To stop it simple stop it simply disable the timer.
To run it again run the timer.
To change direction (it depends of your driver for the motor) I guess you need just to toggle direction pin.
I hope it helps...
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?
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);
}
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).
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.