Hi everybody
I use SAM7x512 and RL-ARM OS. here, OS has 7 tasks and one of them is for CAN receiver. OS_TICK is 1000 and OS_STKSIZE is 1024. and 500000 is my CAN baudrate(my board is just receiver and transmitter is a KVASER PCI CAN Interface).
speed of data transmission is my problem.
when for example transmitter side of CAN bus send a packet per 10ms or more there is know any problem but when I try to increase the speed after some packet (about 200 messages or more) I will get OS_ERR_FIFO_OVF in os_error function. but when I disable the CAN task all other task are working as well.
note: When the system is getting crash and doesn't do any tasks but it's not in os_error function in debug mode with ulink2 sometimes task of CAN in "RTX task and system" -> "stack load" shows "under flow" and after that it calls os_error (in usual states it's about 3% of stack usage)
I tried such works:
1. increase OS_STKSIZE 2. increase OS_FIFOSZ 3. increase CAN_No_ReceiveObjects 4. chaning the OS_TICK
but nothing happened
Does anyone know what I should do?
Hi there is nobody to help me!!! such a good forum!!!
I found somethings else but the problem still exist.
in my project I used Ethernet TCP too.
sometimes before the system get crashed, OS is alive but doesn't do any tasks and when I paused ulink2 to debug, I found it the program involved between CAN IRQ and Ethernet IRQ service routine and never get out of there until WDT restart the system.
I thought I have to use nesting interrupt method. but I don't know how because I found two method to cop such a thing
1. using the method which is mentioned here: http://www.keil.com/support/docs/2910.htm (IENABLE and IDESABLE)
2. using the method which is mentioned in RL-ARM_gs.pdf something like this (page 30):
void Task3 (void) { while (1) { os_evt_wait_or (0x0001, 0xffff); // Wait until ISR triggers an event … // Handle the interrupt } // Loop and go back to sleep }
void IRQ_Handler (void) __irq { isr_evt_set (0x0001, tsk3); // Signal Task 3 with an event EXTINT = 0x00000002; // Clear the peripheral interrupt flag VICVectAddr = 0x00000000; // Signal end of interrupt to the VIC }
I've used both but the problem is still exist.
please help me
thanks
* Try increasing the priority of the CAN task. Too many signals are piling up without being handled which causes the failure. * Try to limit the time the scheduler is disabled. * Don't use nested interrupt. Unlikely to help and introduced a risk of stack overflow. * Try to speed up general interrupt handling.
Is your CAN interrupt "efficient"? Are you storing messages in a buffer, to later be handled by a task or are you blocking the interrupt system while handling the message?
Are you sure there is no interrupt left handing leading to this problem...? Are you acknowledging all interrupts under all circumstances?
Hi I said, in method which is mentioned is rl-arm_gs.pdf from Keil we have such a thing to pass a signal to a task to handle the interrupt and give acknowledgment to that interrupt as soon as posible because the thask is running to process new arrival data.
I did it but I couldn't get any data through CAN network even 1 message. (I don't know how much interrupt we have to complete one message? is it more than one?)
about the acknowledgment of other intrrupts I'm not that much sure. I've build up my project by examples in keil software, (keil\arm\board\atmel\sam7x-ek) and I didn't implement any interrupt except CAN, Ethernet and OS timer which are included in keil software(and I know they have acknowledgment).
thank you
good luck
It's nice with interrupt handlers that sets a signal. But the interrupt handler have to clean out whatever information that caused the interrupt before it returns (received CAN frames, or overrruns or other errors)
So the ISR should pick up received CAN data and store in a queue. Then acknowledge the interrupt and exit. The thread that gets signalled from the ISR will then be able to pick up received CAN frames from that queue and process them.
Hi I've used exactly CAN_SAM7X.c which is in keil examples here is the CAN IRQ ISR:
static void CAN_IRQHandler (void) __irq { U32 ch; CAN_msg *ptrmsg; struct _AT91S_CAN_MB *MailBox; U32 i, int_status, int_ch, int_mask; int_mask = CAN_PTR->CAN_IMR; CAN_PTR->CAN_IDR = 0xFF; /* Disable CAN interrupts */ int_status = CAN_PTR->CAN_SR & int_mask; int_ch = int_status & tx_mailboxes; if (int_ch) { /* Transmission interrupt occured */ tx_mailboxes &= ~int_ch; /* Tx on channel is inactive */ int_mask &= ~int_ch; /* Clear interrupt mask for Tx */ i = 1; for (ch = 1; ch < MAX_OBJ+1; ch++) { /* Find mailbox that caused Tx int */ if (int_ch & i) break; i <<= 1; } MailBox = table_mailbox_addr[ch-1]; /* Mailbox address */ MailBox->CAN_MB_MMR=AT91C_CAN_MOT_DIS;/* Disable mailbox until next write*/ /* If there is a message in the mailbox ready for send, read the message from the mailbox and send it */ if (isr_mbx_receive (MBX_tx_ctrl[CTRL0], (void **)&ptrmsg) != OS_R_OK) { CAN_hw_wr (CTRL, ptrmsg); _free_box(CAN_mpool, ptrmsg); } else { isr_sem_send(wr_sem[CTRL0]); /* Return a token back to semaphore*/ } } int_ch = int_status & rx_mailboxes; if (int_ch) { /* Reception interrupt occured */ i = 1; for (ch = 1; ch < MAX_OBJ+1; ch++) { /* Read all pending received msgs */ if (int_ch & i) { MailBox = table_mailbox_addr[ch-1]; /* Mailbox address */ /* If the mailbox isn't full read the message from the hardware and send it to the message queue */ if (os_mbx_check (MBX_rx_ctrl[CTRL0]) > 0) { ptrmsg = _alloc_box (CAN_mpool); if (ptrmsg) CAN_hw_rd (CTRL, ch, ptrmsg); /* Read received msg */ MailBox->CAN_MB_MCR = AT91C_CAN_MTCR; /* release mailbox */ if (ptrmsg) isr_mbx_send (MBX_rx_ctrl[CTRL0], ptrmsg); } } i <<= 1; } } CAN_PTR->CAN_IER = int_mask; /* Enable CAN interrupt*/ AT91C_BASE_AIC->AIC_ICCR = CAN_INT_CLR; /* Clear interrupt flag*/ AT91C_BASE_AIC->AIC_EOICR = AT91C_BASE_AIC->AIC_EOICR; /* End of interrupt*/ }
then to process the data in separated task I made it in this way:
U32 ch; CAN_msg *ptrmsg; struct _AT91S_CAN_MB *MailBox; U32 i, int_status, int_ch, int_mask; void CAN_process(void) { while (1) { os_evt_wait_or (0x0001, 0xffff); // Wait until ISR triggers an event if (int_ch) { /* Transmission interrupt occured */ tx_mailboxes &= ~int_ch; /* Tx on channel is inactive */ int_mask &= ~int_ch; /* Clear interrupt mask for Tx */ i = 1; for (ch = 1; ch < MAX_OBJ+1; ch++) { /* Find mailbox that caused Tx int */ if (int_ch & i) break; i <<= 1; } MailBox = table_mailbox_addr[ch-1]; /* Mailbox address */ MailBox->CAN_MB_MMR=AT91C_CAN_MOT_DIS;/* Disable mailbox until next write*/ /* If there is a message in the mailbox ready for send, read the message from the mailbox and send it */ if (isr_mbx_receive (MBX_tx_ctrl[CTRL0], (void **)&ptrmsg) != OS_R_OK) { CAN_hw_wr (CTRL, ptrmsg); _free_box(CAN_mpool, ptrmsg); } else { isr_sem_send(wr_sem[CTRL0]); /* Return a token back to semaphore*/ } } int_ch = int_status & rx_mailboxes; if (int_ch) { /* Reception interrupt occured */ i = 1; for (ch = 1; ch < MAX_OBJ+1; ch++) { /* Read all pending received msgs */ if (int_ch & i) { MailBox = table_mailbox_addr[ch-1]; /* Mailbox address */ /* If the mailbox isn't full read the message from the hardware and send it to the message queue */ if (os_mbx_check (MBX_rx_ctrl[CTRL0]) > 0) { ptrmsg = _alloc_box (CAN_mpool); if (ptrmsg) CAN_hw_rd (CTRL, ch, ptrmsg); /* Read received msg */ MailBox->CAN_MB_MCR = AT91C_CAN_MTCR; /* release mailbox */ if (ptrmsg) isr_mbx_send (MBX_rx_ctrl[CTRL0], ptrmsg); } } i <<= 1; } } } } static void CAN_IRQHandler (void) __irq { int_mask = CAN_PTR->CAN_IMR; CAN_PTR->CAN_IDR = 0xFF; /* Disable CAN interrupts */ int_status = CAN_PTR->CAN_SR & int_mask; int_ch = int_status & tx_mailboxes; isr_evt_set (0x0001, CAN_process_TID); CAN_PTR->CAN_IER = int_mask; /* Enable CAN interrupt*/ AT91C_BASE_AIC->AIC_ICCR = CAN_INT_CLR; /* Clear interrupt flag*/ AT91C_BASE_AIC->AIC_EOICR = AT91C_BASE_AIC->AIC_EOICR; /* End of interrupt*/ }
maybe I should do something else in IRQ ...
and the connection of CAN, here is just for receiving data and I don't have send routine.
note: I tried to fully disable CAN peripheral in Ethernet IRQ. note: when I increased the fifo size of OS, about 1 to 3 minutes I can receive data (1 message per 1ms) and after that program will call os_error with error code=2 (fifo ovf) and sometimes the program will call PAbt_Handler or hardly ever DAbt_Handler. (these two handlers maybe due to fifo ovf but they occur rarely)
another question: when the transmitter side before lunching my device send messages, program will crash on os_sem_init (wr_sem[ctrl0], 1); in CAN_init function. how can I prevent it?
I have question a lot, excuse me
thanks again for your responses
Hi about that part of my code in previous post (with isr_evt_set) when I was debugging I found that the program never pass "os_evt_wait_or (0x0001, 0xffff)" (the program has called isr_evt_set but nothing happened after that)
what's wrong with it?
Hi
after a long while I could found what the problem was.
* Try to speed up general interrupt handling.
as you can see in my sample code (2 messages before) the developer of that code has used mailbox to send a new packet to can_receive fuction and can_receive function use mailbox wait to get a new message through OS mailbox
here I try to omit it and instead, referesh a global variable and flag to be aware in main routine that new message has been received
everything is OK without any OVF on FIFO; dAbt and pAbt
I think because the number of intterupts in Ethernet and CAN are too high the only way to cope such a problem is just speed up my ISR routine as Tamir Michael said.
thanks again
well done
I'm not sure I agree with your analysis. Yes, interrupts should always be as fast as possible. And they should clear out the data and leave the hardware in a stable state. You just may not get your queues full and leave data still in the hardware.
Another issue the ISR makes use of os_mbx_check() instead of isr_mbx_check().