2014-02-27 4 views
0

ARM에 익숙하다면 (프로그래밍 문제) I/O 포트의 비트 주소 지정이 혼란 스럽습니다. 특정 포트 핀에서 상수를 정의 할 수 있지만 비트 값을 설정하여 설정해야합니다. 예 :포인터 주소를 int로 변환

#define MyOutput (*((volatile unsigned long *)0x40025040)) //PF4 
// But to set this bit you must write 
MyOutput = 0x10; 

이 느낌이 이상합니다. 특정 핀을 처리하면 1로 설정할 수 있습니다. 따라서 비트 값을 반드시 써야한다는 것을 잊지 않도록하려면이 작업을 수행하는 기능을 만들고 싶습니다. 나는 다음과 같이 생각해 왔지만 포인터 구문이나 포인터 변환에 문제가 있다고 생각한다.

int SetOutput(volatile unsigned long* PIN), int ONOFF){ //ON defined as 1, OFF defined as 0 
    volatile unsigned long PortBit = (PIN & 0xFF); 
    if (ONOFF){ 
     return ((PortBit & 0xFF)>>2); 
    } else { 
     return 0; 
    } 
} 

//Called by 
MyOutput = SetOutput(&MyOutput, ON); 

누구나 어떤 생각이나 조언이 있습니까? 고맙습니다!

+0

당신이 우리에게'PF4' 또는 주소 * 0x40025040 *에 대한 자세한 내용을 알 수 있습니까? SOC/CPU가 무엇입니까? 모든 GPIO 메모리 컨트롤러가 동일하지는 않습니다. –

+0

그것은 ARM Cortex M4입니다. 주소 0x40025040은 포트 F Pin4의 기본 포트입니다. 개별 비트 주소 지정을 허용하지만 여전히 비트 값으로 설정해야합니다 (예 : MyOutput = 0x10, PF4의 경우 1). 각 IO의 SETvalue에 대해 많은 상수를 작성하는 대신 핀의 주소를 기반으로 핀 비트 마스크를 작성하는 함수를 작성하고 싶습니다. 원하는 주소와 값을 전달하면 해당 핀의 올바른 비트 마스크가 반환됩니다. 나는 논리가 정확하다고 생각하지만 그것은 int 주소를 변환하는 데 문제가있는 것 같습니다. 바이너리 표현식 ('long *'및 'int ')에 대한 잘못된 피연산자 가져 오기 – chrismec

+0

포인터를 int로 캐스트하여 마스크 할 수 있습니다. 예를 들어,'bit = ((uint32) PIN & 0x1f0UL) >> 4;'그리고 나서'mask = 1 << bit;'를 사용하십시오. 포트가 0x40025000, 0x40025010, 0x40025020, 0x40025030, 0x40025040 ...이라고 추측합니다. 따라서 두 번째로 낮은 니블을 선택하고 비트 시프트로 사용하십시오. 일반적으로 핀 간격은 고정되어 있으며 16 바이트라고 가정합니다. –

답변

1

개별 비트를 처리 할 수 ​​없습니다. C에서 (그리고 대개 하드웨어에서) 최소 어드레싱 가능 유닛은 하나의 char, 즉 일반적으로 8 비트의 바이트이다.

일반적인 접근 방식은 래퍼 매크로 또는 함수를 작성하는 것입니다. SetOutput에 관해서는 꽤 깨진 것 같습니다. 예를 들어, void 함수에서 값을 반환하려고 시도하고 0xFF 마스크는 1이 아닌 8 비트를 격리하며 출력에 결코 쓰지 않습니다 레지스터. 비트가 0x10 당신이 원하는 핀을 제어 할 경우, 일반적인 방법은 다음과 같습니다 당신이 필요한 주변 매크로를 만들 수 있습니다

MyOutput |= 0x10; // set bit 
MyOutput &= ~0x10; // unset bit 
MyOutput ^= 0x10; // toggle bit 

. 입력 레지스터의 해당 비트의 상태를 확인하려면, 당신은 사용할 수 있습니다 : 당신의 MMV 있도록

if (MyInput & 0x10) { 
    // bit is set 
} 
+0

또한 실제로 비트를 설정하지 않습니다. –

+1

Cortex-M과 같은 일부 ARM CPU에는 [비트 밴딩] (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/Behcjiic.html)이 있습니다. 하드웨어와 관련된 일반적인 가정은 올바르지 않을 수 있습니다. 즉, 나는이 질문에 대해 옳은 것이 무엇인지 모른다. 그러나이 답변에는 몇 가지 가정이있을 수 있다고 생각합니다. 대부분의 경우에 맞습니다. –

+1

@artlessnoise 가정은 기본적으로'sizeof (char)'를'1'로 정의하는 표준 C 다. 따라서 이런 의미에서 포인터 연산을 사용하여 정밀도가 더 높은 모든 것을 직접 주소 지정하는 방법은 없다. 비트 밴딩은 특정 단어 나 바이트를 개별 비트에 매핑하여이를 해결하는 방법이라고 생각합니다. – Arkku

2

크리스, 나는, 코어 텍스 M 전문가가 아니다. 그러나 위의 설명을 기반으로 주변 장치 레지스터의 단일 비트를 수정하는 데 상당히 복잡한 방법을 사용하려고합니다. 정상적으로 처리하는 데는 두 가지 방법이 있습니다.

  1. 매크로 집합 (가장 익숙한 매크로)을 사용하십시오. 이렇게하면 매크로가 컴파일 시간 동안 필요한 정확한 값/연산으로 직접 변환되기 때문에 핀의 각 읽기/쓰기에 대한 공간 및 CPU 시간과 호출 함수 오버 헤드가 줄어 듭니다.

  2. 대신 PF4 레지스터의 비트 밴드 주소를 사용하십시오 (Cortex-M을 사용하지 않았습니다). 이것은 당신의 아키텍쳐에서 선호하는 방법처럼 보입니다.

ARM Cortex-M4 기술 참조 설명서 3.4 절에 따라 비트 밴드 별칭 주소를 사용하여 PF4 비트를 수정할 수 있습니다. TRM 섹션 3.7에서 그 작동 방식에 대한 세부 정보를 찾을 수 있습니다.

주소 0x40025040에서 PF4의 위의 코드 인 비트 4를 기반으로, 비트 밴드의 공식은 (공정 사용에 따라 TRM에서 촬영) 제공합니다 bit_band_base

별칭 영역의 시작 주소입니다. (0x42000000) byte_offset은 대상 비트가 들어있는 비트 밴드 영역의 바이트 수입니다. (0x00025040) • bit_number은 대상 비트의 비트 위치 (0 ~ 7)입니다. (0x4로) bit_word_offset

bit_word_offset = (byte_offset x 32) + (bit_number × 4) 

bwo = 0x25040* 0x20 + 0x4 * 0x4 = 0x004A0810 

bit_word_addr = bit_band_base + bit_word_offset 

bwa = 0x42000000 + 0x4A0810 = 0x424A0810 

는 비트 밴드 메모리 영역의 목표 비트의 위치이다. bit_word_addr은 대상 비트에 매핑되는 별칭 메모리 영역의 단어 주소입니다.

그래서

*(volatile unsigned long *)0x424A0810 = 0x1; 

당신이 정말 기능과 직접 쓰기를 사용하는 경로를 이동하려면

*MyOutput |= 0x10; 

를 작성 동일, 대신 단지 PF31을 (제한이 시도 PF 경우 독자보다는 독자적으로 구현 된 31 개 이상 필요). 이 코드에는 PC 기반 테스트 #define이 포함되어 있으므로 명령 줄에서 gcc로 컴파일 할 수 있습니다.

#include <inttypes.h> 
#include <stdio.h> 

#define PC_TESTING 1 

#if PC_TESTING 
    unsigned long FAKE_PFBASE; 
    #define PFBASE &FAKE_PFBASE 
#else 
    #define PFBASE (volatile unsigned long *) 0x40025040 
#endif 

#define SUCCESS 0 
#define ERROR_INVALID_PIN -1 
#define ERROR_INVALID_STATE -2 

typedef enum {OFF = 0, ON} ONOFF_t; 

typedef enum { PF0 = 0, PF1, PF2, PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10, PF11, PF12, PF13, PF14, PF15, PF16, PF17, PF18, PF19, PF20, PF21, PF22, PF23, PF24, PF25, PF26, PF27, PF28, PF29, PF30, PF31 } PIN_t; 

int SetOutput(PIN_t PIN, ONOFF_t ONOFF) 
{ 
    uint32_t mask, value; 

    // Implementing more than 32 pins is exercise for the reader 
    if (PIN > 31) 
     return ERROR_INVALID_PIN; 

    // In case someone did something wrong 
    if (ONOFF != OFF && ONOFF != ON) 
     return ERROR_INVALID_STATE; 

    //Broken into separate steps for ease of reading 
    //Select the bit of interest 
    mask = 1 << PIN; 

    //Clear the bit of interest 
    value = *PFBASE & ~mask; //~ is a bit-wise invert. 0x0000 0010 becomes 0xFFFF FFEF 

    //Set the bit of interest if that is requested 
    if(ON == ONOFF) 
     value |= mask; 

    *PFBASE = value; 

    return SUCCESS; 
} 

int main() 
{ 
int success = 0; 

FAKE_PFBASE = 0l; 

success = SetOutput(PF4, ON); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

success = SetOutput(PF0, ON); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

success = SetOutput(PF0, OFF); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

//Error handling left to reader 
success = SetOutput(33, OFF); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

success = SetOutput(PF2, 2); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

return 0; 
} 

샘플 출력 :

$ ./a.out 
Success = 0, *PFBASE = 0x00000010 
Success = 0, *PFBASE = 0x00000011 
Success = 0, *PFBASE = 0x00000010 
Success = -1, *PFBASE = 0x00000010 
Success = -2, *PFBASE = 0x00000010