你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

MiniPro STM32H750 开发指南_V1.1-USMART调试组件实验

[复制链接]
STMCU小助手 发布时间:2022-10-7 18:27
USMART调试组件实验; R* n" S9 E& z* r, i* }& n
本章,我们将向大家介绍一个十分重要的辅助调试工具:USMART调试组件。该组件由正点原子开发提供,功能类似linux的shell(RTT的finsh也属于此类)。USMART最主要的功能就是通过串口调用单片机里面的函数,并执行,对我们调试代码是很有帮助的。- F, T. d7 @0 K7 X( I: b+ q
26.1 USMART调试组件简介! P/ n  [$ o0 t  B
USMART是由正点原子开发的一个灵巧的串口调试互交组件,通过它你可以通过串口助手调用程序里面的任何函数,并执行。因此,你可以随意更改函数的输入参数(支持数字(10/16进制,支持负数)、字符串、函数入口地址等作为参数),单个函数最多支持10个输入参数,并支持函数返回值显示,目前最新版本为V3.5。! ^: g% D7 @  @1 h  C# d+ V
USMART的特点如下:
8 I$ v, @/ l& a5 y! I7 q: @1,可以调用绝大部分用户直接编写的函数。
( S, m4 w3 C2 u! p2,资源占用极少(最少情况:FLASH:4K;SRAM:72B)。
/ @6 t3 O  X2 ]- \7 @- }3,支持参数类型多(数字(包含10/16进制,支持负数)、字符串、函数指针等)。
0 F  q8 j9 o/ h4,支持函数返回值显示。7 ]. O$ Q6 q! k  g8 q6 h( Y
5,支持参数及返回值格式设置。
4 y! g9 p: A2 P) h0 E* M  h4 h6,支持函数执行时间计算(V3.1及以后的版本新特性)。0 u. p! ~( U  i) z) P" Y
7,支持AC6编译器。
- I* Q. k% ^$ m( C! P有了USMART,你可以轻易的修改函数参数、查看函数运行结果,从而快速解决问题。比如你调试一个摄像头模块,需要修改其中的几个参数来得到最佳的效果,普通的做法:写函数修改参数下载看结果不满意修改参数下载看结果不满意….不停的循环,直到满意为止。这样做很麻烦不说,单片机也是有寿命的啊,老这样不停的刷,很折寿的。而利用USMART,则只需要在串口调试助手里面输入函数及参数,然后直接串口发送给单片机,就执行了一次参数调整,不满意的话,你在串口调试助手修改参数在发送就可以了,直到你满意为止。这样,修改参数十分方便,不需要编译、不需要下载、不会让单片机折寿。
/ h, g) |; m- K0 w6 d& |USMART支持的参数类型基本满足任何调试了,支持的类型有:10或者16进制数字、字符串指针(如果该参数是用作参数返回的话,可能会有问题!)、函数指针等。因此绝大部分函数,可以直接被USMART调用,对于不能直接调用的,你只需要重写一个函数,把影响调用的参数去掉即可,这个重写后的函数,即可以被USMART调用了。5 j6 E; P9 h7 U/ w+ V6 [
USMART的实现流程简单概括就是:第一步,添加需要调用的函数(在usmart_config.c里面的usmart_nametab数组里面添加);第二步,初始化串口;第三步,初始化USMART(通过usmart_init函数实现);第四步,轮询usmart_scan函数,处理串口数据。1 i9 w% C, s+ Q, `) r8 p
经过以上简单介绍,我们对USMART有了个大概了解,接下来我们来简单介绍下USMART组件的移植。USMART组件总共包含8文件如图26.1.1所示:
1 z/ s( U: P  |+ w7 z+ I! p' c. `9 d9 }: g
4f56a0cc3b2d462ba340f6288e742c48.png " J8 u1 ^( e2 ], t: t  \- o4 \

& ?# B* m6 m) }# W2 u2 {  e图26.1.1 USMART组件代码
1 ^  b" d1 q; eUSMART每个文件的作用如表26.1.1所示:
) e4 Z1 M6 H6 P, |$ ]7 x
( r% w/ l1 D, X b3fe3a0b3c17468b957564ef69962b99.png / a& y! U* q. W2 e. U7 f

' k# ~* J6 ~! s$ x表26.1.1 USMART文件介绍7 }' m: h% `( ?4 ]) \1 U$ q0 R
经过以上简单介绍,我们对USMART有了个大概了解,接下来我们将在下一小节给大家介绍USMART组件的移植和使用。& c$ f# O1 f( H9 F7 K
% v! d' S* G5 R8 t! A# ]9 P
26.2 硬件设计
5 E% [/ u! T, q8 |  a  b7 u' U1.例程功能; Z8 o. Y* [$ n7 M& d
本实验通过usmart调用单片机里面的函数,实现对LCD显示和LED以及延时的控制。LED0闪烁用于提示程序正在运行。& V8 g5 |' W1 F9 g$ \, |) {2 l" s
2.硬件资源
5 P4 U( ?) f% E" U# E& Q( z1)RGB灯
! i$ {  C3 e4 o4 f/ U% n4 mRED :LED0 - PB4
+ z" X4 k0 Z4 i6 c; _- l* F1 ]GREEN :LED1 - PE64 }- T. w8 h6 n- t7 w# I$ R. b
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)" Z5 E; v2 K6 k9 ^" s
3)定时器4
3 N2 q8 Y# \: Q$ @, z5 Z2 C3 c: r" q4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
& `8 R+ u, v( d) w4 w6 E' d$ g7 |7 V) l
26.3 程序设计. y6 |- X* M. Z: v0 O7 W* v# c
26.3.1 程序流程图

8 t! E9 l7 L" Q" l$ e7 L4 L$ q9 B+ R9 j$ ]/ B
c7e8856dc98547fb997dc29230c86a6d.png
) g) M% O  [- m/ Y( U; T. c3 d6 Z, s  R- |
图26.3.1.1 USMART调试组件实验程序流程图1 x$ A* D( H2 L) V. p6 ^

6 K; U0 t3 R$ J  B' y26.3.2 程序解析2 V6 p2 `7 u! U( q7 l; _
1.USMART驱动代码
. p! Y: ^6 o7 R+ j+ \$ a/ G这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。USMART驱动源码包括七个文件:usmart.h、usmart_port.c、usmart_port.h、usmart_str.c、usmart.c、usmart_config.c和usmart_str.h。
* @, K5 r& G/ V6 n, O+ G2 }* {8 z3 D要使用USMART,我们先得进行代码移植,USMART的移植非常简单,我们只需要修改usmart_port.c里面的5个函数即可完成移植。8 Q! v0 K) f5 F+ e
第一个函数,USMART输入数据流获取函数,该函数的实现代码如下:5 n( z# p+ [/ v! c$ s
  1. /**
    6 a" i' i8 H0 |8 {% r* [" W& j
  2. * @brief             获取输入数据流(字符串), O  d0 m# }! B. @
  3. *   @note             USMART通过解析该函数返回的字符串以获取函数名及参数等信息1 ~$ P$ K4 K) l: e3 g
  4. * @param              无
    0 Z: _7 Z' n9 j
  5. * @retval/ v5 [. o9 x" @8 a" j
  6. *   @arg              0,  没有接收到数据8 g5 V) b9 T* a
  7. *   @arg              其他,数据流首地址(不能是0)
    ; V/ X" X! C3 B! U; }, M# Q
  8. */3 o" `! U( y( r" z$ O  v
  9. char *usmart_get_input_string(void)) \. C, R/ P/ L% T
  10. {2 i+ M0 M( D$ j! H' q1 j6 |- m3 @
  11.     uint8_t len;
    : K1 M* r" Q) L" Q; F
  12.     char *pbuf = 0;, P3 L* ]* [& ?; B3 u
  13.     if (g_usart_rx_sta & 0x8000)                /* 串口接收完成? */
    # [5 Q3 p$ ?7 V$ ^$ l" v+ k
  14.     {
    3 j9 K* x& r4 r2 V" O0 A+ ~
  15.         len = g_usart_rx_sta & 0x3fff;          /* 得到此次接收到的数据长度 */
    % Q! `1 g- `, W( G+ Q: `
  16.         g_usart_rx_buf[len] = '\0';             /* 在末尾加入结束符. */
    $ ?/ Y- Y- }$ m3 H+ d% a
  17.         pbuf = (char*)g_usart_rx_buf;
    * W$ J6 [  }5 ]+ D
  18.         g_usart_rx_sta = 0;                     /* 开启下一次接收 */
    6 \7 _, l/ E9 y
  19.     }! `9 D) o  @$ J# H' z# \
  20.     return pbuf;
    0 k! q& A$ L7 o% E" z
  21. }
复制代码

5 g5 m$ X4 I4 x) l% c该函数通过SYSTEM文件夹默认的串口接收来实现输入数据流获取。SYSTEM文件夹里面的串口接收函数,最大可以一次接收200字节,用于从串口接收函数名和参数等。大家如果在其他平台移植,请参考SYSTEM文件夹串口接收的实现方式进行移植(详细接收原理请参考:串口通信实验)。
, x" @' n1 h0 J2 \# K$ W# M( u/ p6 I1 Q$ j, h# B. O/ j. _: d8 y. w
第二个是usmart_timx_init函数,该函数的实现代码如下:
. r% H: F6 f" P1 S+ x* D
3 S. i, U& n, r! d, ^, G, E2 u( c+ K
  1. /**
    & l" |8 e0 o4 _9 S) O4 r
  2. * @brief             定时器初始化函数
    ' l' ]1 |/ \2 W: F
  3. * @param              arr:自动重装载值( g% u! C' r; L5 ~
  4. *                      psc:定时器分频系数, W# P- C6 R" \4 e' n
  5. * @retval            无
    2 {+ w% w4 @! A* Q" K+ d' H2 l" p
  6. */
    2 @& C: z1 s7 V7 m# Q9 e9 D9 f+ T" y6 b
  7. void usmart_timx_init(uint16_t arr, uint16_t psc)
    9 E& \: |- r: ~: l/ G
  8. {
    , A" |9 f1 k: [' F
  9.     USMART_TIMX_CLK_ENABLE();
    7 x" Y  t/ @) p: r
  10.     g_timx_usmart_handle.Instance = USMART_TIMX;    /* 通用定时器4 */- w4 L$ L- {! Y9 [% H
  11.     g_timx_usmart_handle.Init.Prescaler = psc;              /* 分频系数 */$ _5 o1 a4 q+ q4 w
  12.     g_timx_usmart_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  /* 向上计数器 */' w0 @/ {: m) U- X7 h
  13.     g_timx_usmart_handle.Init.Period = arr;          /* 自动装载值 */# }+ M7 W! x/ @
  14.     g_timx_usmart_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    ! F/ `2 p- e) O4 g- r
  15.     HAL_TIM_Base_Init(&g_timx_usmart_handle);' B( a4 L3 V/ F( ~& [1 U
  16.     HAL_TIM_Base_Start_IT(&g_timx_usmart_handle);          /* 使能定时器和定时器中断 */& }: o+ o# D& B5 t
  17.     HAL_NVIC_SetPriority(USMART_TIMX_IRQn,3,3);            /* 抢占优先级3,子优先级3 */- }$ ?3 E0 y, C( a& M: r6 o+ {7 X
  18.     HAL_NVIC_EnableIRQ(USMART_TIMX_IRQn);                    /* 开启TIM中断 */ 9 \7 z% w, R. x1 Q
  19. }
复制代码

: r. P0 P& a0 K该函数只有在:USMART_ENTIMX_SCAN值为1时,才需要实现,用于定时器初始化,利用定时器完成对usmart_scan函数的周期性调用,并实现函数运行时间计时(runtime)功能。该函数里面的:USMART_TIMX_CLK_ENABLE、USMART_TIMX和USMART_TIMX_IRQn等定时器相关宏定义我们在usmart_prot.h里面定义,方便大家修改。本实验我们用的TIM4。1 M. ?- }9 C; |8 p$ R* E
注意:usmart_timx_init函数是在usmart.c里面,被usmart_init调用,usmart_init源码如下:
& i  q; I8 |: J; P' X/ o0 Z* j4 z$ x6 Y, h9 s1 H
  1. /**
    , i' R! ~- s0 R8 c6 T3 M8 S
  2. * @brief              初始化USMART
    ' G' _, v) y1 Y/ s: {
  3. * @param              tclk: 定时器的工作频率(单位:Mhz)
    ) k1 @8 R- Z6 S( [
  4. * @retval             无$ P8 `2 T6 k' l+ i4 Y- t( d
  5. */) c" i$ g9 Y, |
  6. void usmart_init(uint16_t tclk)
    3 D) A0 S' s) I# O. I
  7. {, e# u2 s5 S! {& |2 q! m2 \/ r
  8. #if USMART_ENTIMX_SCAN == 1
    $ z7 ]# _, n! F3 H2 `3 s( U
  9.     usmart_timx_init(1000, tclk * 100 - 1);
    " \: S3 {, T- s6 o: Q* ?$ t
  10. #endif
    # I! W% ~3 A2 C2 ?' f5 k5 K0 Z
  11.     usmart_dev.sptype = 1;          /* 十六进制显示参数 */( d3 s, ]) n! ~3 V! a
  12. }
复制代码
7 m+ E& i* i* p
该函数有一个参数tclk,就是用于定时器初始化。注意:这里的tclk是指所选TIM的时钟频率,而非系统主频!这里我们用的TIM4,对于STM32H750来说,TIM4的时钟源来自2倍的APB1,频率为:240Mhz。( Y; k- _% t" q# o
另外,USMART_ENTIMX_SCAN是在usmart_port.h里面定义的一个是否使能定时器中断扫描的宏定义。如果为1,就初始化定时器中断,并在中断里面调用usmart_scan函数。如果为0,那么需要用户需要自行间隔一定时间(100ms左右为宜)调用一次usmart_scan函数,以实现串口数据处理。注意:如果要使用函数执行时间统计功能(runtime 1),则必须设置USMART_ENTIMX_SCAN为1。另外,为了让统计时间精确到0.1ms,定时器的计数时钟频率必须设置为10Khz,否则时间就不是0.1ms了。
3 S) _( ]$ i" h/ G" y* P7 R第三和第四个函数仅用于服务USMART的函数执行时间统计功能(串口指令:runtime 1),分别是:usmart_timx_reset_time和usmart_timx_get_time,这两个函数代码如下:
$ U0 G5 |  |6 S+ H% h7 ]6 E; o2 d& V3 d- Q( V
  1. /**
    , B. b/ O  m! I, h
  2. * @brief            复位runtime
    0 z& w8 G5 n: |. c& R$ O
  3. *   @note             需要根据所移植到的MCU的定时器参数进行修改' ^2 F% H3 [; D0 b" V+ o( N5 \. g
  4. * @param              无
    * V2 ~" I. t9 i# E/ K- p
  5. * @retval             无0 @# g7 A2 K. U
  6. */
    ' ]: }6 g; x* H2 j; p/ B
  7. void usmart_timx_reset_time(void)8 r* I, ~/ o8 M4 a6 C' z; y" S
  8. {
    * x% f. y" ]+ \, b8 Q
  9.     __HAL_TIM_CLEAR_FLAG(&g_timx_usmart_handle,TIM_FLAG_UPDATE);/* 清除中断标志 */3 C: {; K& i; @6 S* }' z& O- j
  10.     __HAL_TIM_SET_AUTORELOAD(&g_timx_usmart_handle,0XFFFF);        /*重载值设置最大*/9 }! j: k* A' _( y- x! k
  11.     __HAL_TIM_SET_COUNTER(&g_timx_usmart_handle, 0);                        /* 清定时器CNT */! @( f8 z$ }1 {/ O# z! `+ O
  12.     usmart_dev.runtime = 0;
    * T& W) |% {6 m7 Y8 e# i, r
  13. }: r1 }- @, Y+ i7 C
  14.   M# C8 k9 F( Q- V# q
  15. /**2 `1 P1 H9 A* [) J4 c! }
  16. * @brief             获得runtime时间
    : V6 k7 o0 L9 P; |* z- S/ T: \
  17. *   @note             需要根据所移植到的MCU的定时器参数进行修改; e) ]& n  v9 o% F- |) E$ [% u
  18. * @param              无, q( V7 e4 j' ~* U1 A* t0 T
  19. * @retval             执行时间,单位:0.1ms,最大延时时间为定时器CNT值的2倍*0.1ms# c( q  y0 L5 D. [2 R, `% ~- n
  20. */6 M- A1 c; E9 z- {
  21. uint32_t usmart_timx_get_time(void)% E/ z; e$ W" u0 t1 f# d5 o
  22. {" I+ ?4 S1 l# [" S, S" G. @0 N
  23. /* 在运行期间,产生了定时器溢出 */  J& e1 K/ u# F$ g' x# W
  24.     if (__HAL_TIM_GET_FLAG(&g_timx_usmart_handle, TIM_FLAG_UPDATE) == SET)  ) z/ \" Z) x4 h3 f2 V0 u3 d
  25.     {; p9 b/ w/ j1 F' y; \
  26.         usmart_dev.runtime += 0XFFFF;
    3 n' s. L  V7 t- x$ c* v5 S4 ?
  27.     }
      ^8 g: }1 f) P+ I
  28.     usmart_dev.runtime += __HAL_TIM_GET_COUNTER(&g_timx_usmart_handle);% c, |; {+ t3 f% i
  29.     return usmart_dev.runtime;                                 /* 返回计数值 */: l  h0 G( K- s' q' a2 }
  30. }$ J' n' |' L% s: G- {0 b6 @
复制代码

5 B1 d, E5 Z. r4 V& a7 T+ xusmart_timx_reset_time函数在每次USMART调用函数之前执行,清除定时器的计数器,然后在函数执行完之后,调用usmart_timx_get_time获取计数器值,从而得到整个函数的运行时间。由于usmart调用的函数,都是在中断里面执行的,所以我们不太方便再用定时器的中断功能来实现定时器溢出统计,因此,USMART的函数执行时间统计功能,最多可以统计定时器溢出1次的时间,对STM32H750的定时器4,该定时器是16位的,最大计数是65535,而由于我们定时器设置的是0.1ms一个计时周期(10Khz),所以最长计时时间是:
* `: c) U# n3 |* ~0 a! k6553520.1ms=13.1秒
. r  f: J: O% ?# {也就是说,如果函数执行时间超过13.1秒,那么计时将不准确。2 ~7 d9 ?- ]& Q: C( ~& Z$ j1 \
最后一个是USMART_TIMX_IRQHandler函数,该函数的实现代码如下:
( I- I% O" S2 \! @+ I5 }1 }
) x6 X; Q/ \6 b/ V9 U* `
  1. /**
    * O# Z! q' j! B
  2. * @brief              USMART定时器中断服务函数; J6 l  @6 y( D1 }7 W3 z
  3. * @param              无
    " ^/ v9 h' X0 \$ g$ v0 c
  4. * @retval             无
    . u: P: Q. a; W
  5. */
    # _% p. m  I! M1 `- t1 U
  6. void USMART_TIMX_IRQHandler(void)8 r1 d$ c9 a% L
  7. {, z. v9 q6 k) s( s
  8. /* 溢出中断 *// x, S( Z; i  P4 x
  9.     if(__HAL_TIM_GET_IT_SOURCE(&g_timx_usmart_handle,TIM_IT_UPDATE)==SET)
    0 J1 W) |4 _# L# _2 U' Y  `
  10.     {
    . \5 l, Y) n* g: r' x* {3 I7 u* i
  11.         usmart_dev.scan();                                                           /* usmart扫描 */
    $ M5 \0 v: B# k: M( G3 B
  12.         __HAL_TIM_SET_COUNTER(&g_timx_usmart_handle, 0);;            /* 清定时器CNT */
    + D" C3 V- V7 P- c" R- n
  13.         __HAL_TIM_SET_AUTORELOAD(&g_timx_usmart_handle, 100);        /* 恢复原来的设置 */' C1 b2 W1 Q: r, D
  14.     }9 }2 Z2 [- `. @( s
  15.     __HAL_TIM_CLEAR_IT(&g_timx_usmart_handle, TIM_IT_UPDATE);        /* 清除中断标志位 */9 b$ |  P4 J+ n) s- _+ `
  16. }
复制代码

' ~$ t( W2 f3 u3 M" X" u' ^该函数是定时器TIMX的中断服务函数,也是一个宏定义函数,同样是在usmart_port.h里面定义,方便大家修改。该函数主要用于周期性调用usmart扫描函数(实际函数:usmart_scan),完成对输入数据流的处理。同时,清除定时器的CNT值,并设置自动重装载值。7 R$ x* F) I( k% E
完成这几个函数的移植,就可以使用USMART了。不过,需要注意的是,usmart同外部的互交,一般是通过usmart_dev结构体实现,所以usmart_init和usmart_scan的调用分别是通过:usmart_dev.init和usmart_dev.scan实现的。( i& o# t, u9 ?6 ~" [
另外我们还需要在usmart_config.c文件里面添加想要被USMART调用的函数。打开usmart_config.c文件,如图26.3.2.1所示:6 h4 \% @, v/ s1 d0 ^! p+ T
; y1 s5 s- I2 x  N* r2 m
2a0bebf941ea4614b93e686f075559ce.png
- f0 D5 A+ }5 `  E/ B4 z9 Z* W+ S! y6 H6 Y1 D
图26.3.2.1 添加需要被USMART调用的函数
1 W3 W  V! Z5 R3 W3 L7 b6 o这里的添加函数很简单,只要把函数所在头文件添加进来,并把函数名按上图所示的方式增加即可,默认我们添加了两个函数:delay_ms和delay_us。另外,read_addr和write_addr属于usmart自带的函数,用于读写指定地址的数据,通过配置USMART_USE_WRFUNS宏定义,可以使能或者禁止这两个函数。
$ Q* u7 f8 x2 y- V: b( {$ k. J这里我们根据自己的需要按上图的格式添加其他函数,usmart_config.c文件中我们已经添加了LCD的相关函数,大家可以查看本实验的usmart_config.c文件,并可在串口助手中调用。具体的调用方法26.4小节有具体讲解。4 m' z# m  C9 O" k: F' i. H, {3 E
2. main.c代码* ~5 _3 G3 @) w" x1 i2 r: F
在main函数之前,我们添加了led_set和test_fun两个函数,代码如下:
  1. /* LED状态设置函数 */
    / q5 }4 X# D, C! _
  2. void led_set(uint8_t sta)
    $ K) Y! \0 f( f6 x
  3. {
    - ]7 g' X2 y( l$ I. ?1 u: \
  4.     LED1(sta);
    7 [% F- ^, C; \7 E9 j# u, A- o
  5. }
    . V: f# T1 a. p$ k
  6. " Z7 O: E' e1 o) z3 x
  7. /* 函数参数调用测试函数 */
    6 q* G9 V" F# s% z, ?" ]0 F
  8. void test_fun(void(*ledset)(uint8_t), uint8_t sta)
    + `: g* Q+ A( D* m& h' J
  9. {7 W, o  z; n& X2 K6 t/ X
  10.     ledset(sta);! E% i2 H' e2 L& h' b0 w* G. u; @
  11. }
复制代码
: z& H) V5 E  |% l# J. u4 ]' m
led_set函数,用于设置LED1的状态,而第二个函数test_fun则是测试USMART对函数参数的支持的,test_fun的第一个参数是函数,在USMART里面也是可以被调用的。
% A  p) |. z7 F; r2 b! p1 X' d: u# e$ d& _4 X0 Y8 @
main函数代码如下:
! e! ]* H( M% N9 z' _# P1 m* Q: y5 ?
2 _  L: }7 z* Y% w; `8 x! F) p7 X# c' y
  1. int main(void)% ], u8 e3 @5 F) X3 C2 |' S
  2. {" Z( B. T' u3 q; |# _0 f+ P* P
  3.     sys_cache_enable();                            /* 打开L1-Cache */( ]5 n3 {1 i" n+ \
  4.     HAL_Init();                                      /* 初始化HAL库 */5 p  r: S7 f# s  o) e' z
  5.     sys_stm32_clock_init(240, 2, 2, 4);         /* 设置时钟, 480Mhz */0 }$ }9 i1 s/ D" ^' Z- }" i0 a: z
  6.     delay_init(480);                                /* 延时初始化 */
    * B* h4 Q) ]6 V4 F# q
  7.     usart_init(115200);                            /* 串口初始化为115200 */
    % r+ T( U3 ?/ {$ F9 u
  8.     usmart_dev.init(240);                          /* 初始化USMART */
    $ ~5 d& U3 Y0 g% c' o- F" r3 e- g
  9.     led_init();                                      /* 初始化LED */- s4 G5 c, A/ O: l: X# {% O2 y
  10.     mpu_memory_protection();                      /* 保护相关存储区域 */
    3 P9 {. {1 D4 R6 t/ ^; O
  11.     lcd_init();                                             /* 初始化LCD */
      N; Q, C" Y8 w; y; k% D' \0 t* |
  12.     lcd_show_string(30,50,200,16,16,"STM32", RED);
    9 e- P: P: T, y, p7 z
  13.     lcd_show_string(30,70,200,16,16,"USMART TEST", RED);
    ; S/ x5 s3 n7 f& g: _
  14.     lcd_show_string(30,90,200,16,16,"ATOM@ALIENTEK", RED);5 C: G6 l( p% L4 ]8 H' X
  15.     while(1)
    - [; M) q3 f0 d- r7 u7 t
  16.     {8 r$ S& @" ]; n) Q2 X  V% h
  17.         LED0_TOGGLE();  /* LED0(RED) 闪烁 */7 @: n) b$ L3 X/ }5 e, t
  18.         delay_ms(500);3 L) L# @; D$ D, _. L3 G. X
  19.     }  p- y) K) Q) U+ R
  20. }
复制代码

4 H; B& ^+ `2 }9 z+ {- z此部分代码功能如下:经过一系列初始化,显示使用信息后,就是在无限循环中LED0翻转延时,并等待串口数据。% k! ~- w7 J2 M9 O

" ?1 }1 p8 J+ c6 H5 }26.4 下载验证
" s7 i9 ?/ x$ r; j* V将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。同时,屏幕上显示了一些字符(就是主函数里面要显示的字符)。8 _' g; Z  U7 n& R# i
我们打开串口调试助手XCOM,选择正确的串口号多条发送勾选发送新行(即发送回车键)选项,然后发送list指令,即可打印所有usmart可调用函数。如下图所示:
( {) t, X, f7 p" v* f: p, X4 U- I# O! Z
068b21617c73489fae059aca4f27992e.png
9 z" Y2 ~$ p# w8 A0 L7 \
: E4 B6 z, S+ y( m图26.4.1 驱动串口调试助手2 S5 ~0 o1 _6 F0 D) E6 N) B
上图中list、id、help、hex、dec、?和runtime都属于usmart自带的系统命令,点击后方的数字按钮,即可发送对应的指令。下面我们简单介绍下这几个命令:
  i% [0 d/ m3 Elist,该命令用于打印所有usmart可调用函数。发送该命令后,串口将受到所有能被usmart调用得到函数,如图26.4.1所示。
0 j( ?' T9 `* ]6 hid,该指令用于获取各个函数的入口地址。比如前面写的test_fun函数,就有一个函数参数,我们需要先通过id指令,获取led_set函数的id(即入口地址),然后将这个id作为函数参数,传递给test_fun。
% O# B* n* M9 i. `" E, g( H( P4 zhelp(或者‘?’也可以),发送该指令后,串口将打印usmart使用的帮助信息。2 Q7 X7 B0 W2 Z: k
hex和dec,这两个指令可以带参数,也可以不带参数。当不带参数的时候,hex和dec分别用于设置串口显示数据格式为16进制/10进制。当带参数的时候,hex和dec就执行进制转换,比如输入:hex 1234,串口将打印:HEX:0X4D2,也就是将1234转换为16进制打印出来。又比如输入:dec 0X1234,串口将打印:DEC:4660,就是将0X1234转换为10进制打印出来。
7 |2 |' b" T+ y9 Y' ]$ \4 Cruntime指令,用于函数执行时间统计功能的开启和关闭,发送:runtime 1,可以开启函数执行时间统计功能;发送:runtime 0,可以关闭函数执行时间统计功能。函数执行时间统计功能,默认是关闭的。
# o1 _% d2 ~% _' `3 C大家可以亲自体验下这几个系统指令,不过要注意,所有的指令都是大小写敏感的,不要写错哦。
! o) p" w. o" U  x) e, S接下来,我们将介绍如何调用list所打印的这些函数,先来看一个简单的delay_ms的调用,我们分别输入delay_ms(1000)和delay_ms(0x3E8),如图26.4.2所示:& q- L3 I8 a/ e, _; A% A

2 u$ E8 j* u" N  Z 2d932bc28ff9482088f35ff10de1fdac.png
1 @' G2 w0 ?1 C; I9 M- _" }; T& Y2 I5 K- _/ a, t, B
图26.4.2 串口调用delay_ms函数
2 B% [$ E' Q. p- O从上图可以看出,delay_ms(1000)和delay_ms(0x3E8)的调用结果是一样的,都是延时1000ms,因为usmart默认设置的是hex显示,所以看到串口打印的参数都是16进制格式的,大家可以通过发送dec指令切换为十进制显示。另外,由于USMART对调用函数的参数大小写不敏感,所以参数写成:0X3E8或者0x3e8都是正确的。另外,发送:runtime 1,开启运行时间统计功能,从测试结果看,USMART的函数运行时间统计功能,是相当准确的。- e/ u: o5 Z( G4 d/ }
我们再看另外一个函数,lcd_show_string函数,该函数用于显示字符串,我们通过串口输入:lcd_show_string(30,200,200,16,16," This is a test for usmart!!", 0xF800),如图26.4.3所示:
$ m/ r7 _( T4 a/ d6 ?2 R8 v4 ~2 C: w4 R/ A/ H2 A  P5 K3 A
d35713fe2c734e519632cba9bbbe5b5f.png
4 h+ u4 f  m; E$ `& O( k2 u% l! g$ P/ Z. s$ ]/ t
图26.4.3 串口调用lcd_show_string函数
( G& e% @6 V; z  F& ?) p) ~该函数用于在指定区域,显示指定字符串,发送给开发板后,我们可以看到LCD在我们指定的地方显示了:This is a test for usmart!! 这个字符串。
4 G# k2 s1 r: Y  h, f3 t其他函数的调用,也都是一样的方法,这里我们就不多介绍了,最后说一下带有函数参数的函数的调用。我们将led_set函数作为 test_fun的参数,通过在test_fun里面调用led_set函数,实现对LED1的控制。前面说过,我们要调用带有函数参数的函数,就必须先得到函数参数的入口地址(id),通过输入id指令,我们可以得到led_set的函数入口地址是:0X0800321D,所以,我们在串口输入:test_fun(0X0800321D,0),就可以控制LED1亮了。如图26.4.4所示:
. [* Y, g. ^+ B2 z2 T# ^( i9 Z6 W- ^% ?; ?' I/ n; h$ y
18a2a8e872ff48099bef4482abc2ec86.png ! H8 J! u: s/ E  Q8 m
4 {5 r- E: d- j- [; y  m; p
图26.4.4 串口调用test_fun函数
/ h& W9 S3 S# j9 A在开发板上,我们可以看到,收到串口发送的test_fun(0X0800321D,0)后,开发板的LED1亮了,然后大家可以通过发送test_fun(0X0800321D,1),来关闭LED1。说明我们成功的通过test_fun函数调用led_set,实现了对LED1的控制。也就验证了USMART对函数参数的支持。
* L4 s7 j- b4 }$ j! M) WUSMART调试组件的使用,就为大家介绍到这里。USMART是一个非常不错的调试组件,希望大家能学会使用,可以达到事半功倍的效果。1 u$ ~- p( s* f- d3 s: K# E
————————————————
7 G- t- ]3 q" y8 B! ~1 R版权声明:正点原子4 q% J) a% M7 k  \
: a  @$ E" M0 [' U
) t' |# f* N, o2 d

/ q4 V9 F, p8 j$ g4 }( t0 w
收藏 评论0 发布时间:2022-10-7 18:27

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版