2009-12-23 5 views
4

AVR을 대상으로하는 C 코드가 있습니다. 이 코드는 기본적으로 올바른 백엔드가있는 gnu 컴파일러 인 avr-gcc로 컴파일됩니다.함수 포인터 위치가 전달되지 않습니다.

내가 뭘하려는 건 내 이벤트/인터럽트 구동 라이브러리 중 하나에서 콜백 메커니즘을 만드는 것입니다,하지만 난 몇 가지 문제가 함수 포인터의 가치를 유지하는 데있는 것 같습니다.

시작하려면 정적 라이브러리가 있어야합니다. 그것은 다음과 같습니다 헤더 파일 (twi_master_driver.h)을 가지고

#ifndef TWI_MASTER_DRIVER_H_ 
#define TWI_MASTER_DRIVER_H_ 

#define TWI_INPUT_QUEUE_SIZE 256 

// define callback function pointer signature 
typedef void (*twi_slave_callback_t)(uint8_t*, uint16_t); 

typedef struct { 
    uint8_t buffer[TWI_INPUT_QUEUE_SIZE]; 
    volatile uint16_t length; // currently used bytes in the buffer 
    twi_slave_callback_t slave_callback; 
} twi_global_slave_t; 

typedef struct { 
    uint8_t slave_address; 
    volatile twi_global_slave_t slave; 
} twi_global_t; 

void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback); 

#endif 

이제 C 파일 (twi_driver.c) : 다음

#include <stdint.h> 
#include "twi_master_driver.h" 

void twi_init(uint8_t slave_address, twi_global_t *twi, twi_slave_callback_t slave_callback) 
{ 
    twi->slave.length = 0; 
    twi->slave.slave_callback = slave_callback; 

    twi->slave_address = slave_address; 

    // temporary workaround <- why does this work?? 
    twi->slave.slave_callback = twi->slave.slave_callback; 
} 

void twi_slave_interrupt_handler(twi_global_t *twi) 
{ 
    (twi->slave.slave_callback)(twi->slave.buffer, twi->slave.length); 

    // some other stuff (nothing touches twi->slave.slave_callback) 
} 

내가 정적 라이브러리 (.A)로 두 파일을 구축하고 내 주요 프로그램을 구성 (main.c) 사용법 #include 사용법 #include 사용법 #include 사용법 #include 사용법 #include

,536,913,632 "twi_master_driver.h" 10
// ...define microcontroller safe way for mystdout ... 

twi_global_t bus_a; 

ISR(TWIC_TWIS_vect, ISR_NOBLOCK) 
{ 
    twi_slave_interrupt_handler(&bus_a); 
} 

void my_callback(uint8_t *buf, uint16_t len) 
{ 
    uint8_t i; 

    fprintf(&mystdout, "C: "); 
    for(i = 0; i < length; i++) 
    { 
     fprintf(&mystdout, "%d,", buf[i]); 
    } 
    fprintf(&mystdout, "\n"); 
} 

int main(int argc, char **argv) 
{ 
    twi_init(2, &bus_a, &my_callback); 

    // ...PMIC setup... 

    // enable interrupts. 
    sei(); 

    // (code that causes interrupt to fire) 

    // spin while the rest of the application runs... 
    while(1){ 
     _delay_ms(1000); 
    } 
    return 0; 
} 

인터럽트가 발생하는 이벤트를 신중하게 트리거하고 적절한 처리기를 호출합니다. fprintfs를 사용하여 twi_init 함수에서 twi->slave.slave_callback에 할당 된 위치가 twi_slave_interrupt_handler 함수의 함수와 다름을 알 수 있습니다.

숫자는 의미가 없지만 twi_init의 값은 0x13b이고 인쇄 할 때는 twi_slave_interrupt_handler의 값은 0x100입니다. twi_driver.c에 주석 해결 방법 줄을 추가하여

:

twi->slave.slave_callback = twi->slave.slave_callback; 

문제는 도망 간다, 그러나 이것은 분명히 마법과 바람직하지 않은 솔루션입니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?

내가 알 수있는 한, 적절한 변수 volatile을 표시했습니다. 다른 부분을 휘발성으로 표시하고 휘발성 표시를 제거하려고했습니다. twi_init에있는 할당 이후에 fprintf 문을 제거하면 나중에 값을 다르게 읽는 문제가 발생했을 때 해결 방법을 생각해 냈습니다.

문제는 함수 포인터 주위를 지나가고있는 것 같습니다. 특히 포인터의 값 (함수 자체?)에 액세스하는 프로그램 부분은 기술적으로 다른 스레드에 있습니다.

아이디어가 있으십니까?

편집 : 코드에서

  • 해결 오타. 실제 파일에

  • 링크 : http://straymark.com/code/ [TEST.C | twi_driver.c | twi_driver.h]

  • FWIW : 컴파일러 옵션 : 나도 같은 코드를 직접 포함 해봤 -Wall -Os -fpack-struct -fshort-enums -funsigned-char -funsigned-bitfields -mmcu=atxmega128a1 -DF_CPU=2000000UL

  • (도서관을 통하는 것이 아니라). 나는 똑같은 문제가있다.

편집 (2 라운드) : 나는 모든 최적화를 제거

  • 예상대로, 내 "해결 방법"없이 코드가 작동합니다. 다시 -O를 추가하면 오류가 발생합니다. 왜 -O는 코드를 손상시키는 걸까요?
+0

아마도 아무것도 바뀌지 않지만 main의 첫 번째 줄에서'&'를 제거해보십시오 :'twi_init (2, & bus_a, my_callback) ;; – pmg

+0

몇 시간 동안 나가서 나중에 다시 시도하고 다시 시도해 보겠습니다. 해결되지 않은 경우 : 간단한 아이디어; 라이브러리를 사용하지 않으면 작동합니까? (즉, 모든 파일을 하나의 파일로 표시). 인터럽트에서 정말로 fprintf() 할 수 있습니까 (위험한 소리가 들림)? 모든 것을 보여주는 재 게시; 당신이 가진 것은 세부 사항에서 완벽하지 않습니다. 예를 들어, my_callback()의 ​​길이 대 len과 같이, 인터럽트를 활성화하는 곳은 어디입니까? twi_driver.h 또는 twi_master_driver.h입니까? 아직 많이 도움이되지 않아서 미안해. 나중에 다시 시도 할거야. –

+0

avr-gcc에 의해 생성 된 어셈블리는 회피 라인이있는 경우와없는 경우 어떻게 다릅니 까? 즉 두 버전 모두에 대해'avr-gcc -S twi_driver.c'의 결과를 게시 할 수 있습니까? 어떤 AVR을 타겟팅하고 있습니까? – mrkj

답변

2
그냥 직감

,하지만 당신은 주변이 두 줄을 전환하면 어떤 일이 발생 :

twi->slave.slave_callback = slave_callback; 
twi->slave.length = 0; 

-fpack-struct GCC 플래그가 문제를 해결 제거합니까? 그 length 필드가 콜백 값의 일부를 덮어 쓰는 버그를 발견하지 못했습니까? 그것은에 -Os 최적화와 같은 날에 보이는


컴파일러가 조작 할 수있는 권한 코드를 방출되지 않으며, (당신이 -Os으로 활성화 개별 최적화의 조합을 시도 할 수 하나가 원인이되는 정확하게 볼 수 있습니다) uint16_t 길이 필드가 2 바이트 경계에 정렬되지 않은 경우. 이 경우 twi_global_t이 포장 된 twi_global_slave_t을 포함하면 twi_global_t의 초기 uint8_t 구성원이 twi_global_slave_t 구조체가 홀수 주소에 배치되기 때문입니다.

초기 필드가 twi_global_t 인 경우 uint16_t으로 수정하면 구조체 패킹을 해제 할 수 있습니다. 최신 gcc 빌드를 시도하고 여전히 발생하는지 확인하십시오. 문제가 있으면 최소한의 테스트 케이스를 작성하여 gcc 프로젝트에 버그 보고서를 제출할 수 있어야합니다.

+0

이 제대로 된 것 같습니다. 주문을 바꿔서 문제를 해결했습니다. 길이 필드가 콜백 필드를 덮어 쓰는 이유는 무엇입니까? –

+0

더 복잡한 것들, 구조체 요소를 재정렬하면 문제도 사라질 것 같습니다. –

0

main() 함수에서 "& my_callback"을 "my_callback"으로 바꿉니다.

다른 스레드가 콜백 주소에 액세스하므로 mutex 또는 읽기 - 쓰기 잠금으로 보호 해보십시오.

신호 처리기에서 콜백 함수 포인터에 액세스하지 않으면 "volatile"한정자가 필요하지 않습니다.

+0

나는 그것을 시도했다, 그것은 아무런 차이가 없다. 인터럽트가 활성화되기 전에 값이 설정되고 변경되지 않으므로 액세스 뮤텍스 읽기 - 쓰기 잠금이 필요하지 않습니다 (액세스 제어는 규칙에 따라 다름). –

+1

'my_callback'은 함수이기 때문에'& my_callback'과'my_callback' 사이에는 차이가 없습니다. "다른 스레드"는 실제로 인터럽트 컨텍스트에서 실행되는 코드이므로 잠금은 단순히 교착 상태가됩니다. 'twi_init' 함수가 실행되는 동안 인터럽트를 사용할 수 없도록하는 것이 필요합니다. 인터럽트가 그 시점 이후까지 가능하지 않으면 보장됩니다. – caf

+0

@caf, 그게 내가 가진거야. @Steve Emmerson : 콜백 함수 포인터는 신호 처리기에 의해 액세스됩니다. 즉, 해당 처리기 나 초기화에 의해 수정되지는 않습니다. –

1

정말 스택/메모리 손상 문제가있는 것 같습니다. elf 파일에서 avr-size를 실행하면 무엇을 얻게됩니까? (data + bss) < 부분에있는 RAM을 확인하십시오. 이러한 유형의 문제는 추적하기가 매우 어렵습니다. 관련없는 코드를 제거/이동하면 동작이 변경된다는 사실은 커다란 붉은 깃발입니다.