Полная версия этой страницы:
Прерывания Cortex-M3
Пробую прерывания в EFM32TG210. Пакет IAR 6.3 Код настройки:
//_______*** RTC ***_______
/* Enable RTC clock */
CMU->LFACLKEN0 |= CMU_LFACLKEN0_RTC;
RTC->IEN |= RTC_IEN_COMP0;
RTC->CTRL = RTC_CTRL_COMP0TOP | RTC_CTRL_DEBUGRUN;
RTC->COMP0 = 0x10000;
RTC->IFC = RTC_IFC_COMP0;
NVIC_EnableIRQ(RTC_IRQn);
RTC->CTRL |= RTC_CTRL_EN;
Хандлер:
void RTC_IRQHandler(void);
{
int current_time, next_time;
RTC->IFC = RTC_IFC_COMP0;
RTC->COMP0 = 0x10000;
GPIO->P[1].DOUT = (( 1<< 13) & (~(GPIO->P[1].DOUT)));
}
В дебугере вхожу в хандлер по шагам F11, процесс "зависает" - вернее начинает бежать не по шагам, как будто F5 нажал, когда останавливаешь, то в дизассемблере всякие BusFault_Handler, DebugMon_Handler, HardFault_Handler и т.д. Я прерывания не так настраиваю?
Mareng
Mar 15 2012, 07:28
Цитата(rat @ Mar 15 2012, 14:12)

void RTC_IRQHandler(void);
Наверное проблема в ;
Улетает прерывание в HardFault_Handler.
Цитата(Mareng @ Mar 15 2012, 14:28)

Наверное проблема в ;
Улетает прерывание в HardFault_Handler.
При попытке убрать ; появляется Error[Pe065]: expected a ";"
Mareng
Mar 15 2012, 07:46
; для начала убрать надо
Цитата(Mareng @ Mar 15 2012, 14:46)

; для начала убрать надо
При попытке убрать ; появляется Error[Pe065]: expected a ";"
Цитата(rat @ Mar 15 2012, 14:49)

При попытке убрать ; появляется Error[Pe065]: expected a ";"
Поправил. Ошибка ; исчезла, но попрежнему HardFault_Handler при попытке войти в прерывание
Сергей Борщ
Mar 15 2012, 07:56
QUOTE (rat @ Mar 15 2012, 09:49)

При попытке убрать ; появляется Error[Pe065]: expected a ";"
Значит ошибка где-то чуть раньше, вероятно в конце предыдущей функции забыли "}". Ибо с ";" это не функция обрабочика прерывания, а объявление функции и не связанный с ним кусок кода.
Mareng
Mar 15 2012, 08:03
Цитата(rat @ Mar 15 2012, 14:54)

Поправил. Ошибка ; исчезла, но попрежнему HardFault_Handler при попытке войти в прерывание
Теперь осталось выяснить адрес какой функции записан в векторе обработчика RTC. Обычно это в startup*.s написано
Цитата(Сергей Борщ @ Mar 15 2012, 14:56)

Значит ошибка где-то чуть раньше, вероятно в конце предыдущей функции забыли "}". Ибо с ";" это не функция обрабочика прерывания, а объявление функции и не связанный с ним кусок кода.
Это да, поправил. В дизассемблере ошибка выглядит так: сразу после выполнения RTC->CTRL |= RTC_CTRL_EN; следует POP R4, PC вот после этого шага и происходит трабл, загорается "красная рука" (как при F5) и при остановке перечисляется куча фаультных хандлеров.
Цитата(Mareng @ Mar 15 2012, 15:03)

Теперь осталось выяснить адрес какой функции записан в векторе обработчика RTC. Обычно это в startup*.s написано
В startup_EFM32tg.s написано:
PUBWEAK RTC_IRQHandler
SECTION .text:CODE:REORDER(1)
RTC_IRQHandler
B RTC_IRQHandler
Mareng
Mar 15 2012, 08:22
Можно посмотреть в пдф-е на проц точный адрес вектора RTC, посмотреть реальный адрес функции-обработчика, а потом убедиться(просмотром памяти), что в адресе вектора записан адрес именно этой функции. Тогда уже верняк.
Сергей Борщ
Mar 15 2012, 08:39
QUOTE (rat @ Mar 15 2012, 10:16)

сразу после выполнения RTC->CTRL |= RTC_CTRL_EN; следует POP R4, PC
Это возврат из функции. Куда именно оно должно вернуться? Стек настроен? Что лежит в первых восьми байтах стека перед выполнением этой команды? Там правильный адрес возврата, его никто не затирает? Или это у вас конец main(), в котором вы забыли сделать бесконечный цикл?
Цитата(Сергей Борщ @ Mar 15 2012, 15:39)

Это возврат из функции. Куда именно оно должно вернуться? Стек настроен? Что лежит в первых восьми байтах стека перед выполнением этой команды? Там правильный адрес возврата, его никто не затирает? Или это у вас конец main(), в котором вы забыли сделать бесконечный цикл?
Возврат из функции POP R4, PC при этом в R4 лежит 37E, т.е. должен произойти возврат на адрес 0х37E? Должно вернуться в следующую функцию, но она начинается по адресу 0х384. А по адресам 0х344-0х380 лежит DataTable. В этом дело?
Вопрос разрешился. Не был подключен файл startup_efm32tg.c Спасибо большое за помощь комраду Борщу )
batisto4ka
Mar 19 2012, 08:00
Помогите разобраться с приоритетами прерываний. У меня реализован модбас чере зперрывания от юсарт и считывание внешнего ацп через exti(сигнал готовности данных). Но когда на входе ацп 0, то модбас работает прекрасно. а когда напряжение на вхоже повышается то модбас отвечает через раз.
CODE
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART,USART_IT_ERR,ENABLE);
}
NVIC_SetPriority(USART2_IRQn,0); //!!
USART_ITConfig(USARTx,USART_IT_TC,ENABLE);
USART_ITConfig(USARTx,USART_IT_RXNE,ENABLE);
/*===============================================================================
===============================*/
void ADCRDYInterruptConfig (void)
{
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Line = EXTI_Line1|EXTI_Line13;
EXTI_Init(&EXTI_InitStructure);
}
/*===============================================================================
===============================*/
void ADCNVIC_Conf (void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*===============================================================================
===============================*/
void ADC2NVIC_Conf (void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
и еще, возможно ли такое, что когда выполняется бесконечный цикл while, прерывания не срабатывают? потому что запись команд и чтение АЦп сделано через spi и очень часто когда виснет программа я ее останавливаю и останавливается на строчке while (!(SPIx->SR & SPI_SR_RXNE));
Код
uint8_t WriteSPI(SPI_TypeDef * SPIx,uint8_t val)
{
SPIx->DR = val;
while (!(SPIx->SR & SPI_SR_RXNE));//while(!(SPI1->SR & 0x01));
return SPIx->DR;
}
batisto4ka
Mar 19 2012, 10:36
Явно что-то с вайлом. Первыйй АЦП общается с контроллером через аппартаный SPI и виснет на while (!(SPIx->SR & SPI_SR_RXNE)), а второй АЦП через программно реализованный SPI там в релизации есть задержки и виснет на них на ожидании переполнения от таймера while(!TIMx->SR&TIM_FLAG_Update) {}. АЦП AD7734, камень stm32f103. Кто что скажет/посоветует?
Если хардварный SPI виснет на ожидании SPI_SR_RXNE, то либо SPI неправильно настроен, и надо в первую очередь проверить осциллографом наличие на контактах CLK и chip enable, либо всё же SPI работает, но где-то в системе работает обработчик прерывания по SPI, который сбрасывает событие, и основная программа его поэтому не видит (очень маловероятно).
По поводу ожидания таймера - таймер как-нибудь проверяли, что он запущен и работает? Проблема возникает ТОЛЬКО при работе с АЦП?
batisto4ka
Mar 19 2012, 13:42
Да, только при работе с АЦП. Еще есть один вопрос, если делаю опрос АЦП по прерыванию от EXTI (сигнал готовности данных АЦП), как бы так можно организовать и аппаратный и программный SPI, чтобы не было while, ведь АЦП может вдруг и не ответить. И если АЦП опрашивается по прерываю, а запись данных в АЦП по SPI содержит ожидание ответа от SPI в вайле, то прерывание зависнет.
У меня запись по аппартному SPI осуществляется в фоне.
Конечно, организовать можно по-разному, и смешать вместе работу как по прерываниям, так и ручную проверку в коде. Только почему бы всё не сделать единообразно? А чтобы зависаний не было, вместо while(...) ставить поллинг-проверку if(...) flag=1; и потом смотреть if(flag).
Однако, не понимаю, почему должен зависать код обработки прерывания по EXTI? как он связан с кодом работы по SPI? код в студию!
batisto4ka
Mar 20 2012, 08:40
CODE
void EXTI1_IRQHandler(void)
{
static uint8_t ADC1_Channel=1;
static unsigned char Command=ReadChannelData_0;
static unsigned char Channel;
static uint32_t temp=0;
static uint32_t temp1=0;
static uint8_t lenght=0;
static uint32_t mul=0;
static float k=0;
static float b=0;
static uint8_t ADCBuf_ind;
static uint8_t HR_ind;
static uint8_t adcMode;
if (EXTI->PR&(1<<1)) // Прерывание от EXTI1
{
EXTI_ClearITPendingBit(EXTI_Line1);
k=(float)ReadFromFlash(HR_ADDRESS+F_K_ADC*2)/100000;
b=(float)ReadFromFlash(HR_ADDRESS+F_B_ADC*2)/100000;
if (IfValid(ADC_MODE_REG,ReadFromFlash(HR_ADDRESS+F_ADC_MODE_REG*2)))
adcMode=ReadFromFlash(HR_ADDRESS+F_ADC_MODE_REG*2);
else adcMode=0x40;
if ((adcMode==0x20)||(adcMode==0x40))
{
if ((adcMode>>1)&0x01)
{
lenght=3;
mul=0x10000;
}
else
{
lenght=2;
mul=0x100;
}
switch (ADC1_Channel)
{
case 1: {
Channel=ReadChannelData_0;
Command=WriteToModeChannel_2;
HR_ind=ADC_CODE_1;
ADCBuf_ind=0;
ADC1_Channel++;
} break;
case 2: {
Channel=ReadChannelData_1;
Command=WriteToModeChannel_3;
HR_ind=ADC_CODE_2;
ADCBuf_ind=1;
ADC1_Channel++;
} break;
case 3: {
Channel=ReadChannelData_2;
Command=WriteToModeChannel_4;
HR_ind=ADC_CODE_3;
ADCBuf_ind=2;
ADC1_Channel++;
} break;
case 4: {
Channel=ReadChannelData_3;
Command=WriteToModeChannel_1;
HR_ind=ADC_CODE_4;
ADCBuf_ind=3;
ADC1_Channel=1;
} break;
}
temp=WriteToADC(SPI_1,Channel,8,SPI1);
while (lenght)
{
temp1=WriteToADC(SPI_1,0x00,8,SPI1);
temp=temp+temp1*mul;
mul=mul/0x100;
lenght--;
}
HoldingRegister[HR_ind]=temp; //код ацп
ADCBuffer[ADCBuf_ind]=(float)temp*k+b;
temp=WriteToADC(SPI_1,Command,8,SPI1);
temp=WriteToADC(SPI_1,adcMode,8,SPI1); //16bit resolution
}
}
}
uint8_t WriteToADC(uint8_t type,uint8_t firstParam, uint8_t secondParam, SPI_TypeDef * thirdParam)
{
uint8_t temp=0;
if (type==SPI_1)
temp=WriteSPI(thirdParam,firstParam);
else
temp=WriteSoftSPI(firstParam,secondParam);
return temp;
}
uint8_t WriteSPI(SPI_TypeDef * SPIx,uint8_t val)
{
SPIx->DR = val;
while (!(SPIx->SR & SPI_SR_RXNE));//while(!(SPI1->SR & 0x01));
return SPIx->DR;
}
uint8_t WriteSoftSPI(uint8_t val, uint8_t data_lenght)
{
uint8_t c;
for(c = 0; c < data_lenght; c++)
{
// ADC_Delay(ADC_TIM);
CLR_SCK_ADC1();
if(val & 0x80)
SET_SDI_ADC1();
else
CLR_SDI_ADC1();
// ADC_Delay(ADC_TIM);
SET_SCK_ADC1();
// ADC_Delay(ADC_TIM);
val <<= 1;
if(SDO_ADC1())
val |= 0x01;
}
return val;
}
/*===============================================================================
=====*/
void ADC_Delay (TIM_TypeDef* TIMx)
{
TIMx->CR1|=0x01;
while(!TIMx->SR&TIM_FLAG_Update) {}
TIMx->CR1&=~0x01;
}
Вот и получается, что первый АЦП опрашивается через аппаратный эспиай МК и подвисает на while while (!(SPIx->SR & SPI_SR_RXNE)), а второй АЦП опрашивается программно реализованным эспиай и подвисает на функции задержки на while(!TIMx->SR&TIM_FLAG_Update) {}.
Дак судя по коду вообще получается, что у вас периферия не работает. Посмотрите в
соседней ветке как народ настраивает периферию.
batisto4ka
Mar 20 2012, 11:30
ну это не весь код естесственно, это сами обработчики и функции работы с АЦП
я это понимаю. но из кода функций WriteSPI и ADC_Delay и с ваших слов, что они зависают, выходит, что периферийные блоки не включены, что-то такое.
batisto4ka
Mar 20 2012, 13:08
они не зависают,а подвисают..если сделать функцию опроса АЦП не по гвотовности данных по внешнему прерыванию, а просто функция в фоне которая опрашивает АЦП, товсе работает, а если сделать через прерывания - то модбас отвечает через раз, и бывает такое, что опрос АЦП подвисает, потмоу что нет ответа от АЦП, по SPI не считываются данные и соотвественно след канала АЦП не опрашивается и виснет программа, либо (в случае программной реализации АЦп) подвисает таймер ..или бывает такое, что АЦП может не ответить, как тогда в таком случае можно организовать опрос через SPI, чтоб если вдруг АЦП не ответит, не висло прерывание или фон?
Здравствуйте. Есть такой код:
CODE
#define STM32F10X_MD_VL
#include "stm32f10x.h"
uint8_t change = 0;
void __EXTI0_IRQHandler (void) {
if (change == 0) {
change = 1;
GPIOC->BSRR = GPIO_BSRR_BS8 | GPIO_BSRR_BR9;
} else {
change = 0;
GPIOC->BSRR = GPIO_BSRR_BR8 | GPIO_BSRR_BS9;
};
EXTI->PR = EXTI_PR_PR0;
};
static __INLINE void system_init (void) {
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)) {};
RCC->CFGR |= RCC_CFGR_PLLMULL_0 | RCC_CFGR_PLLSRC;
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) {};
RCC->CFGR |= RCC_CFGR_SW_1;
while (!(RCC->CFGR & RCC_CFGR_SWS_PLL)) {};
RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN |
RCC_APB2ENR_IOPDEN | RCC_APB2ENR_AFIOEN);
};
int __main (void) {
system_init ();
GPIOC->CRH &= ~GPIO_CRH_CNF8 & ~GPIO_CRH_CNF9 & ~GPIO_CRH_MODE8 & ~GPIO_CRH_MODE9;
GPIOC->CRH |= GPIO_CRH_MODE8_0 | GPIO_CRH_MODE8_1 | GPIO_CRH_MODE9_0 | GPIO_CRH_MODE9_1;
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0_PA;
EXTI->FTSR |= EXTI_RTSR_TR0;
EXTI->RTSR |= EXTI_RTSR_TR0;
EXTI->IMR |= EXTI_IMR_MR0;
NVIC_SetPriority (EXTI0_IRQn, 15);
NVIC_EnableIRQ (EXTI0_IRQn);
__enable_irq ();
GPIOC->BSRR = GPIO_BSRR_BR8 | GPIO_BSRR_BS9;
while (1) {
};
};
При этом содержимое *.s файла такое:
CODE
sram_base EQU 0x20000000
sram_size EQU 0x2000
stack_top EQU (sram_base + sram_size)
;------------------------------------------------
AREA reset, DATA, READONLY
DCD stack_top
DCD RESET_Handler
DCD 0;NMI_Handler ; NMI Handler
DCD 0;HardFault_Handler ; Hard Fault Handler
DCD 0;MemManage_Handler ; MPU Fault Handler
DCD 0;BusFault_Handler ; Bus Fault Handler
DCD 0;UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0;SVC_Handler ; SVCall Handler
DCD 0;DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD 0;PendSV_Handler ; PendSV Handler
DCD 0;SysTick_Handler ; SysTick Handler
; External Interrupts
DCD 0;WWDG_IRQHandler ; Window Watchdog
DCD 0;PVD_IRQHandler ; PVD through EXTI Line detect
DCD 0;TAMPER_IRQHandler ; Tamper
DCD 0;RTC_IRQHandler ; RTC
DCD 0;FLASH_IRQHandler ; Flash
DCD 0;RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD 0;EXTI1_IRQHandler ; EXTI Line 1
DCD 0;EXTI2_IRQHandler ; EXTI Line 2
DCD 0;EXTI3_IRQHandler ; EXTI Line 3
DCD 0;EXTI4_IRQHandler ; EXTI Line 4
DCD 0;DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD 0;DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD 0;DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD 0;DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD 0;DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD 0;DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD 0;DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD 0;ADC1_IRQHandler ; ADC1
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0;EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD 0;TIM1_BRK_TIM15_IRQHandler ; TIM1 Break and TIM15
DCD 0;TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16
DCD 0;TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger and Commutation and TIM17
DCD 0;TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD 0;TIM2_IRQHandler ; TIM2
DCD 0;TIM3_IRQHandler ; TIM3
DCD 0 ; Reserved
DCD 0;I2C1_EV_IRQHandler ; I2C1 Event
DCD 0;I2C1_ER_IRQHandler ; I2C1 Error
DCD 0;0 ; Reserved
DCD 0 ; Reserved
DCD 0;SPI1_IRQHandler ; SPI1
DCD 0 ; Reserved
DCD 0;USART1_IRQHandler ; USART1
DCD 0;USART2_IRQHandler ; USART2
DCD 0 ; Reserved
DCD 0;EXTI15_10_IRQHandler ; EXTI Line 15..10
DCD 0;RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line
DCD 0;CEC_IRQHandler ; HDMI-CEC
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0;TIM6_DAC_IRQHandler ; TIM6 and DAC underrun
DCD 0;TIM7_IRQHandler ; TIM7
;------------------------------------------------
AREA main_code, CODE, READONLY
RESET_Handler PROC
ENTRY
IMPORT __main
LDR R0, =__main
BX R0
ENDP
EXTI0_IRQHandler\
PROC
IMPORT __EXTI0_IRQHandler
ENDP
;------------------------------------------------
AREA stack, DATA, READWRITE
SPACE 0x1000
;------------------------------------------------
END
Прогоняю его в отладчике, при нажатии на кнопку устанавливается бит PR0 регистра EXTI_PR, но перехода к обработчику прерывания не происходит, программа так и крутится в бесконечном цикле. Никак не могу понять, что я делаю не так?
Решил проблему, мой косяк был.
Надо было делать не так:
Код
EXTI0_IRQHandler\
PROC
IMPORT __EXTI0_IRQHandler
ENDP
, а так:
Код
EXTI0_IRQHandler\
PROC
EXPORT EXTI0_IRQHandler [WEAK]
ENDP
Причем как я понял, в отладчике кейла перехода к обработчику не происходит в любом случае, то ли так задумано, то ли я чего не так делаю. А вот в железе изначально происходило зависание при возникновении прерывания, т.е. мк переходил по неизвестно какому адресу. Теперь с этим все ок, правда возникла другая трабла, но это в отдельной теме.
p.s. - про "кодбокс" не знал, спасибо, буду использовать.
lead_seller
Apr 3 2012, 17:44
to batisto4ka:
1. По зависанию SPI в цикле ожидания передачи: если Вы кладете данные в аппаратный SPI и не дожидаясь окончания передачи остановите ядро (по точке останова, или просто кнопкой отладчика) то передача по SPI накроется медным тазом, и флаг окончания передачи никогда не поднимется, сам с таким сталкивался. Так что возможно Ваша проблема связана не с зависанием SPI или АЦП.
2. На мой взгляд, писать такой огромный кусок кода в прерывание, а тем более вызывать из него кучу ветвящихся процедур не очень красиво. Вполне возможно ваши проблемы возникают тогда, когда обработчик прерывания EXTI ушел в какую-нибудь функцию, а в это время возникает прерывание от USART. Я бы сделал немного по другому, в основном цикле я бы реализовал конечный автомат обработки MODBUS и опроса АЦП, а в прерываниях оставил бы лишь генерацию входов для этого автомата.
3. Если уж хотите обезопасить себя от зависания в циклах, вводите счетчики итераций - хорошая практика, к примеру:
uint8_t counter = 20;
while ((/*Ваше условие*/) && (counter--) )
{
/*Ваши действия*/
}
if (!counter) {/*Обработка ошибки*/};
Таким образом из любого, потенциально опасного цикла Ваша программа выйдет либо по нормальному условию, либо через четко заданное количество итераций, а следующий за циклом if обработает аварийную ситуацию.
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.