ARM的新手(以及针对该问题的编程)并发现I / O端口的位寻址令人困惑。您可以在特定端口引脚上定义常量,但仍必须写入其位值以进行设置。例如:
#define MyOutput (*((volatile unsigned long *)0x40025040)) //PF4
// But to set this bit you must write
MyOutput = 0x10;
这对我来说很奇怪。如果我寻址某个引脚,我应该能够设置它1.为了防止我忘记我必须写出它的位值,我想为我做一个函数。我已经提出了以下内容,但是我的指针语法或指针转换为int我遇到了麻烦。
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 :(得分:2)
克里斯, 我不是Cortex-M专家,所以你的MMV。但根据您上面的描述,您试图使用一种非常复杂的方法来修改外设寄存器中的单个位。有两种方法可以正常处理它:
使用一组宏(我最熟悉的)。这将减少每次读/写引脚的调用函数的空间和CPU时间开销,因为宏在编译期间直接转换为所需的精确值/操作。
使用PF4寄存器的位带地址(从未使用过Cortex-M)。这看起来是使用您的架构的首选方式。
根据ARM Cortex-M4技术参考手册的3.4节,您可以使用位带别名地址来修改PF4位。有关其工作原理的详细信息,请参见TRM的第3.7节。
根据您的上述代码,PF4为地址0x40025040的第4位,位带公式给出(取自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
答案 1 :(得分:1)
你不能解决个别位; C中(通常在硬件中)的最小可寻址单位是一char
,即通常是8位的字节。
典型的方法是编写包装器宏或函数。至于你的SetOutput
,它似乎很破碎,例如,它试图从void
函数返回一个值,0xFF
掩码隔离8位,而不是1位(这是pin可能是),它从不写入输出寄存器。如果位0x10
控制您想要的引脚,则典型的方法是:
MyOutput |= 0x10; // set bit
MyOutput &= ~0x10; // unset bit
MyOutput ^= 0x10; // toggle bit
您可以根据需要在这些周围创建宏。要检查输入寄存器中相应位的状态,可以使用:
if (MyInput & 0x10) {
// bit is set
}