1
0
mirror of https://github.com/rfivet/stm32bringup.git synced 2025-02-21 05:37:09 -05:00
stm32bringup/docs/26_uptime.html

350 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>2.6 uptime</title>
<link type="text/css" rel="stylesheet" href="style.css">
</head>
<body>
<h1>2.6 uptime</h1>
Its time to throw away the prototype and write a production version of
<code>uptime</code>. There is several things I want to straighten up in
<b>uptime.1.c</b>:
<ul>
<li> <code>main()</code> is using assembly code to wait for interrupt.
Definitively not high level C.
<li> Also not high level C the lines <code>kputu( last) ; puts( ” sec”) ;</code>.
I should have <code>printf( “%u sec\n”, last) ;</code> instead.
<li> <code>kputc()</code> function prototype and the external variable
declaration for <code>uptime</code> should be included as a C header file.
</ul>
Similar to what I did when I split the functionalities between files
according to the three stages of execution (boot, init, main), I will
reorganize the code according to three categories (system, library,
application).
<h2>System</h2>
First I make clear what the system interface is by writing the header
<b>system.h</b>. Here belong the global variables external declarations and the
function prototypes.
<pre>
/* system.h -- system services */
extern volatile unsigned uptime ; /* seconds elapsed since boot */
int init( void) ; /* System initialization, called once at startup */
void kputc( unsigned char c) ; /* character output */
int kputs( const char s[]) ; /* string output */
void yield( void) ; /* give way */
</pre>
Next, I make a revision of <b>uplow.1.c</b> by making a copy into
<b>uplow.2.c</b>.
<p>
I include <b>system.h</b> which is the interface that <b>uplow.2.c</b>
implements. I will have several implementations of the same interface,
so <b>system.h</b> is not just the interface published by <b>uplow.2.c</b>,
its <b>uplow.2.c</b> which is an implementation of <b>system.h</b>.
<pre>
#include "system.h" /* implements system.h */
</pre>
I extract the code for <b>puts()</b> as it is a library function that doesnt
really belong to the system.
<p>
I add the implementation of <b>kputs()</b> and <b>yield()</b>.
<pre>
int kputs( const char s[]) { /* string output */
int cnt = 0 ;
int c ;
while( (c = *s++) != 0) {
kputc( c) ;
cnt += 1 ;
}
return cnt ;
}
void yield( void) { /* give way */
__asm( "WFI") ; /* Wait for System Tick Interrupt */
}
</pre>
<h2>Library</h2>
I create the implementation of <code>printf()</code> in <b>printf.c</b>.
<ul>
<li> It uses the system interface <b>system.h</b>.
<li> I have eliminated the recursivity from my previous <code>kputu()</code>
version by adding characters at the beginning of a string.
<li> <code>kputu()</code> takes one additional divider parameter, so it can be
used to print unsigned integer in various format like octal, decimal and
hexadecimal. Current implementation will work for base 8 to 16, it wont
work for binary or base 36.
<li> <code>kputi()</code> outputs signed integer.
<li> <code>printf()</code> implements a subset of the format interpreter: %%,
%c, %d, %i, %o, %s, %u, %x, %X.
</ul>
<pre>
/* printf.c -- format and print data */
#include &lt;stdarg.h>
#include &lt;stdio.h>
#include "system.h" /* kputc(), kputs() */
static int kputu( unsigned u, unsigned d) {
char s[ 12] ; /* room for 11 octal digit + EOS */
char *p = &s[ sizeof s - 1] ; /* point to last byte */
*p = 0 ; /* null terminated string */
do {
unsigned r = u % d ;
u /= d ;
*--p = "0123456789ABCDEF"[ r] ;
} while( u) ;
return kputs( p) ;
}
static int kputi( int i) {
int flag = i < 0 ;
if( flag) {
i = -i ;
kputc( '-') ;
}
return flag + kputu( i, 10) ;
}
int printf( const char *fmt, ...) {
va_list ap ;
int cnt = 0 ;
int c ; /* current char in format string */
va_start( ap, fmt) ;
while( ( c = *fmt++) != 0)
if( c != '%') {
cnt += 1 ; kputc( c) ;
} else if( ( c = *fmt++) == 0) {
cnt += 1 ; kputc( '%') ;
break ;
} else
switch( c) {
case 'c':
cnt += 1 ; kputc( va_arg( ap, int /* char */)) ;
break ;
case 'o':
cnt += kputu( va_arg( ap, unsigned), 8) ;
break ;
case 'u':
cnt += kputu( va_arg( ap, unsigned), 10) ;
break ;
case 'x':
case 'X':
cnt += kputu( va_arg( ap, unsigned), 16) ;
break ;
case 'i':
case 'd':
cnt += kputi( va_arg( ap, int)) ;
break ;
case 's':
cnt += kputs( va_arg( ap, char *)) ;
break ;
default:
cnt += 1 ; kputc( '%') ;
/* fallthrough */
case '%':
cnt += 1 ; kputc( c) ;
}
va_end( ap) ;
return cnt ;
}
</pre>
<h2>Application</h2>
I write my final version of uptime in <b>uptime.c</b>.
<ul>
<li> It uses the system interface and standard library.
<li> Instead of a count of seconds elapsed it displays a breakdown in week,
days, hours, minutes and seconds.
</ul>
<pre>
/* uptime.c -- tells how long the system has been running */
#include &lt;stdio.h>
#include "system.h" /* uptime, yield() */
static void display( unsigned u, const char *s) {
if( u)
printf( " %d %s%s", u, s, &"s"[ u <= 1]) ;
}
int main( void) {
unsigned last = 0 ;
for( ;;)
if( last != uptime) {
unsigned w, d, h, m ,s ;
last = uptime ;
d = h = m = 0 ;
s = last % 60 ;
w = last / 60 ;
if( w) {
m = w % 60 ;
w /= 60 ;
if( w) {
h = w % 24 ;
w /= 24 ;
if( w) {
d = w % 7 ;
w /= 7 ;
}
}
}
printf( "up") ;
display( w, "week") ;
display( d, "day") ;
display( h, "hour") ;
display( m, "minute") ;
display( s, "second") ;
printf( "\n") ;
} else
yield() ; /* Wait for System Tick Interrupt */
}
</pre>
<h2>Build</h2>
To build I add the composition in <b>Makefile</b>.
<pre>SRCS = startup.c uplow.2.c uptime.c printf.c</pre>
Unfortunately, the build fails at the link phase.
<pre>
$ make
f030f4.elf
D:\Program Files (x86)\GNU Arm Embedded Toolchain\arm-gnu-toolchain-14.2.rel1-mi
ngw-w64-i686-arm-none-eabi\bin\arm-none-eabi-ld.exe: uptime.o: in function `main
':
D:\home\Projects\stm32bringup/uptime.c:41:(.text.startup+0xa4): undefined refere
nce to `putchar'
make: *** [Makefile:53: f030f4.elf] Error 1
</pre>
The linker found a reference to <code>putchar()</code> at line 41 of
<b>uptime.c</b>.
<pre>
printf( "\n") ;
</pre>
I havent used <code>putchar()</code> in my code and line 41 is a
<code>printf( "\n")</code> that can be optimized to a
<code>putchar( '\n')</code>. This must be some high level C optimization of gcc.
<p>
I add the code for <code>putchar()</code> in <b>putchar.c</b> as it is a
standard library function.
<pre>
/* putchar.c -- write a character to stdout */
#include &lt;stdio.h>
#include "system.h" /* kputc() */
int putchar( int c) {
kputc( c) ;
return c ;
}
</pre>
Updating <b>Makefile</b> by adding <code>putchar.c</code> to the composition.
<pre>SRCS = startup.c uplow.2.c uptime.c printf.c putchar.c</pre>
The build now complete successfully.
<pre>
$ make
f030f4.elf
text data bss dec hex filename
1317 0 8 1325 52d f030f4.elf
f030f4.hex
f030f4.bin
</pre>
By checking the map file provided by the linker, I can see that the
number of low level modules referred by the code generated by the
compiler has increased. Both integer and unsigned division but also some
code to handle <code>switch()</code> statement are now referenced.
<pre>
Archive member included to satisfy reference by file (symbol)
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
(_thumb1_case_sqi.o)
printf.o (__gnu_thumb1_case_sqi)
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
(_udivsi3.o)
uptime.o (__aeabi_uidiv)
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
(_divsi3.o)
uptime.o (__aeabi_idiv)
D:/Program Files (x86)/GNU Arm Embedded Toolchain/arm-gnu-toolchain-14.2.rel1-mi
ngw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/thumb/v6-m/nofp\libgcc.a
(_dvmd_tls.o)
D:/Program Files (x86)/GNU Arm Embedded Toolchain/
arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi/lib/gcc/arm-none-eabi/1
4.2.1/thumb/v6-m/nofp\libgcc.a(_udivsi3.o) (__aeabi_idiv0)
</pre>
<h2>Test</h2>
I flash the board and start execution, the output works as expected.
<p>
<img alt="uptime" src="img/26_uptime.png">
<p>
It will take a while to see the days and weeks counts appear, so I will
need to power the board independently from its serial interface. For
test purpose I fast forward the execution by using a bigger value for
the increment of <code>uptime</code> in <code>SysTick_handler()</code>.
<h2>Checkpoint</h2>
Rereading the code while writing this web page, I found a typo in the
week calculation. After that I retested with a bigger time increment to
make sure days and weeks values are correct. Its also clear that the
test coverage for the printf format interpreter is not sufficient as I have
coded more than is necessary to implement <b>uptime</b>.
<p>
I didnt expect gcc to optimize calls to high level C functions,
replacing some <code>printf()</code> by <code>putchar()</code>, thus forcing me
to write additional code. So far I am not concerned by execution speed, so this
type of optimization is a bit counter productive.
<p>
<a href="27_library.html">Next</a>, I will make sure that what belongs to the
library category fits in an actual library file.
<hr>© 2020-2025 Renaud Fivet
</body>
</html>