time delay in simulink to be used with c code generation

I am trying to interface a STM32F411RE board with a 4 digital seven segment display via simulink and embedded coder support. To do so i need to introduce various time delays for example: the clock pin goes high and then after 2 micro-seconds it goes low etc. but i an unable to model such time delay. I have tried introducing a for loop that does nothing but the code generator optimizes the code and omits the for loop as it is not providing any fruitful output. is there any way that i can turn this optimization off so that it keeps the for loop? I have tried another way where i introduce a dummy variable inside for loop but the c code generator changes the order of the commands and moves the for loop in the start and not where i want the delay to be. please let me know if anyone has dealt with a similar problem? memory block/delay block also doesn't work.
Thank you!!

14 Comments

Hi @Sadia,

When you introduce a for loop that does not have any side effects (i.e., it does not modify any variables or produce output), the code generator recognizes it as redundant and optimizes it out. This is why your attempts to create a delay using a for loop have not been successful. Similarly, introducing a dummy variable may not work as expected because the code generator can still rearrange the order of operations based on its optimization algorithms. So, to achieve the desired timing without the code generator optimizing away the delay, we can utilize the Simulink Delay block or a combination of Simulink blocks to create a timed sequence. Below is a simplified example of how to implement this:

% Simulink Model Setup
model = 'STM32_Segment_Display';
open_system(model);
% Create a new model
new_system(model);
add_block('built-in/Constant', [model '/Clock_High']);
set_param([model '/Clock_High'], 'Value', '1');
add_block('built-in/Constant', [model '/Clock_Low']);
set_param([model '/Clock_Low'], 'Value', '0');
add_block('built-in/Delay', [model '/Delay']);
set_param([model '/Delay'], 'DelayLength', '2e-6'); % 2 microseconds
add_block('built-in/Outport', [model '/Out']);
set_param([model '/Out'], 'Port', '1');
% Connect blocks
add_line(model, 'Clock_High/1', 'Delay/1');
add_line(model, 'Delay/1', 'Out/1');
add_line(model, 'Clock_Low/1', 'Out/1');
% Save and build the model
save_system(model);
slbuild(model);

So, in this code, I created a new Simulink model named STM32_Segment_Display which consists of the following components:

Clock High and Low Constants: These blocks represent the high and low states of the clock pin. The Constant blocks are set to output 1 (high) and 0 (low) respectively.

Delay Block: The Delay block is configured to introduce a delay of 2 microseconds. This is crucial for ensuring that the clock pin remains high for the required duration before transitioning to low.

Outport Block: The Outport block is used to send the output signal to the STM32F411RE board.

Connections: The blocks are connected in a sequence where the Clock_High output goes into the Delay block, and the output of the Delay block is then sent to the Out port. The Clock_Low can be similarly connected to ensure the clock pin goes low after the delay.

Addressing your concern regarding to prevent the code generator from optimizing away the delay, you have to make sure that the Delay block is properly configured and connected in the model. The use of a Delay block is preferred over a for loop, as it provides a clear and functional way to introduce timing without being optimized out.

After implementing these changes, validate the timing using an oscilloscope or logic analyzer to ensure that the delays are functioning as expected. Please let me know if this approach helped resolve your problem.

Thank you for your reply. I have tried out the code.
add_line(model, 'Clock_High/1', 'Delay/1');
add_line(model, 'Delay/1', 'Out/1');
add_line(model, 'Clock_Low/1', 'Out/1');
however on the last line above it gives the following error : "The destination port already has a line connection", as the out port is already connected to clock_high through the delay block. can you please tell me How to connect the two signals to one port?

Hi @Sadia,

Use a Mux block or a Switch block. I will prefer Mux block because it will combine multiple signals into a single vector, which can then be sent to the Outport. For more information on this block, please refer to

https://www.mathworks.com/help/simulink/slref/mux.html

Here’s how you can modify your model:

Add a Mux Block:Use the command add_block('built-in/Mux', [model '/Mux']); to create a Mux block in your model.

How to Connect Signals to the Mux:

Connect the output of the Delay block to one input of the Mux.

Connect the output of the Clock_Low block to another input of the Mux.

Connect the Mux to the Outport:

Finally, connect the output of the Mux to the Outport.

Here’s a simplified code snippet to illustrate this:

% Add Mux block
add_block('built-in/Mux', [model '/Mux']);
set_param([model '/Mux'], 'Inputs', '2'); % Set number of inputs to 2
% Connect blocks
add_line(model, 'Delay/1', 'Mux/1'); % Connect Delay to Mux
add_line(model, 'Clock_Low/1', 'Mux/2'); % Connect Clock_Low to Mux
add_line(model, 'Mux/1', 'Out/1'); % Connect Mux to Outport

This approach will maintain the integrity of your model while allowing you to send multiple signals to a single Outport. Please try this modification and let me know if it resolves your issue.

Thankyou very much. however, i am a little confused, lets say i have the clock signal high initially and then i give a 2 us delay and the add a constant block that is set to zero (indicating clock_low) but when i add a mux and connect the two signals, The signal is still high (the mux shows two waveforms one high and one low with a 2 us delay). but the output will still be high. maybe i am not underatanding it correctly.
If i use a switch block then i dont need the delay block right? since i can just switch between the two values using the condition if the switch?
Hi @Sadia,
Given your requirements, if you're primarily interested in conditionally passing one of two signals based on their states without introducing delays, then using a Switch block may be more effective. Conversely, if maintaining both signals is essential for further processing or analysis, sticking with the Mux is appropriate but requires careful consideration of how those signals interact at the output. Hope this clarifies the confusion. If you have further questions or need more clarification on specific aspects of your model design, feel free to ask!
Thank you. I will try this with the switch and see if it works.
Hi @Sadia,
Please update me about the progress you made by using switch.
hello umar,
The thing with the switch does work but it just does not feel sustainable to me because i have to introduce so many delays at different time instants (there is a start condition then a write byte condition, an acknowledgment condition and a stop condition). I have tried adjusting the timings but it is not feasible for example:
unsigned char i;
for (i = 0; i <8; i + +)
{
Clk = 0;
if (oneByte & 0x01) // low front
{dio = 1;}
else {dio = 0;}
Delay_us (3);
oneByte = oneByte >> 1;
clk = 1;
Delay_us (3);
}
If i try to implement this, the CLK goes low then i have to implement the If statement and the give a delay but how can i incorporate this delay because the CLK signal remains the same before and after. Its time based like the execution should maybe pause for the said time. On the top of that, there is a loop as well. I have tried using the pause() function in matlab but it aparently is not c code generation supported.
If you have any more leads. please let me know. Thankyou.

Hi @Sadia,

Your existing loop structure is a good start, but there are a few adjustments you can make to improve timing control. For example, instead of using arbitrary delays (Delay_us), consider implementing a more efficient method to manage timing. Using hardware timers or interrupts which can help you achieve more precise control over signal transitions without blocking your main program execution. If your microcontroller supports it, set up a timer interrupt that triggers at regular intervals corresponding to your desired clock frequency. In the interrupt service routine (ISR), toggle the CLK line and manage data transmission without explicitly using delays in your main loop. Here’s an example code snippet for simplified version of how you might structure this using timer interrupts.

       void Timer_ISR() {
         static unsigned char bit_index = 0;
         if (bit_index < 8) {
             Clk = 0; // Set clock low
             dio = (oneByte & 0x01) ? 1 : 0; // Set data line based on LSB
             Delay_us(3); // Short delay for data setup
             Clk = 1; // Set clock high
             Delay_us(3); // Delay for data hold time
             oneByte >>= 1; // Shift right to prepare for next bit
             bit_index++;
         } else {
             bit_index = 0; // Reset after sending all bits
             // Optionally handle stop condition here
         }
       }

Hope this helps.

Hi @Sadia,
Could you please update about your progress.

Hi @Sadia,

After going through documentation listed at https://www.mathworks.com/help/ecoder/STM32f4xx-based-boards.html?s_tid=CRUX_lftnav

and addition to my above comments, I will now recommend utilizing hardware timers and interrupts effectively to manage your signal transitions without excessive software delays. In my opinion, it will allow you to achieve more precise timing and a more sustainable implementation.Here’s a basic outline of how you can structure your code to use timers for controlling your clock signal while sending bits using “c” language. For more information on “ #include "stm32f4xx_hal.h", please refer to

https://www.st.com/resource/en/user_manual/um1725-description-of-stm32f4-hal-and-lowlayer-drivers-stmicroelectronics.pdf

Code to use timers for controlling your clock signal while sending bits

#include "stm32f4xx_hal.h"
TIM_HandleTypeDef htim2; // Assuming TIM2 is used for timing
volatile uint8_t oneByte;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  static uint8_t bitIndex = 0;
    if (htim->Instance == TIM2) {
        if (bitIndex < 8) {
            Clk = 0; // Set CLK low
            dio = (oneByte & 0x01) ? 1 : 0; // Set DIO based on the             current bit
            oneByte >>= 1; // Shift right to process the next bit
            // Prepare for next cycle
            bitIndex++;
        } else {
            Clk = 1; // Set CLK high after sending all bits
            bitIndex = 0; // Reset for next byte
        }
    }
  }
void setupTimer() {
  // Configure TIM2 to generate an interrupt every specific microsecond    (for example, every 3us)
  __HAL_RCC_TIM2_CLK_ENABLE();
    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 84 - 1; // Assuming 84 MHz clock, adjust as     needed
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 300 - 1; // Adjust this value for timing (3us)
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_Base_Init(&htim2);
    HAL_TIM_Base_Start_IT(&htim2); // Start timer with interrupts enabled
  }
int main(void) {
  HAL_Init();
  setupTimer();
    while (1) {
        // Main loop can handle other tasks or prepare data to send
        oneByte = ...; // Load your data here
        HAL_Delay(100); // Example delay between bytes if needed
    }
  }

Explanation of the Code Structure

Timer Initialization: The setupTimer function initializes TIM2 to generate an interrupt every specified period (e.g., every 3 microseconds). The prescaler is set according to the system clock frequency.

Interrupt Handling: In HAL_TIM_PeriodElapsedCallback, handle what happens each time the timer overflows. Here I manage the clock and data output according to the current bit being sent. The Clk signal is toggled appropriately based on the current state.

Main Loop: In the main loop, you can prepare data (`oneByte`) that needs to be sent. This allows for flexibility in managing when to send new data without blocking execution.

I also utilized interrupts method which offloads timing control from your main application logic, allowing it to run more smoothly without needing busy-wait delays. Please make sure that your timer configurations align with your hardware specifications and desired timing accuracy. Adjust the prescaler and period values as needed based on your clock speed.

Hopefully, by implementing this approach, you should find that it greatly enhances the sustainability and reliability of your communication protocol without overwhelming your main program flow with delays.

Dear Umar,
Thank you very much for your detailed reply.
My problem is whenever I try to include the a C function (using coder.ceval etc) in a Matlab function block, initially it gives errors such as cannot find header files such as "stm32f4xx_hal_conf.h" etc. Then when i include all the directories, I get some assembler errors such as:
operand mismatch for ds, Error: no such instruction: `dmb 0xF etc.
I am not very well versed with C and assembly language so I don't really understand this. I have tried googling it but no luck specifically in Matlab.
If you have any idea about these errors let me know. Thankyou.

Hi @Sadia,

Please see my response to your comments below.

To address your first query regarding, My problem is whenever I try to include the a C function (using coder.ceval etc) in a Matlab function block, initially it gives errors such as cannot find header files such as "stm32f4xx_hal_conf.h" etc

The error regarding missing header files such as "stm32f4xx_hal_conf.h" indicates that the MATLAB environment cannot locate the necessary STM32 HAL library files. This is often due to incorrect paths or missing configurations in the MATLAB setup.

Now, addressing your second query, operand mismatch for ds, Error: no such instruction: `dmb 0xF etc.

The assembler errors, such as "operand mismatch for ds" and "no such instruction: `dmb 0xF," suggest that the assembly code generated by the C code is not compatible with the target architecture or that the compiler settings are not correctly configured for the STM32 platform.

My suggestion would be at this point would be integrating the C code into MATLAB, test the C code in a standalone environment (e.g., an STM32 IDE) to ensure that it compiles and runs correctly. This can help isolate whether the issue is with the C code itself or the integration with MATLAB.

After reading your comments about “I am not very well versed with C and assembly language so I don't really understand this. I have tried googling it but no luck specifically in Matlab.”

I may suggest how exactly are you compiling code since I have no clue what is going on your side of “ interface a STM32F411RE board with a 4 digital seven segment display via simulink

So, please provide some detailed instructions and let’s follow a step by step process in order to accomplish your goal. Otherwise, exchanging these ideas back and forth and not knowing how are you interfacing your board with simulink will not help reach your target goal. Hope, you understand.

Sign in to comment.

Answers (1)

Hey @Sadia,
Adjusting code generation optimization settings can help embedded system applications manage time delays. Here are some steps that will help resolve the timing issues:
1. Disable Block Reduction:
  • Open the Model Configuration Parameters dialog box from the Settings. Then, navigate to the Simulation Target tab.
  • In the advanced parameters section, uncheck the option labeled Block reduction. This will prevent the code generator from simplifying blocks that are crucial for timing accuracy.
2. Modify Optimization Levels:
  • Go to the Code Generation tab and then select Optimization.
  • Consider setting the Optimization levels option to Minimum. This setting reduces the extent to which the code is optimized, preserving your intended logic.
  • Alternatively, it can be set to Balanced with readability or Maximum if those settings better suit the needs.
  • If either Balanced with readability or Maximum is chosen, enable Specify custom optimization and uncheck the option Eliminate superfluous local variables (expression folding). This prevents the code generator from folding expressions that might interfere with timing logic.
These settings can be carefully adjusted to maintain code structure and time delays, ensuring the application works on the hardware.
Let me know if this workaround helps or if any further help is needed!

3 Comments

hey saurav,
Thankyou for your reply.
I have tried all these but still no luck. it still omits the for loop or changes its position.
Do you have any other suggestion? do let me know.
Thank you.
Can You try this if it works:
  1. Create a MATLAB Function Block:
  • In your Simulink model, add a MATLAB Function block.
  • Double-click the block to open the editor and define the delay function. Use coder.ceval to call a custom C function:
function delayMicrosecondsWrapper(microseconds)
%#codegen
coder.cinclude('delay.h');
coder.ceval('delayMicroseconds', microseconds);
2. Write the Custom C Code:
  • Create a file named delay.c with the following content:
#include "stm32f4xx_hal.h"
void delayMicroseconds(uint32_t microseconds) {
volatile uint32_t count;
const uint32_t delay = microseconds * (SystemCoreClock / 1000000) / 5; % Adjust as necessary
for (count = 0; count < delay; count++) {
__asm volatile("nop");
}
}
3. Create a Header File:
  • Create a delay.h file to declare the function:
#ifndef DELAY_H
#define DELAY_H
#include <stdint.h>
void delayMicroseconds(uint32_t microseconds);
#endif
4. Include Custom Code in Simulink:
  • In the Model Settings under Code Generation > Custom Code, specify the delay.c and delay.h files in the appropriate sections for source and header files.
  • In the Source files field, enter the name of your custom source file, e.g., delay.c. If the file is not in the current working directory, provide the full path or relative path from the model file location.
  • In the Header files field, enter the name of your custom header file, e.g., delay.h. Similar to source files, provide the full path if necessary.
  • If you need to include any specific initialization code or additional functions, you can add them in the Include directives field. For example, you might add #include "delay.h" if it is not already included in your code.
hello,
Thank you for your reply. I have actually already tried doing this but initially it gives errors such as cannot find header files such as "stm32f4xx_hal_conf.h" etc. Then when i include all the directories, I get some assembler errors :
operand mismatch for ds, Error: no such instruction: `dmb 0xF etc.
I am not very well versed with C and assembly language so I don't really understand this. I have tried googling it but no luck specifically in Matlab.
If you have any more leads do let me know. Thankyou.

Sign in to comment.

Categories

Find more on MATLAB in Help Center and File Exchange

Products

Release

R2023b

Asked:

on 22 Aug 2024

Commented:

on 29 Aug 2024

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!