前言1 J+ ~/ @, F2 e/ g1 }! k( u
最近用了NUCLEO的板子做一些东西,用到了串口通讯,结合一些资料再加上自己整合基本实现了发送和接收任意长度数据段(第一次写博客有点小紧张哈哈)。/ E( o4 M$ I T% E( M; Q; V
! f: s4 U. S+ b1 y: HCubeMx软件的配置过程* f4 n F) h' V U* U: D5 d' k
首先,建立新的工程,选择芯片类型,这里我用的是stm32l073rz。
% o$ t. Q# a' v7 W) M/ h1 W% a D! D- {3 `: } A% ]
# f3 W) E) B3 T3 s: K+ ?
下面选择sys部分,勾选Debug Serial Wire (这个好像是下载调试 st-link,也不是很清楚)。" B w1 H, y& u9 G
3 \. F, i6 G4 S3 T
+ d. Q( `0 z& c3 |1 c- F, \% K) R$ ^- g( m- p& I7 O0 t
选择USART2,选择模式Asynchronous(异步通信)模式。
+ v' A+ B; Q5 ]- w. ~6 R3 }' N( \; P6 M/ i
5 u4 h/ q, f- Q
; L2 K( _" U. I1 I( A+ i* d选择下面DMA模式 ,添加rx tx,并设置tx为循环模式。( a- T: |4 `. t% T
; e# y# z# O* h. W+ y; X
1 w" n4 X6 E# u7 P
: r- z, I5 l7 j: ^/ w: N5 d% g勾选USART2全局中断。. U5 {/ m; p+ m7 u
! h# k4 n2 ]: |2 e ]3 v
) D/ ^1 a! r c# L( c1 H8 m' p5 Z( \9 G/ y0 ~+ N' I7 |
保存工程,选择相应的选项,生成代码即可。
- G4 V0 T7 N1 g2 t( L7 ?0 Y. ~
6 ^. J* @% r- h' F! h- C6 y; K3 G4 J5 ]" ]" l
2 l+ q# j1 l' A: b! ]" s; w5 z$ P3 {( u& i- N
代码部分
; i* `7 d8 F9 r$ R, v% v! @. hC语言的重定向fputc函数
, f0 L% _( X! _/ \- int fputc(int ch,FILE *f)
9 @! E! i0 S8 R4 c4 a3 p# Y7 D4 s - {# L; G$ R- I% [! B# `9 X: n0 k1 F
- HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF); d) ]. L/ L1 v& y' ~
- return ch;
4 d3 }: h3 K' W# m7 Z# T - }
复制代码
) A. u8 T4 \2 r( a8 i P R4 d% h. ~ch为要写入的参数,fp指向FILE结构的指针;这样重新定向后就可以使用printf直接输出。
$ m" H' S3 L6 a9 j1 g ]4 l/ q
0 U" _ H( Z( c变量定义6 t6 A) i |+ i4 J4 M! z$ b5 [" q7 R
- #define RX_LEN 1024 //定义总的数据长度
2 S; f+ R. ^) \8 S1 P( H - volatile uint8_t RX_flag=0; //IDLE 接收标志
# d- t! ?3 z: V2 R* X/ t* m - volatile uint8_t RX_Size=0; //接收的数据段长度1 j, i6 @6 a3 U( @6 Z* |
- uint8_t RX_pData[RX_LEN]; //DMA 接收的数组
复制代码 ' B6 ?$ [, G, [. C3 q _
volatile 关键字提醒编译器定义的变量是易变的,编译后的程序每次需要存储或读取该变量时,会直接从变量地址读取数据。在中断或多线程中使用volatile关键字可以避免不同优化等级时程序出错,提高程序的鲁棒性。
' c6 w7 V; M, R: X# S# W2 d2 P$ c/ ^: W* i4 C" x3 X
中断函数9 }9 i8 }, z, T, q
- void UsartReceive_IDLE(UART_HandleTypeDef *huart) - q: t! q8 q: m. G
- {
1 y) b$ e* q1 \9 c5 h8 ^ - uint32_t temp;
* t$ L: T: |" b( y! l2 l - uint32_t tmp_flag=0;
' A; S! g J* W - tmp_flag =__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE); // USART_IT_IDLE中断,是串口收到一帧数据(也可以叫做一包数据)后,发生的中断。
) u9 G$ m1 r( j5 B2 z; a: O - if((tmp_flag != RESET))//tmp_flag不等于0+ f! ^1 b' t% o+ |
- { ( w: x! Q, [+ G* D8 D r
- __HAL_UART_CLEAR_IDLEFLAG(huart); //tmp_flag=0;3 r/ W6 u9 j# \' t" {0 T
- HAL_UART_DMAStop(huart);
; {( @! `* f Y8 V - temp = hdma_usart2_rx.Instance->CNDTR; //cndtr为未传输的数据
) S: K5 r4 m8 W. a8 V+ F+ P. F - RX_Size = RX_LEN - temp; //总数减去未传输的,得到发送的数据长度
& @- f$ d/ I$ S9 h/ m/ J - RX_flag=1;
/ e' d$ _7 [ I' \, u6 e - } 4 ?: l2 e# u& X; W! @! B
- }
复制代码 + y& l6 t% s9 f
, z5 h" A+ c. c& z9 F8 i7 _在中断请求函数加入自定义的中断函数。! G* c! W$ N7 _1 ?4 ~
; f: }+ A# K0 G7 J8 e5 P- void USART2_IRQHandler(void)//串口中断请求
, J1 c `* q0 c6 [ - {
/ ^7 M& `1 P: n3 ^ - /* USER CODE BEGIN USART2_IRQn 0 */0 ]( h; ?, m, b0 P" x
- UsartReceive_IDLE(&huart2);% d0 \6 v* R# {/ ?2 P& W7 ~8 M
- /* USER CODE END USART2_IRQn 0 */
1 n' ?* B- _: ^2 ` - HAL_UART_IRQHandler(&huart2);
! f |" p& {+ W k
- F: L: j- f. l2 _# t- } ( r) s h5 e: x ^$ Y! Q, V
- static void MX_USART2_UART_Init(void)//在这个中间加入使能函数 切记切记
6 M' \! p! U6 }* n% |' | d - {
0 q$ _( ?& m' b* ]; i6 r4 o - /* USER CODE BEGIN USART2_Init 2 */
/ b& u2 s& C5 n* E - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//使能idle中断+ \( o" S0 _$ v3 L+ |
- / O; \" b# {" }7 {4 S7 c8 G
- /* USER CODE END USART2_Init 2 */% m" Y9 C0 O5 R; m3 u/ M {- V# |, K
- 8 P2 ? Z7 p) r- C
- }
复制代码 Q% t% ~- s& }; h* c
发送主函数: u# d! ^' e/ V
- void uart_pc_com (UART_HandleTypeDef *huart)
" C( S1 V; c8 n% {! Z, } F5 Y" n$ x - {, I5 d# p ^6 x! ~' I( V
- if(RX_flag ==1)
- H# I* C) x" \! \6 b( B& v - {# B# |% z5 D- f: C# v
- printf("rx_len=%d\r\n",RX_Size);//\r是回车符,\n是换行符* g( h9 a- b9 u5 X) y$ M$ }
- HAL_UART_Transmit(&huart ,RX_pData, RX_Size,200);
0 v3 D& h. }1 O7 }" d - for(uint8_t i=0;i<RX_Size;i++)
9 {! J5 b! r+ y - {, N4 q8 A$ g. f3 c0 R# r: N
- RX_pData<i>=0;//清除缓存
- j9 W" L7 t2 @( m% A9 p - </i> }! y( X, S$ ?% {# L R- _
- RX_Size=0;3 i) }' R" _$ H4 H. T% l
- RX_flag=0;//清除结束标志位
$ r" W' o5 A8 C- M) S" q3 d' [: ~& } - }
) ^9 t9 y' ?9 @+ M* M+ ? - HAL_UART_Receive_DMA(huart,RX_pData,RX_LEN );//打开dma数据存入rx——RX_pData
* ^- a+ |- ?& F - //加在 MX_USART2_UART_Init中 pc串口助手只能接受到一次数据 只有处于main函数while循环才能一直接受发送
& s/ U4 N/ G, p5 x - }
复制代码
) G( }4 h6 ]/ N: c' p! |在main函数的while循环里面加入此函数,并注明使用的uart。! _/ J9 |1 J& c$ o0 z* u7 m
+ _& t7 r9 H6 L( N7 v* \7 |
对于程序的理解3 s" A2 w1 Q: Y; f
首先uart接受数据,触发idle中断,进入中断请求函数,获取接受到的数据以及数据长度,并将标志位置1,进入主函数输出到串口助手5 [; k' L. J0 W7 J7 Z; s% o
v/ }( @# {8 O% s' R0 P% H串口助手效果0 k1 Y6 H( a9 h. Z: [" q1 { e4 t
: s" e9 h, ?7 R/ b, G基本可以实现本次的目的,首次写,表达不够清楚,望理解。
5 Q# k4 }0 m& Y! S$ {+ T' J' U6 j& h# E6 H9 F$ x1 ?1 A5 H* E
7 {2 U1 J7 Z. n L# K
3 h0 e8 L$ ^/ _% k. Y- E |