我想通过C ++模板访问STM32F0外设寄存器。 GPIO端口由供应商头文件定义如下:
摘录stm32f0xx.h
#define __IO volatile //!< Defines 'read / write' permissions
typedef struct
{
__IO uint32_t MODER;
__IO uint16_t OTYPER;
uint16_t RESERVED0;
__IO uint32_t OSPEEDR;
__IO uint32_t PUPDR;
__IO uint16_t IDR;
uint16_t RESERVED1;
__IO uint16_t ODR;
uint16_t RESERVED2;
__IO uint32_t BSRR;
__IO uint32_t LCKR;
__IO uint32_t AFR[2];
__IO uint16_t BRR;
uint16_t RESERVED3;
} GPIO_TypeDef;
#define PERIPH_BASE ((uint32_t)0x40000000)
#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000)
#define GPIOA_BASE (AHB2PERIPH_BASE + 0x00000000)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
我为输出处理创建了一个模板类。
main.cpp中:
template <uintptr_t port, uint8_t pin>
class Output {
public:
static void set() {
GPIO_TypeDef *castedPort = reinterpret_cast<GPIO_TypeDef *>(port);
castedPort->ODR = (1 << pin);
}
};
int main(void)
{
Output<GPIOA_BASE, 5>::set();
while(1)
{
}
}
这个代码运行正常,如果我使用launchpad g ++ for arm编译它。但我想测试我的代码 使用GoogleTest,所以我对它进行了测试并尝试编译它。
intArgument.cpp:
#include "gtest/gtest.h"
typedef struct {
/* see above definition */
} GPIO_TypeDef;
uint32_t gpioa[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
template <uintptr_t port, int pin>
class Output {
public:
static void set() {
GPIO_TypeDef * castedPort = reinterpret_cast<GPIO_TypeDef *>(port);
castedPort->ODR = (1 << pin);
}
};
TEST(OutputTest, OutputDataRegisterWritten) {
Output<gpioa, 5>::set();
GPIO_TypeDef * port = reinterpret_cast<GPIO_TypeDef *>(gpioa);
EXPECT_EQ((1 << 5), port->ODR);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
但是现在编译失败了。不允许通过reinterpret_cast
强制转换为int,因为它不再是常量表达式。
fabian@ubuntu:~/workspace/stackOverflowQuestion$ g++ -std=c++11 intArgument.cpp -lgtest -pthread -o intptrArgument.out
intArgument.cpp: In member function ‘virtual void OutputTest_OutputDataRegisterWritten_Test::TestBody()’:
intArgument.cpp:23:18: error: conversion from ‘uint32_t* {aka unsigned int*}’ to ‘long unsigned int’ not considered for non-type template argument
Output<gpioa, 5>::set();
^
intArgument.cpp:23:18: error: could not convert template argument ‘gpioa’ to ‘long unsigned int’
intArgument.cpp:23:26: error: invalid type in declaration before ‘;’ token
Output<gpioa, 5>::set();
所以我尝试将port
的类型更改为GPIO_TypeDef *
。
pointerArgument.cpp:
typedef struct {
/* see above definition */
} GPIO_TypeDef;
GPIO_TypeDef gpioa;
// using GPIO_TypeDef * as template argument
template <GPIO_TypeDef * port, int pin>
class Output {
public:
static void set() {
port->ODR = (1 << pin);
}
};
TEST(OutputTest, OutputDataRegisterWritten) {
Output<&gpioa, 5>::set();
EXPECT_EQ((1 << 5), gpioa.ODR);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
编译并测试通过。
fabian@ubuntu:~/workspace/stackOverflowQuestion$ g++ -std=c++11 pointerArgument.cpp -lgtest -pthread -o pointerArgument.out
fabian@ubuntu:~/workspace/stackOverflowQuestion$ ./test.out
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from OutputTest
[ RUN ] OutputTest.OutputDataRegisterWritten
[ OK ] OutputTest.OutputDataRegisterWritten (0 ms)
[----------] 1 test from OutputTest (1 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (1 ms total)
[ PASSED ] 1 test.
但是对于arm编译器来说,使用这种方法是失败的:
的main.cpp
template <GPIO_TypeDef * port, uint8_t pin>
class Output {
public:
static void set() {
port->ODR = (1 << pin);
}
};
int main(void)
{
Output<GPIOA, 5>::set();
while(1)
{
}
}
compiler error:
[cc] main.cpp:13:17: error: '1207959552u' is not a valid template argument for 'GPIO_TypeDef*' because it is not the address of a variable
[cc] main.cpp:13:25: error: invalid type in declaration before ';' token
我理解这两个错误,但有没有办法让这项工作?我搜索了编译器标志,但没有找到
任何,可能会改变这种行为。 #define TESTING
与#ifdef/#ifndef
相结合可能有效,但我不这样做
喜欢它,因为经过测试的代码与生成的代码不同。也许有更好的解决方案?
二手编译器:
g++ (i686-posix-dwarf-rev3, Built by MinGW-W64 project), 4.9-2014q4 by Launchpad for STM32F0XX
g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2 for Testing
答案 0 :(得分:0)
使用链接器标志Wl,section-start
,您可以定义指定节的起始地址。所以首先我强迫我的寄存器模拟在一个自己的部分:
GPIO_TypeDef gpioa __attribute__((section(".myTestRegisters")));
GPIO_TypeDef gpiob __attribute__((section(".myTestRegisters")));
我还为部分开始和每个寄存器定义了const uintptr_t
个地址值。
const uintptr_t myTestRegisterSectionAddress = 0x8000000;
const uintptr_t gpioaAddress = myTestRegisterSectionAddress;
const uintptr_t gpiobAddress = myTestRegisterSectionAddress + sizeof(GPIO_TypeDef);
这些值我可以用作模板参数。
template <uintptr_t port, int pin>
class Output {
public:
static void set() {
GPIO_TypeDef * castedPort = reinterpret_cast<GPIO_TypeDef *>(port);
castedPort->ODR = (1 << pin);
}
};
TEST(OutputTest, OutputDataRegisterWritten) {
Output<gpioaAddress, 5>::set();
EXPECT_EQ(1 << 5, gpioa.ODR);
Output<gpiobAddress, 10>::set();
EXPECT_EQ(1 << 10, gpiob.ODR);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
要强制部分的起始地址,您可以使用Wl,-section-start=.{sectionName}={startAddress}
。
所以在我的情况下,我使用:
g++ intArgument.cpp -std=c++11 -g -o intArgument.out -lgtest -pthread -Wl,-section-start=.myTestRegisters=0x8000000
运行应用程序导致:
fabian@ubuntu:~/workspace/stackOverflowQuestion$ ./intArgument.out
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from OutputTest
[ RUN ] OutputTest.OutputDataRegisterWritten
[ OK ] OutputTest.OutputDataRegisterWritten (0 ms)
[----------] 1 test from OutputTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
objdump显示以下信息:
fabian@ubuntu:~/workspace/stackOverflowQuestion$ objdump -S -j .myTestRegisters intArgument.out
intArgument.out: file format elf64-x86-64
Disassembly of section .myTestRegisters:
0000000008000000 <gpioa>:
...
000000000800000c <gpiob>:
这不适用于优化,因为可以交换元素。