|
一个STM8S ADC脚与其它功能复用时的话题 , J2 K% z4 s- Z3 f8 W- i2 R2 [
2 I" V9 f) q0 B
一年多前写过一篇《STM8S芯片GPIO脚复用AD功能后无法回到GPIO状态问题》的小文,介绍STM8S芯片的ADC应用时相关施密特触发器未适时开关而导致的问题。
( s0 b! N/ n: G, g2 U/ _
大致内容就是某一GPIO口被复用为AD输入脚做相关AD检测。之后,把该脚AD功能禁用掉,再配置切换为带下降沿触发的外部中断触发脚,让其作为芯片休眠唤醒脚。
! ~# I5 T) ~- q5 _9 H; f% u% h9 `/ h
奇怪的是,那样设置后根本没法唤醒。即使不做休眠,做好切换配置后,直接查看该脚的IDR位的电平,不管外部输入如何,发现对应IDR位始终提示为0. & Z0 U1 L! P' d5 k2 A2 A
后来找到原因是跟那个施密特触发器的配置有关。可能有人觉得该问题是钻牛角尖,其实,也不尽然。毕竟应用需求是五花八门的,遇到的问题往往也是五彩缤纷,问题不论大小折磨起人来也是不分男女老少的。 4 c0 D7 ] X7 C( a7 C, ^
这里再次分享个类似话题 ,希望能让见到本文的人有所启示。工程师反馈基本情况如下:
9 B6 x8 O7 _! t; J7 Y
使用STM8S芯片开发。因为TIM1/2都用做PWM了,所以用TIM4来做基本定时。 TIM4正常中断,UART1串口发送正常,就是串口接收中断进不去。 但只要把 TIM4_initialzation();屏蔽掉,串口马上正常中断接收,一旦打开TIM4,串口就接收不了,其它功能都正常。 6 o1 v q. M7 c
上面是该工程师对症状的基本描述和初步判断。【当然,调试遇到麻烦时候的判断难免有偏差,偏差大小因人因景不同,有时甚至完全误判。】 下面是他的主循环代码【为了排版和阅读,做了些删减】
0 |; A# f: ^: S7 j! ~2 Z
- int main( void )
d0 u C5 f) { - {0 M% b7 w" G( {" p0 X& X* K, Y
- CLK_DeInit(); //寄存器复位& U0 o' I9 \2 [' ]9 K4 T5 L
- CLK_HSICmd(ENABLE); //内部高速时钟使能 9 i0 e2 w ]& D9 _6 \8 l" D
- CLK_HSIPrescalerConfig( ); //分频
8 Y/ d2 }4 k6 }$ L* G) B - GPIO_initialzation();" t. r! O1 E( z# A1 ]
- uart_initialzation();
, ?/ _( c# c, \1 p - PWM1_initialzation();
5 t" r( C4 W( Q( A$ J4 f3 W - PWM2_initialzation();
+ f0 b; n4 _/ l) a - TIM4_initialzation(); //TIM4初始化
& E2 R$ f9 p0 k6 i4 J - enableInterrupts();//* 开启总中断 */
0 l2 o/ r4 l5 h1 T -
d0 l' J& Y6 y: M; U% _ - Ts_cnt = 1000;
% Z2 S ]9 A: S ?3 R9 N9 v - Ls_cnt = 500;* I7 ]1 ]2 U. ^2 v
-
7 F) d) b3 n! a1 e' O - while(1)
; B, ]! }+ m5 |$ n! E% r7 \+ { - {( S m9 p& A+ f5 ?5 \8 D- Y
- PLED_flash(499); //LED 闪烁
7 A' r7 ^5 [' d) \1 r: B - relay_control(); //继电器控制
: u, i* d2 m6 k! f- S; S - CCT_calculate();//获取相关AD值2 I* n7 _- y' D6 ]* u) a
- send_information();//输出提示信息0 {, O- Q- O; V3 w- X9 W% [
- if(Flag_rec)
. X# L j" e/ z" Q& j E - {9 a, g6 u" t Z# g4 N) `
- 。。。。。。【略】4 s5 p6 Y* u) `/ Q% D2 h
- } : ~: |6 o5 h( K! Z$ ^; g
- }! y( y9 P9 q) |+ ]. J- K
- }
复制代码 $ q/ ^* b2 b. M+ D7 f5 J$ G( B/ ?
现在的情况是当注释掉上面的 TIM4_initialzation();语句后,UART-RX接收中断就正常。
6 C0 r2 w$ i$ z# [
TIM4只是做基本时钟,不涉及外面其它硬件,最大可能是二者中断优先级有冲突导致UART-RX的正常接收。但当把UART-RX中断优先级调高于TIM4的更新中断时问题并无好转。
, H0 N% {1 ?# D
但事实又的确显示出TIM4的中断跟UART-RX接收有关系。
5 ]1 ~& {9 I/ M- L. t
TIM4、UART1初始化代码只是些各种相关基本配置,不跟别的外设有关联。不妨看看TIM4、UART1中断服务程序里能否找到些蛛丝马迹。 * e5 A9 F- r2 ~4 _8 @% D: U
- INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler,23)
' x+ b- ?' r" o: q2 L - {
9 \9 q* a+ |5 M$ Z6 N9 \* R - TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
# x* l6 L9 S2 c! c - ) y& \6 A+ K" a+ O
- ms_cnt++;//LED FLASH
1 z; k# d' O2 y7 A* Q/ ] - Ts_cnt++; //AD sample
$ @0 ^! S- l5 K5 X9 e1 | - Ls_cnt++; //relay control
/ U$ p1 c. I% c0 {; \0 _ - uart_cnt++;//send information& h9 `, }2 n- O
-
4 Y- j8 c3 j" G6 _# H - PWM2_duty_setting(Ts_cnt);
* M' w# k- J7 X _ - pwm1_correct_cnt++;
! ]3 `4 E* c9 d' E/ q - if(pwm1_correct_cnt > 100)
$ W( p, {0 F# i. L6 L8 p# j" Z# x - {
- r$ I9 P, ]9 T h - pwm1_correct_cnt = 0;; G$ ] p1 D' X6 y# R# y
- if(pwm1_cnt > CCT_target)
( f# {8 {7 U) e( e8 j [ - pwm1_cnt--;, D2 l9 i% c: Y
- else
9 U2 }1 H7 h/ m; f6 V - pwm1_cnt++; " q' R; d4 |& y# _# G
- PWM1_duty_setting(pwm1_cnt);
0 Y* h" t& Q: E, x* G* z5 J - }4 w0 M9 O$ |# E' l
- }/ j5 A0 I) X6 b8 B7 N9 M
- : Q% ]4 `- T1 D7 ~5 }* J7 q
- INTERRUPT_HANDLER(UART1_RX_IRQHandler,18)
6 L ]: i$ @+ B* a9 ^' ^" F4 ] - {
! L7 P- V0 W( e" G$ n0 k - static uint8_t index = 0;) D2 J" _# r7 E: Q. }1 ?3 N# a
- UART1_ClearITPendingBit(UART1_IT_RXNE);* D# A6 [3 t1 q' ?/ ~6 _1 h9 d
- recived_data[index] = UART1_ReceiveData8(); //读数据
4 w, u T" z3 u% u0 B4 R1 u - if(recived_data[0] == 0x41)
: _. F% ]; f6 q. Q- E* L$ a9 X3 E - {
: ?% y! H3 [' q: [, x - index++;/ d% [0 f# s- K9 a( p) m. e
- if((index > 7)&&(recived_data[7] == 0x0d))
/ @ L8 q0 o# Y5 O# a/ d- C8 J3 r - {8 J- g$ v" y0 y- P1 ]5 {* V3 X: J
- index = 0;" f) ^1 u: ]% H% h+ o' H# v9 R7 e
- Flag_rec = 1;: m- ~6 u6 c- C) Y5 s ~/ ?, u
- }
8 i7 {6 Q8 `) c1 F2 P6 {% \9 H - }! J7 G$ p; U A6 |5 S2 i
- else
2 C: ^2 p& W- K& Z/ ^5 F - {: t# e1 L8 y O f1 o% P, I: d* |
- index = 0;
; L1 m- {' U& T; g9 q6 A8 u- w - recived_data[0] = 0;
m- L1 `* c/ H) c# R. t - }
7 r$ Y" ]6 y7 K, \8 O) [" ^& H! J - }
复制代码 . s8 I q* ]$ d Y
从TIM4的中断服务程序里出现了好几个全局变量,看看这些全局变量哪些函数会用到。因为TIM4的主要功能就是计数定时,下面几个计时变量肯定是给别人用的。 - ms_cnt++;//LED FLASH
$ Z6 J% @: j: ^6 i1 l( J3 B7 } - Ts_cnt++; //AD sample( G. i/ j( X6 j4 }9 F, j
- Ls_cnt++; //relay control) ?; I2 g; p# b1 v0 u5 V
- uart_cnt++;//send information
$ M0 y8 x! K! u# [' J
复制代码
, J- r5 p- Q4 n' R 问题到这里,继续往下查就需要耐心了。客户代码不复杂,用到的外设模块也不多,主循环里也就下面几个函数,一个个函数模块进行排查。 + j5 S. X) m: l8 m$ K" }* S4 E
- PLED_flash(499); //LED闪烁3 F7 _# G/ [ o& C" @! O
- relay_control(); //继电器控制0 I m$ D% G$ m& u' Q
- CCT_calculate();//做AD转换
' @5 F1 A6 q' X( O - send_information();//输出提示信息
复制代码 , y" R& A# {* u6 Z
后来发现TIM4保持工作的同时屏蔽CCT_calculate();,UART-RX能正常接收。看来TIM4并非是影响UART接收的元凶。不过CCT_calculate()的运行还是跟TIM4中断有关,有个变量TS_CNT是在TIM4中断里进行累加的。
# y" M4 N: P, x( v) x
看看下面CCT_calculate()的代码,里面有个条件判断,即if(Ts_cnt > 1000)的判断。 : U$ N5 }' t! H# K2 u3 H" I
- voidCCT_calculate(void)
+ l4 ~( s5 |1 w) H/ K' _ - {& @5 G7 l4 v, N7 f) {
- if(Ts_cnt> 1000)
+ W" N" F& _; b" _- `% \4 B - {7 \6 J1 D" }2 S/ r
- Ts_cnt = 0;8 x) a8 B6 a2 f+ G, a( E
- T_ad = Get_ADCCH_Value(Ts_channel);
1 B: H3 e! X2 H" {6 x. F - T_degree = cal_temp(T_ad)-11;
+ f" t0 [, |2 K' j! H -
* k+ C- z6 [" g; B3 K) g - 。。。。。。【略】; r/ P/ E, Y* a" t- X, r
- }4 U* V. K9 i6 s1 Y; P% n3 Q( T D
- }
复制代码 3 g6 a( y- q# C4 U5 O Q0 K: R; L5 d
如果TIM4被屏蔽不工作,TS_CNT就不会得到累加而大于1000然后往下执行Get_ADCCH_Value();函数。该Get_ADCCH_Value();函数对ADC做初始化之后执行AD转换并获取相关AD值。
l, Z4 }9 [5 c3 R/ q) l, {
正是在ADC初始化代码里有对相关ADC通道对应脚的施密特触发器做了禁用配置。而且该ADC通道脚跟UART-RX脚又是复用的,麻烦就此产生了。 9 ~" v* P6 z6 p8 \6 R b# r
- s( H8 B1 Y2 n6 p
在STM8MCU的GPIO 的各IO模块里有个施密特触发器,通过寄存器ADC_TDR控制其开和关。默认情况下是打开的,IO脚的信号可以自由通过它进到输入寄存器或其它外设模块。
1 I/ v V& V6 G6 Z2 R( x7 D' v2 z
如果某管脚做AD模拟输入时,建议通过ADC_TDR将相应的施密特触发器关闭,目的是为了降低GPIO的功耗。如下图所示,当施密特触发器被关闭后,不管外部引脚电平如何变化,它的输出恒定为0。 R% r2 ^; }$ v
3 r$ k% }1 N. z结合到本案例中的问题,因为他在AD转换函数中初始化AD时关闭了该施密特触发器,该脚又复用为UART-RX,此时RX信号根本进不到UART接收模块中,不能产生UART接收中断也就自然而然了。 6 z3 |- n& v% V
后来当它打开施密特触发器后,URAT-RX接收也就正常了。 2 s/ Q0 D, F/ I9 o% J( @' a0 s
显然,客户最先认为的TIM4影响UART-RX是个错觉。因为它是每隔一定时间才去做AD转换,同时做些AD初始化配置。如果TIM4关闭了,相应的时间条件不成立也就不去做AD转换,也就不会禁用施密特触发器,进而就不会发生UART-RX失败的情况。
, w; M; g: t9 `. W
谈到这里,就此打住,目的想让大家通过类似案例分享而有所收获。
2 X$ F8 ]: P# r
文章出处: 茶话MCU 4 | \ ]( r: X/ z0 x3 }. b5 R
! k+ N! }% w# ]: W+ e8 u |