指向外围硬件的指针作为模板参数

时间:2015-02-03 19:44:30

标签: c++ c++11 stm32

我想通过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

1 个答案:

答案 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>:

这不适用于优化,因为可以交换元素。