Собственно система работает под управлением FreeRTOS - это модифицированный вариант примера IwIp для Rowley только вместо стека lwIP я прикрутил uIP. В системе запущены три задачи: первая обслуживает сеть, вторая USB (виртуальный COM), третья мониторит и перерабатывает данные полученные через USB. Кроме этого в системе добавлено прерывание по таймеру (сравнение с регистром RC) по которому с заданым периодом из полученных даных фомируется короткая посылка, которая скармливается DMA для SPI. Приоритет этого прерывания я поставил наивысший (7), понизив приоритет таймера ОС до 6. Система работает, но (как я понимаю) в связи с наличием критических секций, период вывода в SPI плавает. Мне порекомендовали перейти на FIQ, что я и попробовал сделать. Но возникли проблемы - в таком варианте система отказывается работать. Если период вывода большой, то вывод на SPI работает, а всё остальное не откликается, если маленький, то даже вывод в SPI молчит. Стоит забрать опцию FastForcing, то-есть перевести тот-же код на IRQ - всё нормально работает. Само FIQ прерывание, похоже работает нормально, так-как если после инициализиции запустить систему по пустому циклу, то вывод на SPI работает:
Код
InitTimerInt();
portENABLE_INTERRUPTS();
for(;;)
{
}
/* Finally, start the scheduler.
vTaskStartScheduler();
portENABLE_INTERRUPTS();
for(;;)
{
}
/* Finally, start the scheduler.
vTaskStartScheduler();
А при старте в нормальном режиме с выполнением задач в какой-то момент система останавливается, что-ли.
Ума уже не приложу что ещё нужно сделать. Похоже на какой-то конфликт ядра FreeRTOS с FIQ, но где именно копать понять не могу. Часть кода относящаяся к этой проблеме ниже. По сравнению с вариантом реализованым на IRQ изменения следующие:
1. В flash_placement.xml изменил размер стека для FIQ с нуля до 0х190
2. В AT91SAM7_Startup.s изменил команду перехода на обработку прерывания FIQ
Код
ldr pc, [PC, #-0xF20] /* irq */
//ldr pc, [pc, #fiq_handler_address - . - 8] /* fiq */
ldr pc, [PC, #-0xF20] /* fiq */
//ldr pc, [pc, #fiq_handler_address - . - 8] /* fiq */
ldr pc, [PC, #-0xF20] /* fiq */
3. У функцию инициализации прерывания добавлено установку адреса обработчика для FIQ и включение FastForcing:
Код
void InitTimerInt(void)
{
// AT91F_AIC_ConfigureIt( AT91C_ID_TC0, DACsTIMER_PRIORITY, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)( void ) ) vTimer_ISR );
//Disable the interrupt on the interrupt controller
AT91C_BASE_AIC->AIC_IDCR=(0x1<<AT91C_ID_TC0);
AT91C_BASE_AIC->AIC_IDCR=(0x1<<AT91C_ID_FIQ); //Добавлено это
//Save the interrupt handler routine pointer and the interrupt priority
AT91C_BASE_AIC->AIC_SVR[AT91C_ID_FIQ] = (unsigned int) vTimer_ISR; //Добавлено это
AT91C_BASE_AIC->AIC_SVR[AT91C_ID_TC0] = (unsigned int) vTimer_ISR;
//Store the Source Mode Register AT91C_BASE_AIC->AIC_SMR[AT91C_ID_TC0]=AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL|DACsTIMER_PRIORITY;
//Clear the interrupt on the interrupt controller
AT91C_BASE_AIC->AIC_ICCR=(0x1<<AT91C_ID_TC0); //Добавлено это
AT91C_BASE_AIC->AIC_ICCR=(0x1<<AT91C_ID_FIQ); //Добавлено это
AT91C_BASE_AIC->AIC_FFER=(1<<AT91C_ID_TC0); //Добавлено это
AT91C_BASE_TC0->TC_CMR=AT91C_TC_CLKS_TIMER_DIV1_CLOCK|AT91C_TC_EEVT_XC0|AT91C_TC_WAVESEL_UP_
AUTO|AT91C_TC_WAVE|AT91C_TC_ACPA_SET|AT91C_TC_ACPC_CLEAR;
AT91C_BASE_TC0->TC_IER=AT91C_TC_CPCS;
AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_TC0;
AT91C_BASE_TC0->TC_CCR=AT91C_TC_CLKEN | AT91C_TC_SWTRG;
}
{
// AT91F_AIC_ConfigureIt( AT91C_ID_TC0, DACsTIMER_PRIORITY, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)( void ) ) vTimer_ISR );
//Disable the interrupt on the interrupt controller
AT91C_BASE_AIC->AIC_IDCR=(0x1<<AT91C_ID_TC0);
AT91C_BASE_AIC->AIC_IDCR=(0x1<<AT91C_ID_FIQ); //Добавлено это
//Save the interrupt handler routine pointer and the interrupt priority
AT91C_BASE_AIC->AIC_SVR[AT91C_ID_FIQ] = (unsigned int) vTimer_ISR; //Добавлено это
AT91C_BASE_AIC->AIC_SVR[AT91C_ID_TC0] = (unsigned int) vTimer_ISR;
//Store the Source Mode Register AT91C_BASE_AIC->AIC_SMR[AT91C_ID_TC0]=AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL|DACsTIMER_PRIORITY;
//Clear the interrupt on the interrupt controller
AT91C_BASE_AIC->AIC_ICCR=(0x1<<AT91C_ID_TC0); //Добавлено это
AT91C_BASE_AIC->AIC_ICCR=(0x1<<AT91C_ID_FIQ); //Добавлено это
AT91C_BASE_AIC->AIC_FFER=(1<<AT91C_ID_TC0); //Добавлено это
AT91C_BASE_TC0->TC_CMR=AT91C_TC_CLKS_TIMER_DIV1_CLOCK|AT91C_TC_EEVT_XC0|AT91C_TC_WAVESEL_UP_
AUTO|AT91C_TC_WAVE|AT91C_TC_ACPA_SET|AT91C_TC_ACPC_CLEAR;
AT91C_BASE_TC0->TC_IER=AT91C_TC_CPCS;
AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_TC0;
AT91C_BASE_TC0->TC_CCR=AT91C_TC_CLKEN | AT91C_TC_SWTRG;
}
4. Изменённый обработчик выглядит следующим образом:
Код
void vTimer_ISR( void ) __attribute__ ((section(".fast"))) __attribute__ ((naked));
void vTimer_ISR( void )
{
portENTER_SWITCHING_ISR();
AT91C_BASE_PIOB->PIO_OER = BIT27;
AT91C_BASE_PIOB->PIO_PER = BIT27;
AT91C_BASE_PIOB->PIO_CODR = BIT27;//Вывод на светодиод чтобы проверить что обработчик работает
AT91C_BASE_TC0->TC_SR;
AT91C_BASE_PIOA->PIO_CODR = BIT3;
AT91C_BASE_PIOA->PIO_SODR = BIT3;
if (OutFlags & NotFlag){
tempBuff[0]=AnPDCS3;
tempBuff[1]=XCS0;
tempBuff[2]=BnPDCS3;
tempBuff[3]=XCS0;
tempBuff[4]=CnPDCS3;
tempBuff[5]=YCS0;
tempBuff[6]=DnPDnCCS3;
Current++;
if (Current>=Numb)
{
Current=0;
}
} else {
tempBuff[0]=(A1[Current]<<4)|AnPDCS3;
tempBuff[1]=XCS0;
tempBuff[2]=(A2[Current]<<4)|BnPDCS3;
tempBuff[3]=A3[Current]|XCS0;
tempBuff[4]=(A4[Current]<<4)|CnPDCS3;
tempBuff[5]=A5[Current]|YCS0;
tempBuff[6]=(A6[Current]<<4)|DnPDnCCS3;
Current++;
if (Current>=Numb)
{
Current=0;
}
}
AT91C_BASE_SPI1->SPI_TPR=(unsigned int)tempBuff;
AT91C_BASE_SPI1->SPI_TCR=7;
//AT91C_BASE_AIC->AIC_EOICR = 0; \\Это включается для IRQ
AT91C_BASE_AIC->AIC_ICCR = (0x1 << AT91C_ID_TC0)|(0x1 << AT91C_ID_FIQ);
if (Current==300) {
AT91C_BASE_AIC->AIC_IDCR = 0x1 << AT91C_ID_TC0;
} //Вот этой конструкцией я проверял начинает ли работать FIQ. Если где-то после 300 (или меньше) прерываний их запретить, то дальше вся система работает нормально (конечно кроме вывода в SPI)
portEXIT_SWITCHING_ISR(pdFALSE);
}
void vTimer_ISR( void )
{
portENTER_SWITCHING_ISR();
AT91C_BASE_PIOB->PIO_OER = BIT27;
AT91C_BASE_PIOB->PIO_PER = BIT27;
AT91C_BASE_PIOB->PIO_CODR = BIT27;//Вывод на светодиод чтобы проверить что обработчик работает
AT91C_BASE_TC0->TC_SR;
AT91C_BASE_PIOA->PIO_CODR = BIT3;
AT91C_BASE_PIOA->PIO_SODR = BIT3;
if (OutFlags & NotFlag){
tempBuff[0]=AnPDCS3;
tempBuff[1]=XCS0;
tempBuff[2]=BnPDCS3;
tempBuff[3]=XCS0;
tempBuff[4]=CnPDCS3;
tempBuff[5]=YCS0;
tempBuff[6]=DnPDnCCS3;
Current++;
if (Current>=Numb)
{
Current=0;
}
} else {
tempBuff[0]=(A1[Current]<<4)|AnPDCS3;
tempBuff[1]=XCS0;
tempBuff[2]=(A2[Current]<<4)|BnPDCS3;
tempBuff[3]=A3[Current]|XCS0;
tempBuff[4]=(A4[Current]<<4)|CnPDCS3;
tempBuff[5]=A5[Current]|YCS0;
tempBuff[6]=(A6[Current]<<4)|DnPDnCCS3;
Current++;
if (Current>=Numb)
{
Current=0;
}
}
AT91C_BASE_SPI1->SPI_TPR=(unsigned int)tempBuff;
AT91C_BASE_SPI1->SPI_TCR=7;
//AT91C_BASE_AIC->AIC_EOICR = 0; \\Это включается для IRQ
AT91C_BASE_AIC->AIC_ICCR = (0x1 << AT91C_ID_TC0)|(0x1 << AT91C_ID_FIQ);
if (Current==300) {
AT91C_BASE_AIC->AIC_IDCR = 0x1 << AT91C_ID_TC0;
} //Вот этой конструкцией я проверял начинает ли работать FIQ. Если где-то после 300 (или меньше) прерываний их запретить, то дальше вся система работает нормально (конечно кроме вывода в SPI)
portEXIT_SWITCHING_ISR(pdFALSE);
}
На всякий случай ещё привожу расшифровку макросов обёртки прерывания, может там что нужно поменять:
Код
#define portRESTORE_CONTEXT() \
{ \
extern volatile void * volatile pxCurrentTCB; \
extern volatile unsigned portLONG ulCriticalNesting; \
/* Set the LR to the task stack. */ \
asm volatile ( \
"LDR R0, =pxCurrentTCB \n\t" \
"LDR R0, [R0] \n\t" \
"LDR LR, [R0] \n\t" \
/* The critical nesting depth is the first item on the stack. */ \
/* Load it into the ulCriticalNesting variable. */ \
"LDR R0, =ulCriticalNesting \n\t" \
"LDMFD LR!, {R1} \n\t" \
"STR R1, [R0] \n\t" \
/* Get the SPSR from the stack. */ \
"LDMFD LR!, {R0} \n\t" \
"MSR SPSR, R0 \n\t" \
/* Restore all system mode registers for the task. */ \
"LDMFD LR, {R0-R14}^ \n\t" \
"NOP \n\t" \
/* Restore the return address. */ \
"LDR LR, [LR, #+60] \n\t" \
/* And return - correcting the offset in the LR to obtain the */ \
/* correct address. */ \
"SUBS PC, LR, #4 \n\t" \
); \
( void ) ulCriticalNesting; \
( void ) pxCurrentTCB; \
}
/*-----------------------------------------------------------*/
#define portSAVE_CONTEXT() \
{ \
extern volatile void * volatile pxCurrentTCB; \
extern volatile unsigned portLONG ulCriticalNesting; \
/* Push R0 as we are going to use the register. */ \
asm volatile ( \
"STMDB SP!, {R0} \n\t" \
/* Set R0 to point to the task stack pointer. */ \
"STMDB SP,{SP}^ \n\t" \
"NOP \n\t" \
"SUB SP, SP, #4 \n\t" \
"LDMIA SP!,{R0} \n\t" \
/* Push the return address onto the stack. */ \
"STMDB R0!, {LR} \n\t" \
/* Now we have saved LR we can use it instead of R0. */ \
"MOV LR, R0 \n\t" \
/* Pop R0 so we can save it onto the system mode stack. */ \
"LDMIA SP!, {R0} \n\t" \
/* Push all the system mode registers onto the task stack. */ \
"STMDB LR,{R0-LR}^ \n\t" \
"NOP \n\t" \
"SUB LR, LR, #60 \n\t" \
/* Push the SPSR onto the task stack. */ \
"MRS R0, SPSR \n\t" \
"STMDB LR!, {R0} \n\t" \
"LDR R0, =ulCriticalNesting \n\t" \
"LDR R0, [R0] \n\t" \
"STMDB LR!, {R0} \n\t" \
/* Store the new top of stack for the task. */ \
"LDR R0, =pxCurrentTCB \n\t" \
"LDR R0, [R0] \n\t" \
"STR LR, [R0] \n\t" \
); \
( void ) ulCriticalNesting; \
( void ) pxCurrentTCB; \
}
/*-----------------------------------------------------------
* ISR entry and exit macros. These are only required if a task switch
* is required from the ISR.
*----------------------------------------------------------*/
#define portENTER_SWITCHING_ISR() \
/* Save the context of the interrupted task. */ \
portSAVE_CONTEXT(); \
/* We don't know the stack requirements for the ISR, so the frame */\
/* pointer will be set to the top of the task stack, and the stack*/\
/* pointer left where it is. The IRQ stack will get used for any */\
/* functions calls made by this ISR. */ \
asm volatile ( "SUB R11, LR, #4" ); \
{
#define portEXIT_SWITCHING_ISR( SwitchRequired ) \
/* If a switch is required then we just need to call */ \
/* vTaskSwitchContext() as the context has already been */ \
/* saved. */ \
if( SwitchRequired ) \
{ \
vTaskSwitchContext(); \
} \
} \
/* Restore the context of which ever task is now the highest */ \
/* priority that is ready to run. */ \
portRESTORE_CONTEXT();
{ \
extern volatile void * volatile pxCurrentTCB; \
extern volatile unsigned portLONG ulCriticalNesting; \
/* Set the LR to the task stack. */ \
asm volatile ( \
"LDR R0, =pxCurrentTCB \n\t" \
"LDR R0, [R0] \n\t" \
"LDR LR, [R0] \n\t" \
/* The critical nesting depth is the first item on the stack. */ \
/* Load it into the ulCriticalNesting variable. */ \
"LDR R0, =ulCriticalNesting \n\t" \
"LDMFD LR!, {R1} \n\t" \
"STR R1, [R0] \n\t" \
/* Get the SPSR from the stack. */ \
"LDMFD LR!, {R0} \n\t" \
"MSR SPSR, R0 \n\t" \
/* Restore all system mode registers for the task. */ \
"LDMFD LR, {R0-R14}^ \n\t" \
"NOP \n\t" \
/* Restore the return address. */ \
"LDR LR, [LR, #+60] \n\t" \
/* And return - correcting the offset in the LR to obtain the */ \
/* correct address. */ \
"SUBS PC, LR, #4 \n\t" \
); \
( void ) ulCriticalNesting; \
( void ) pxCurrentTCB; \
}
/*-----------------------------------------------------------*/
#define portSAVE_CONTEXT() \
{ \
extern volatile void * volatile pxCurrentTCB; \
extern volatile unsigned portLONG ulCriticalNesting; \
/* Push R0 as we are going to use the register. */ \
asm volatile ( \
"STMDB SP!, {R0} \n\t" \
/* Set R0 to point to the task stack pointer. */ \
"STMDB SP,{SP}^ \n\t" \
"NOP \n\t" \
"SUB SP, SP, #4 \n\t" \
"LDMIA SP!,{R0} \n\t" \
/* Push the return address onto the stack. */ \
"STMDB R0!, {LR} \n\t" \
/* Now we have saved LR we can use it instead of R0. */ \
"MOV LR, R0 \n\t" \
/* Pop R0 so we can save it onto the system mode stack. */ \
"LDMIA SP!, {R0} \n\t" \
/* Push all the system mode registers onto the task stack. */ \
"STMDB LR,{R0-LR}^ \n\t" \
"NOP \n\t" \
"SUB LR, LR, #60 \n\t" \
/* Push the SPSR onto the task stack. */ \
"MRS R0, SPSR \n\t" \
"STMDB LR!, {R0} \n\t" \
"LDR R0, =ulCriticalNesting \n\t" \
"LDR R0, [R0] \n\t" \
"STMDB LR!, {R0} \n\t" \
/* Store the new top of stack for the task. */ \
"LDR R0, =pxCurrentTCB \n\t" \
"LDR R0, [R0] \n\t" \
"STR LR, [R0] \n\t" \
); \
( void ) ulCriticalNesting; \
( void ) pxCurrentTCB; \
}
/*-----------------------------------------------------------
* ISR entry and exit macros. These are only required if a task switch
* is required from the ISR.
*----------------------------------------------------------*/
#define portENTER_SWITCHING_ISR() \
/* Save the context of the interrupted task. */ \
portSAVE_CONTEXT(); \
/* We don't know the stack requirements for the ISR, so the frame */\
/* pointer will be set to the top of the task stack, and the stack*/\
/* pointer left where it is. The IRQ stack will get used for any */\
/* functions calls made by this ISR. */ \
asm volatile ( "SUB R11, LR, #4" ); \
{
#define portEXIT_SWITCHING_ISR( SwitchRequired ) \
/* If a switch is required then we just need to call */ \
/* vTaskSwitchContext() as the context has already been */ \
/* saved. */ \
if( SwitchRequired ) \
{ \
vTaskSwitchContext(); \
} \
} \
/* Restore the context of which ever task is now the highest */ \
/* priority that is ready to run. */ \
portRESTORE_CONTEXT();