MSP432 learning experience: system tick timer
[Copy link]
This post was last edited by Hot Ximixiu on 2019-9-2 23:04
The system tick timer is very important in the operating system. It can provide a good system clock beat, just like our heart, beating at a certain frequency. It provides a good time base for the operation of the system. Here, we will use it to complete the implementation of a delay function. Why use it? Because it beats very accurately, and the configuration is relatively simple, and it will not temporarily use our timer or other peripherals. No more i, j, and two for loops running there to delay. Do you want to try it? Let's see.
The Cotex-M4 core of MSP432 is provided by ARM, so many things here can be universal. What we are discussing this time is a system tick timer that is included in both M3 and M4. Since many things are the same, including the definitions of related registers, I directly used the delay function code in the atomic STM32 and copied it directly, but I found one of the problems, which is the clock problem. I found that the description chapter of the tick timer in our 432 data sheet mentioned it.
So what does the free running clock (FCLK) here specifically refer to?
ARM Technical Note: FCLK is the processor's free-running processor clock, used to sample interrupts and time debug modules. When the processor is asleep, FCLK is used to ensure that interrupts can be sampled and sleep events can be tracked. The "free running clock" FCLK of the Cortex-M3 core. "Free" in that it does not come from the system clock HCLK, so FCLK continues to run when the system clock stops. FCLK and HCLK are synchronized with each other. FCLK is a free-running HCLK. FCLK and HCLK should be balanced to ensure the same delay when entering the Cortex-M3.
So here by default, Systick's clock comes from the CPU's operating clock.
The CPU here uses MCLK.
So is the clock we are talking about correct? We will verify it later.
Now we can temporarily ignore whether the clock is accurate or not, and first assume it is 48MHz.
Here we define two variables, which are static global variables. Let me explain why they are defined as static global variables. First, let's look at what static means. From the literal meaning, we can understand that the variable is in a "quiet state", that is, it is a quiet handsome man (or beautiful woman). So what is its characteristic? It is lazy to move.
Let’s take a simple example to illustrate (the program from Baidu Encyclopedia is used here).
The following is the experimental result obtained with static local variables.
In the following code, there is no static keyword, and the result is different.
Okay, now let's see why the variable with the static keyword is "too lazy to move". We can see that the function fun() returns the calculated value of the formula f = f * n. Then the code in the first picture is the result of 1,2,6,24,120, let's analyze it.
In this result, the latter result is the value of f obtained by the above calculation, and then multiplied by the following n to get a new value. In short, when we call the fun() function again, the statement static int f = 1; is not executed. In the figure below, if there is no static, the statement f = 1 will be executed again, so each time 1 * n will get the results of 1, 2, 3, 4, 5.
So, adding static will make the variable lazy, it will keep the value of the last calculation unless it is reassigned. I believe you can understand the meaning of static more clearly through the above code.
Well, except for the feature of "laziness", static local variables and static global variables are the same as our local variables and global variables.
Then it is easy to understand why we define the two multipliers as static. If I use a register to count, the number must be either increasing or decreasing. Otherwise, if I count once and you assign it the initial value, I will count in vain. I will still be standing still after counting over and over again. So it is defined as static here.
But if you are careful, you will find that static and non-static actually have the same effect. This is because we define them as global variables, so these two definition statements cannot be executed again when the function is called, which means they only have one chance to execute. So if you change them to non-static global variables, it will also work. But if you move these two statements into our function, you sometimes need to think about it. Here we define them as static for safety.
We have assumed that the clock is 48MHz, so here we assign fac_us a value of 48. This should be easy to understand. A frequency of 48MHz means 1us, which should be understandable. If not, calculate it yourself.
There is no doubt that fac_ms is 1000 times fac_us, so just multiply it here.
Below we explain the implementation of the delay function, the code is as follows:
Because I copied the code directly from STM32, I thought it could be used directly, but I found it was not possible. The code was compiled, but an error occurred when the code was executed, so I went back to read the 432 data sheet carefully. After all, the data sheet is like our textbooks in school, and other things are like our extracurricular reference books, so the most important thing is our textbooks, and everything is based on the textbooks, except for the problem of course first go back to our textbooks, data sheets to find.
The content that needs to be checked is as follows, marked in the red box.
In these contents, we found the instructions for using SysTick of 432. Here we saw the configuration steps and the following notes. One important point is that the control bit CLKSOURCE of the clock source of 432 must be set to one. This is very important.
The code for the clock source control position number of 1 is as follows: SysTick_CTRL_ENABLE_Msk is to enable the system tick timer, and SysTick_CTRL_CLKSOURCE_Msk is the clock source control position one.
As for why a “|” (or) is used in the above code?
Here we explain this C language knowledge. For an 8-bit register, for simplicity, we take an 8-bit register as an example. Here, if we want to modify one of the bits without changing the values of other bits, how to implement it? Let's take a closer look here.
First, let's assume that the default initial value of the 8-bit register is set to: 1001_0111
Now let's say we want to change the value of the 6th bit (here we mean the actual 6th bit. Why do we say that? Normally, registers are from bit 0 to bit 7).
Our implementation is as follows:
1001_0111 | 0010_0000 = 1011_0111
It can be seen that by using the OR method we can achieve changing only one bit without affecting the data of other bits.
So if we want to reverse a bit, from 1 to 0, we can do it by ANDing. For example, let's try changing the fifth bit.
1001_0111 & 0001_0000 = 0001_0000
We can see that the result is not what we want. We need to make a small adjustment here. The data to be ANDed later must be inverted before the two can be ANDed.
1001_0111 & (~(0001_0000)) = 1001_0111 & 1110_1111 = 1000_0111
In this way, we can successfully achieve that no matter whether it is set to zero or set to one, the data of other bits will not be changed. The reason for this is that 430 has no way to perform bit addressing, so there is no way to directly operate on the bit, and it can only be operated through registers, while 51 can directly perform bit addressing. The 32 implementation method is implemented by bit binding. Here we will not go into details about this bit binding method. We will write a post next time to explain this issue and understand the specific implementation method.
OK, now that we understand this, let's look at the previous SysTick->CTRL. This refers to the register instruction provided by ARM, which is written to the control register of our SysTick module. In ARM-Cotex-M4, there are four registers to control the SysTick module. We can find their descriptions (I didn't find the Chinese description of the tick timer for Cotex-M4, but there is the Chinese description for Cotex-M3, which are the same in the tick timer section, so we refer to the M3 one).
Here we explain in detail the four registers of our tick timer, including the related control bit functions. We need to understand these things, but there is no need to memorize them. We just need to know a learning method so that we can find them when we need them in the future. After all, when you go out to work in a company and do projects, it is impossible for you to memorize the meaning of each bit of a register. It is not realistic and meaningless. Therefore, the method is very important and you must master the method.
We compare it to the register description given in the 432 manual. There are also four registers. The content behind it is a bit long, so I won’t show it in screenshots here. By comparing them, we will find that the two are actually exactly the same (after all, they are produced by the same company, ARM).
Follow the configuration steps given in 432:
1. The first step is to configure the STCVR register, which is our SysTick->LOAD. The first step is to give the reload value. Here we must understand that the reload value is 24-bit data, so we can calculate the corresponding maximum delay number. A 24-bit register can count up to 16_777_216 data, so we can calculate our delay time based on our clock frequency. If it is 48MHz, we can count up to 349_525 us, which means 34.9ms. So at this frequency, we have to control our delay time and it can't be too high. Under normal circumstances, such a long delay time is enough for us. It can be seen that the frequency of 48MHz is still a bit too high. To get a longer delay, we need to reduce the clock frequency.
2. Next we need to configure our STCVR register. This register corresponds to our SysTick->VAL, which is the value of our current register. We need to clear it.
3. The last step is to control the status register. As we have explained above, we need to enable the SysTick module and set the clock source control bit to 1.
In this way, we have completed all the configuration work. What we also need to understand here is that SysTick is a 24-bit automatic reload register. When we enable it, it will load the value to be counted from the STCVR load register, and then count down instead of up, until it reaches 0. After counting to 0, the COUNTFLAG flag in the status register will be set. We also keep checking this flag after turning on the tick timer to see if the count is finished.
temp&0x01 is used to query the SysTick module when it is enabled. It queries the lowest bit. Why is & used? It is similar to the setting 1 and setting 0 mentioned above. If it doesn't work, you can write one or two data to verify it yourself and you will understand it.
temp&(1<<16) queries the COUNTFLAG flag we mentioned above, so why does 1 need to be shifted left by 16 bits? From the description of the register, we can see that this bit is the 16th bit of the register, so it needs to be shifted left by 16 bits to perform the AND operation.
The two conditions of temp&0x01&&!(temp&(1<<16)) need to be met at the same time. After completion, the while loop will be exited, and then we turn off the SysTick module, because it is an automatic reload register. If it is not turned off, it will take out data from the reload register again to continue counting.
The same is true for the ms delay below. I won't explain it here.
One more thing to add is that we used a macro definition like this when configuring the SysTick->CRTL register, as shown below. There is a 1UL in front, so what does this UL mean? 1UL here refers to the unsigned long integer number 1. If you don't write a suffix, the system defaults to int type. Why do we need to use a long integer here? Because this is a 32-bit register, and an integer cannot represent such a large number, so a long integer is required. The following SysTick_CTRL_ENABLE_Pos, the principle is the same as the query COUNTFLAG we mentioned above, left shift.
Now we need to verify which clock source is used. We have no idea what FCLK is in the manual, but we will know it through actual operation.
Here we use the control variable method. There are a total of ACLK, BCLK, HSMCLK, MCLK, and SMCLK clocks. Set one of them to high frequency and the others to low frequency. Then turn on a light and visually observe the flashing frequency to know which one it is.
We have previously deduced that it might be MCLK, so I changed the frequency of MCLK directly at the beginning. It is obvious that the flashing frequency of the light has changed a lot. At the beginning, we used 48MHz as the benchmark, and then I changed it to 32KHz. The gap in the middle is very large, so it is determined that the clock source of SysTick is MCLK. Now we can quickly determine how to use and configure the delay of SysTick.
The process of modifying the code and the reasoning will not be explained here. Just remember the conclusion: the clock of SysTick comes from MCLK.
Here is a debugging tip. If you are not sure whether your configuration code is correct and need to check it at the register level, we can find the corresponding register through the Debug window of CCS. After clicking it, the changes of each bit will be displayed. You can pause the code to view the running status of each bit.
We can get the following result. And the corresponding bits are also given next to it for explanation, which is very convenient.
Finally, we change the code to the following, select the frequency of our MCLK as the high-speed clock, and then divide it by 16, so that we can get a relatively low frequency of 3MHz, so that we can have a longer delay.
At the same time, we have modified the initial function of the delay to make it easier for everyone to understand and use. Please see the attachment for other codes.
|