克里斯, 我不是Cortex-M的專家,所以你的MMV。但是根據你上面的描述,你試圖用一種非常複雜的方式來修改外設寄存器中的一個位。有兩種方式可以正常處理它:
使用一組宏(我最熟悉的)。由於宏在編譯期間直接轉換爲所需的精確值/操作,因此這會減少空間和CPU時間與每次讀/寫引腳的調用函數相比的開銷。
改爲使用PF4寄存器的位帶地址(從未使用過Cortex-M)。這看起來像是用你的架構去做的首選方式。
根據ARM Cortex-M4技術參考手冊的第3.4節,您可以使用位帶別名地址修改PF4位。關於如何運作的細節可以在TRM的第3.7節中找到。
基於您的代碼以上,PF4的是4位在地址0x40025040,對位帶式給出(從TRM下合理使用截取):
•bit_band_base
是別名區的起始地址。 (0x42000000) •byte_offset
是包含目標位的位帶區域中的字節數。 (0x00025040) •bit_number
是目標位的位位置0到7。 (爲0x4)
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_offset
處於位帶存儲區中的目標位的位置。 •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
你能告訴我們更多關於'PF4'或地址* 0x40025040 *?這是什麼SOC/CPU?並非所有的GPIO內存控制器都是相同的。 –
這是一個ARM Cortex M4。地址0x40025040是端口F引腳4的基礎。它允許單獨的位尋址,但您仍然必須使用它的位值進行設置,比如MyOutput = 0x10而不是PF4的1。我不想爲每個IO的SET值創建很多常量,而是希望創建一個基於引腳地址創建引腳位掩碼的函數。傳入您想要的地址和值,並返回該引腳的正確位掩碼。我認爲這個邏輯是正確的,但似乎我無法將地址轉換爲int。 Getinvalid操作數爲二進制表達式('long *'和'int')。 – chrismec
您可以將指針轉換爲int並對其進行掩碼。例如,'bit =((uint32)PIN&0x1f0UL)>> 4;'然後使用'mask = 1 << bit;'。我猜測端口是0x40025000,0x40025010,0x40025020,0x40025030,0x40025040,...所以選擇第二個最低半字節並將其用作位移。通常這些引腳是固定的間距,我猜是16個字節。 –