一個STM8S ADC腳與其它功能複用時的話題
大致內容就是某一GPIO口被複用為AD輸入腳做相關AD檢測。之後,把該腳AD功能禁用掉,再配置切換為帶下降沿觸發的外部中斷觸發腳,讓其作為晶片休眠喚醒腳。
奇怪的是,那樣設定後根本沒法喚醒。即使不做休眠,做好切換配置後,直接檢視該腳的IDR位的電平,不管外部輸入如何,發現對應IDR位始終提示為0.
後來找到原因是跟那個施密特觸發器的配置有關。可能有人覺得該問題是鑽牛角尖,其實,也不盡然。畢竟應用需求是五花八門的,遇到的問題往往也是五彩繽紛,問題不論大小折磨起人來也是不分男女老少的。
這裡再次分享個類似話題 ,希望能讓見到本文的人有所啟示。工程師反饋基本情況如下:
使用STM8S晶片開發。因為TIM1/2都用做PWM了,所以用TIM4來做基本定時。
TIM4正常中斷,UART1串列埠傳送正常,就是串列埠接收中斷進不去。
但只要把 TIM4_initialzation();遮蔽掉,串列埠馬上正常中斷接收,一旦開啟TIM4,串列埠就接收不了,其它功能都正常。
上面是該工程師對症狀的基本描述和初步判斷。【當然,除錯遇到麻煩時候的判斷難免有偏差,偏差大小因人因景不同,有時甚至完全誤判。】
下面是他的主迴圈程式碼【為了排版和閱讀,做了些刪減】。
int main( void )
{
CLK_DeInit(); //暫存器復位
CLK_HSICmd(ENABLE); //內部高速時鐘使能
CLK_HSIPrescalerConfig( ); //分頻
GPIO_initialzation();
uart_initialzation();
PWM1_initialzation();
PWM2_initialzation();
TIM4_initialzation(); //TIM4初始化
enableInterrupts();//* 開啟總中斷 */
Ts_cnt = 1000;
Ls_cnt = 500;
while(1)
{
PLED_flash(499); //LED
relay_control(); //繼電器控制
CCT_calculate();//獲取相關AD值
send_information();//輸出提示資訊
if(Flag_rec)
{
。。。。。。【略】
}
}
}
現在的情況是當註釋掉上面的 TIM4_initialzation();語句後,UART-RX接收中斷就正常。
TIM4只是做基本時鐘,不涉及外面其它硬體,最大可能是二者中斷優先順序有衝突導致UART-RX的正常接收。但當把UART-RX中斷優先順序調高於TIM4的更新中斷時問題並無好轉。
但事實又的確顯示出TIM4的中斷跟UART-RX接收有關係。
TIM4、UART1初始化程式碼只是些各種相關基本配置,不跟別的外設有關聯。不妨看看TIM4、UART1中斷服務程式裡能否找到些蛛絲馬跡。
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler,23)
{
TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
ms_cnt++;//LED FLASH
Ts_cnt++; //AD sample
Ls_cnt++; //relay control
uart_cnt++;//send information
PWM2_duty_setting(Ts_cnt);
pwm1_correct_cnt++;
if(pwm1_correct_cnt > 100)
{
pwm1_correct_cnt = 0;
if(pwm1_cnt > CCT_target)
pwm1_cnt--;
else
pwm1_cnt++;
PWM1_duty_setting(pwm1_cnt);
}
}
INTERRUPT_HANDLER(UART1_RX_IRQHandler,18)
{
static uint8_t index = 0;
UART1_ClearITPendingBit(UART1_IT_RXNE);
recived_data[index] = UART1_ReceiveData8(); //讀資料
if(recived_data[0] == 0x41)
{
index++;
if((index > 7)&&(recived_data[7] == 0x0d))
{
index = 0;
Flag_rec = 1;
}
}
else
{
index = 0;
recived_data[0] = 0;
}
}
從TIM4的中斷服務程式裡出現了好幾個全域性變數,看看這些全域性變數哪些函式會用到。因為TIM4的主要功能就是計數定時,下面幾個計時變數肯定是給別人用的。
ms_cnt++;//LED FLASH
Ts_cnt++; //AD sample
Ls_cnt++; //relay control
uart_cnt++;//send information
問題到這裡,繼續往下查就需要耐心了。客戶程式碼不復雜,用到的外設模組也不多,主迴圈裡也就下面幾個函式,一個個函式模組進行排查。
PLED_flash(499); //LED閃爍
relay_control(); //繼電器控制
CCT_calculate();//做AD轉換
send_information();//輸出提示資訊
後來發現TIM4保持工作的同時遮蔽CCT_calculate();,UART-RX能正常接收。看來TIM4並非是影響UART接收的元凶。不過CCT_calculate()的執行還是跟TIM4中斷有關,有個變數TS_CNT是在TIM4中斷裡進行累加的。
看看下面CCT_calculate()的程式碼,裡面有個條件判斷,即if(Ts_cnt > 1000)的判斷。
voidCCT_calculate(void)
{
if(Ts_cnt> 1000)
{
Ts_cnt = 0;
T_ad = Get_ADCCH_Value(Ts_channel);
T_degree = cal_temp(T_ad)-11;
。。。。。。【略】
}
}
如果TIM4被遮蔽不工作,TS_CNT就不會得到累加而大於1000然後往下執行Get_ADCCH_Value();函式。該Get_ADCCH_Value();函式對ADC做初始化之後執行AD轉換並獲取相關AD值。
正是在ADC初始化程式碼裡有對相關ADC通道對應腳的施密特觸發器做了禁用配置。而且該ADC通道腳跟UART-RX腳又是複用的,麻煩就此產生了。
在STM8MCU的GPIO 的各IO模組裡有個施密特觸發器,通過暫存器ADC_TDR控制其開和關。預設情況下是開啟的,IO腳的訊號可以自由通過它進到輸入暫存器或其它外設模組。
如果某管腳做AD模擬輸入時,建議通過ADC_TDR將相應的施密特觸發器關閉,目的是為了降低GPIO的功耗。如下圖所示,當施密特觸發器被關閉後,不管外部引腳電平如何變化,它的輸出恆定為0。
結合到本案例中的問題,因為他在AD轉換函式中初始化AD時關閉了該施密特觸發器,該腳又複用為UART-RX,此時RX訊號根本進不到UART接收模組中,不能產生UART接收中斷也就自然而然了。
後來當它開啟施密特觸發器後,URAT-RX接收也就正常了。
顯然,客戶最先認為的TIM4影響UART-RX是個錯覺。因為它是每隔一定時間才去做AD轉換,同時做些AD初始化配置。如果TIM4關閉了,相應的時間條件不成立也就不去做AD轉換,也就不會禁用施密特觸發器,進而就不會發生UART-RX失敗的情況。
談到這裡,就此打住,目的想讓大家通過類似案例分享而有所收穫。
相關話題連結:【點選閱讀】
轉自:http://www.51hei.com/bbs/dpj-51301-1.html