多久为微控制器开发固件时,在调试过程中,未在UART上运行字节时,您会大声喊道:“啊!没有启用时钟!” 或者,当您更换LED支脚时,您是否忘了为新端口“通电”?我认为通常是这样。我,至少-可以肯定。
乍一看,控制外围设备的时序似乎很简单:写入1-启用,0-禁用。
但是“简单”并不总是有效的...
问题的提法
在编写代码之前,有必要确定评估代码的标准。对于控制器外设时钟系统,列表可能如下所示:
- 在嵌入式系统中,最重要的标准之一是在尽可能短的时间内执行最小的结果代码。
- . - code review , /
- , ,
- ( )
弄清评估标准后,我们将在定义条件和实现的“环境”的过程中设置一个特定的任务:
编译器:GCC 10.1.1 + Make
语言:C ++ 17
环境:Visual Studio代码
控制器:stm32f103c8t6(cortex-m3)
任务:启用时钟SPI2,USART1(均使用DMA的两个接口)
控制器的选择当然是由于它的普遍性,特别是由于中国民间工艺之一-蓝色药丸板的生产。
从意识形态的角度来看,选择哪个控制器无关紧要:stmf1,stmf4或lpc,因为使用外设时钟系统的工作仅减少为写入某个位,要么关闭0,要么打开1。
在stm32f103c8t6中,有3个负责启用外设时钟的寄存器:AHBENR,APB1ENR,APB2ENR。
并非偶然选择用于数据传输SPI2和USART1的硬件接口,因为要充分发挥功能,必须启用所有列出的寄存器中的时钟位-接口本身的位,DMA1以及输入输出端口的位(SPI2的GPIOB和USART1的GPIOA)。
应该注意的是,为了获得时钟的最佳性能,有必要考虑-AHBENR包含用于SPI2和USART1的功能的共享资源。也就是说,禁用DMA将立即导致两个接口的不可操作性,同时,重合闸效率甚至不会为零,而是负值,因为此操作将占用程序存储器,并会导致读取-修改-写入易失性寄存器的额外时钟消耗。
解决了问题的目标,条件和特征后,让我们继续寻找解决方案。
基本方法
本节包含启用外设时钟的典型方法,当然,您也已经看到和/或使用了它们。从用C实现的简单表达式到从C ++ 17折叠表达式。考虑其固有的优点和缺点。
如果要直接进行元编程,则可以跳过本节,转到下一个。
直接写入寄存器
对于C和C ++,“开箱即用”的经典方法。供应商通常为控制器提供头文件,所有寄存器及其位均默认为该头文件,这使得可以立即开始使用外围设备:
int main(){
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN
| RCC_APB2ENR_IOPBEN
| RCC_APB2ENR_USART1EN;
RCC->APB2ENR |= RCC_APB1ENR_SPI2EN;
…
}
清单
// AHBENR( DMA1)
ldr r3, .L3
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// APB2ENR( GPIOA, GPIOB, USART1)
ldr r2, [r3, #24]
orr r2, r2, #16384
orr r2, r2, #12
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
代码大小:36个字节。查看
优点:
- 最小代码大小和执行速度
- 最简单,最明显的方法
缺点:
- 必须记住寄存器的名称和位的名称,或者经常参考手册
- 在代码中容易出错。读者一定已经注意到,USART1被重新启用,而不是SPI2。
- 为了使某些外围设备正常工作,还需要启用其他外围设备,例如GPIO和DMA作为接口
- 完全缺乏便携性。选择其他控制器时,此代码将失去其含义
尽管存在所有缺点,但至少在需要通过
初始化功能
让我们尝试从用户那里提取和隐藏带有寄存器的作品。普通的C函数将帮助我们:
void UART1_Init(){
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN
| RCC_APB2ENR_USART1EN;
//
}
void SPI2_Init(){
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
//
}
int main(){
UART1_Init();
SPI2_Init();
…
}
代码大小:72个字节。看
清单
UART1_Init():
// AHBENR( DMA1)
ldr r2, .L2
ldr r3, [r2, #20]
orr r3, r3, #1
str r3, [r2, #20]
// APB2ENR( GPIOA, USART1)
ldr r3, [r2, #24]
orr r3, r3, #16384
orr r3, r3, #4
str r3, [r2, #24]
bx lr
SPI2_Init():
// (!) AHBENR( DMA1)
ldr r3, .L5
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// (!) APB2ENR( GPIOB)
ldr r2, [r3, #24]
orr r2, r2, #8
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
bx lr
main:
push {r3, lr}
bl UART1_Init()
bl SPI2_Init()
优点:
- 您不必每次都查看手册。
- 错误在编写外围设备驱动程序的阶段被本地化
- 定制代码易于阅读
缺点:
- 所需指令的数量增加了所涉及外围设备的数量的倍数
- 大量代码重复-对于每个UART和SPI编号,它几乎是相同的
尽管我们摆脱了直接在用户代码中写入寄存器的方式,但是付出了什么代价?打开所需的内存大小和执行时间已加倍,并且将继续增加,其中涉及更多外围设备。
时钟使能功能
让我们将时钟的修改包装在一个单独的函数中,假设这将减少所需的内存量。同时,我们将为外围设备引入一个标识符参数-减少驱动程序代码:
void PowerEnable(uint32_t ahb, uint32_t apb2, uint32_t apb1){
RCC->AHBENR |= ahb;
RCC->APB2ENR |= apb2;
RCC->APB1ENR |= apb1;
}
void UART_Init(int identifier){
uint32_t ahb = RCC_AHBENR_DMA1EN, apb1 = 0U, apb2 = 0U;
if (identifier == 1){
apb2 = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
}
else if (identifier == 2){…}
PowerEnable(ahb, apb2, apb1);
//
}
void SPI_Init(int identifier){
uint32_t ahb = RCC_AHBENR_DMA1EN, apb1 = 0U, apb2 = 0U;
if (identifier == 1){…}
else if (identifier == 2){
apb2 = RCC_APB2ENR_IOPBEN;
apb1 = RCC_APB1ENR_SPI2EN;
}
PowerEnable(ahb, apb2, apb1);
//
}
int main(){
UART_Init(1);
SPI_Init(2);
…
}
代码大小:92个字节。看
清单
PowerEnable(unsigned long, unsigned long, unsigned long):
push {r4}
ldr r3, .L3
ldr r4, [r3, #20]
orrs r4, r4, r0
str r4, [r3, #20]
ldr r0, [r3, #24]
orrs r0, r0, r1
str r0, [r3, #24]
ldr r1, [r3, #28]
orrs r1, r1, r2
str r1, [r3, #28]
pop {r4}
bx lr
UART_Init(int):
push {r3, lr}
cmp r0, #1
mov r2, #0
movw r1, #16388
it ne
movne r1, r2
movs r0, #1
bl PowerEnable(unsigned long, unsigned long, unsigned long)
pop {r3, pc}
SPI_Init(int):
push {r3, lr}
cmp r0, #2
ittee eq
moveq r1, #8
moveq r1, #16384
movne r1, #0
movne r2, r1
movs r0, #1
bl PowerEnable(unsigned long, unsigned long, unsigned long)
pop {r3, pc}
main:
push {r3, lr}
movs r0, #1
bl UART_Init(int)
movs r0, #2
bl SPI_Init(int)
优点:
- 可以缩短微控制器驱动程序的描述代码
- 指令数量减少*
缺点:
- 执行时间增加
*是的,在这种情况下,可执行代码的大小与以前的版本相比有所增加,但这是由于条件运算符的出现,如果使用每种外围设备的至少2个副本,则可以消除其影响。
因为 include函数接受参数,然后堆栈操作出现在汇编程序中,这也对性能产生负面影响。
在这一点上,我认为
值属性和模板
开始考虑积极的方法之后,我们将立即跳过在类构造函数中包含时钟的选项,因为 这个方法实际上与C风格的初始化函数没有什么不同。
由于在编译时我们知道需要写入寄存器的所有值,因此我们将摆脱堆栈操作。为此,我们将使用模板方法创建一个单独的类,并为外围类赋予属性(值特征),该属性将存储相应寄存器的值。
struct Power{
template< uint32_t valueAHBENR, uint32_t valueAPB2ENR, uint32_t valueAPB1ENR>
static void Enable(){
// = 0,
if constexpr (valueAHBENR)
RCC->AHBENR |= valueAHBENR;
if constexpr (valueAPB2ENR)
RCC->APB2ENR |= valueAPB2ENR;
if constexpr (valueAPB1ENR)
RCC->APB1ENR |= valueAPB1ENR;
};
};
template<auto identifier>
struct UART{
// identifier
static constexpr auto valueAHBENR = RCC_AHBENR_DMA1EN;
static constexpr auto valueAPB1ENR = identifier == 1 ? 0U : RCC_APB1ENR_USART2EN;
static constexpr auto valueAPB2ENR = RCC_APB2ENR_IOPAEN
| (identifier == 1 ? RCC_APB2ENR_USART1EN : 0U);
//
};
template<auto identifier>
struct SPI{
static constexpr auto valueAHBENR = RCC_AHBENR_DMA1EN;
static constexpr auto valueAPB1ENR = identifier == 1 ? 0U : RCC_APB1ENR_SPI2EN;
static constexpr auto valueAPB2ENR = RCC_APB2ENR_IOPBEN
| (identifier == 1 ? RCC_APB2ENR_SPI1EN : 0U);
//
};
int main(){
//
using uart = UART<1>;
using spi = SPI<2>;
Power::Enable<
uart::valueAHBENR | spi::valueAHBENR,
uart::valueAPB2ENR | spi::valueAPB2ENR,
uart::valueAPB1ENR | spi::valueAPB1ENR
>();
…
}
代码大小:36个字节。看
清单
main:
// AHBENR( DMA1)
ldr r3, .L3
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// APB2ENR( GPIOA, GPIOB, USART1)
ldr r2, [r3, #24]
orr r2, r2, #16384
orr r2, r2, #12
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
优点:
- 直接写入寄存器的大小和执行时间与参考版本相同
- 扩展项目非常容易-只需添加
水的外围属性值就足够了
缺点:
- 通过将value属性放在错误的参数中可能会导致错误
- 与直接写入寄存器的情况一样,可移植性受到影响
- 施工超载
我们能够实现几个既定目标,但是使用起来方便吗?我认为不是,因为要添加另一块外围设备,必须控制方法模板的参数中类属性的正确排列。
理想...差不多
为了减少自定义代码的数量和出错的机会,我们将使用参数包,该参数包将删除对自定义代码中外围类的属性的访问。这只会更改启用时钟的方法:
struct Power{
template<typename... Peripherals>
static void Enable(){
// |
// value = uart::valueAHBENR | spi::valueAHBENR ..
if constexpr (constexpr auto value = (Peripherals::valueAHBENR | ... ); value)
RCC->AHBENR |= value;
if constexpr (constexpr auto value = (Peripherals::valueAPB2ENR | ... ); value)
RCC->APB2ENR |= value;
if constexpr (constexpr auto value = (Peripherals::valueAPB1ENR | ... ); value)
RCC->APB1ENR |= value;
};
};
…
int main(){
//
using uart = UART<1>;
using spi = SPI<2>;
Power::Enable<uart, spi>();
…
}
代码大小:36个字节。看
清单
main:
// AHBENR( DMA1)
ldr r3, .L3
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// APB2ENR( GPIOA, GPIOB, USART1)
ldr r2, [r3, #24]
orr r2, r2, #16384
orr r2, r2, #12
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
与以前的版本相比,用户代码的简便性得到了极大的提高,出错的可能性变得最小,并且内存消耗保持在同一水平。
而且,看来,您可以在此停下来,但是...
扩展功能
让我们转向目标之一:
除了启用和禁用外设时钟的基本功能外,还需要高级功能
假设任务是使设备低功耗,为此,当然,需要关闭控制器不使用的所有外围设备以退出省电模式。
在本文开头所述的情况下,我们将假定唤醒事件的生成器为USART1,并且必须禁用SPI2和相应的GPIOB端口。在这种情况下,共享资源DMA1必须保持启用状态。
使用上一部分中的任何选项,将不可能高效,最佳地解决此问题,并且同时又不对涉及的块进行手动控制。
例如,让我们采取最后一种方式:
int main(){
using uart = UART<1>;
using spi = SPI<2>;
…
// USART, SPI, DMA, GPIOA, GPIOB
Power::Enable<uart, spi>();
// Some code
// SPI GPIOB (!) DMA
Power::Disable<spi>();
// DMA (!) USART GPIOA
Power::Enable<uart>();
// Sleep();
// SPI GPIOB (!) DMA
Power::Enable<spi>();
…
}
代码大小:100字节。看
清单
main:
// AHBENR( DMA1)
ldr r3, .L3
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// APB2ENR( GPIOA, GPIOB, USART1)
ldr r2, [r3, #24]
orr r2, r2, #16384
orr r2, r2, #12
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
// SPI2
// AHBENR( DMA1)
ldr r2, [r3, #20]
bic r2, r2, #1
str r2, [r3, #20]
// APB2ENR( GPIOB)
ldr r2, [r3, #24]
bic r2, r2, #8
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
bic r2, r2, #16384
str r2, [r3, #28]
// (!) USART1
// AHBENR( DMA1)
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// APB2ENR( GPIOA, USART1)
ldr r2, [r3, #24]
orr r2, r2, #16384
orr r2, r2, #4
str r2, [r3, #24]
// Sleep();
// AHBENR( DMA1)
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// APB2ENR( GPIOB)
ldr r2, [r3, #24]
orr r2, r2, #8
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
同时,寄存器中的参考代码占用了68个字节。查看
显然,对于此类任务,绊脚石将是共享资源,例如DMA。另外,在这种特殊情况下,有时两个接口都无法使用,并且实际上会发生紧急情况。
让我们尝试找到解决方案...
结构体
为了简化理解和发展,我们将描述我们想要的通用时序结构:
它仅包含四个模块:
独立:
- IPower-用户界面,准备数据以写入寄存器
- 硬件-将值写入控制器寄存器
取决于硬件:
- 外围设备-项目中使用的外围设备,告诉接口应该打开或关闭哪些设备
- 适配器-传输要写入硬件的值,指示应将值写入哪个寄存器
IPower接口
考虑所有要求后,我们将在界面中定义所需的方法:
template<typename… Peripherals>
Enable();
template<typename EnableList, typename ExceptList>
EnableExcept();
template<typename EnableList, typename DisableList>
Keep();
启用-启用模板参数中指定的外围设备。
EnableExcept-启用EnableList参数中指定的外围设备,但ExceptList中指定的外围设备除外。
说明
, :
SPI2EN IOPBEN. , DMA1EN, USART1EN IOPAEN .
, :
0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 0 |
, :
EnableExcept<spi, uart>();
SPI2EN IOPBEN. , DMA1EN, USART1EN IOPAEN .
, :
resultEnable = (enable ^ except) & enable
这些可以通过相反的补充Disable方法来补充。
保留-从EnableList启用外围设备,从DisableList禁用外围设备,而如果两个列表中都存在外围设备,则它将保持其状态。
说明
, :
SPI2EN IOPBEN, USART1EN IOPAEN , DMA1EN .
, :
0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 0 |
, :
Keep<spi, uart>();
SPI2EN IOPBEN, USART1EN IOPAEN , DMA1EN .
, :
resultEnable = (enable ^ disable) & enable
resultDisable = (enable ^ disable) & disable
使用折叠表达式已经很好地实现了on / off方法,但是其余的呢?
如果我们像解释中那样限制使用两种类型的外围设备,则不会出现任何困难。但是,当项目使用许多不同的外围设备时,会出现问题-您不能在模板中显式使用多个参数包,因为 编译器将无法确定一个终止于何处,第二个终止于何处:
template<typename… EnableList, typename… ExceptList>
EnableExcept(){…};
// EnableList ExceptList
EnableExcept<spi2, pin3, uart1, pin1, i2c3>();
可以为外围设备创建一个单独的包装器类,并将其传递给方法:
template<typename… Peripherals>
PowerWrap{
static constexpr auto valueAHBENR = (Peripherals::valueAHBENR | …);
static constexpr auto valueAPB1ENR = (Peripherals:: valueAPB1ENR | …);
static constexpr auto valueAPB2ENR = (Peripherals:: valueAPB2ENR | …);
};
using EnableList = PowerWrap<spi2, uart1>;
using ExceptList = PowerWrap<pin1, i2c1>;
EnableExcept<EnableList, ExceptList>();
但是即使在这种情况下,接口也将严格地与寄存器的数量绑定在一起,因此,对于每种类型的控制器,都将有必要编写自己的单独类,并进行许多相同类型的操作,而又不可能分成抽象层。
由于使用的所有外设和时钟寄存器在编译阶段都是已知的,因此可以使用元编程来解决该任务。
元编程
由于元编程不是基于普通类型而是基于列表,因此我们将定义两个实体,它们将使用典型和非典型参数进行操作:
template<typename... Types>
struct Typelist{};
template<auto... Values>
struct Valuelist{};
…
using listT = Typelist<char, int> ;// char int
…
using listV = Valuelist<8,9,5,11> ;// 4
在对这些列表进行任何有用的处理之前,我们需要实现一些基本操作,从而可以执行更复杂的操作。
1.从列表中检索第一项
面前
//
template<typename List>
struct front;
//
//
template<typename Head, typename... Tail>
struct front<Typelist<Head, Tail...>>{
//
using type = Head;
};
//
template<auto Head, auto... Tail>
struct front<Valuelist<Head, Tail...>> {
//
static constexpr auto value = Head;
};
//
template<typename List>
using front_t = typename front<List>::type;
template<typename List>
static constexpr auto front_v = front<List>::value;
//
using listT = Typelist<char, bool, int>;
using type = front_t<listT>; // type = char
using listV = Valuelist<9,8,7>;
constexpr auto value = front_v<listV>; //value = 9
2.从列表中删除第一项
pop_front
template<typename List>
struct pop_front;
//
//
template<typename Head, typename... Tail>
struct pop_front<Typelist<Head, Tail...>> {
// ,
using type = Typelist<Tail...>;
};
template<auto Head, auto... Tail>
struct pop_front<Valuelist<Head, Tail...>> {
using type = Valuelist<Tail...>;
};
template<typename List>
using pop_front_t = typename pop_front<List>::type;
//
using listT = Typelist<char, bool, int>;
using typeT = pop_front_t<listT>; // type = Typelist<bool, int>
using listV = Valuelist<9,8,7>;
using typeV = pop_front_t<listV>; // type = Valuelist<8,7>
3.将一个项目添加到列表的开头
push_front
template<typename List, typename NewElement>
struct push_front;
template<typename... List, typename NewElement>
struct push_front<Typelist<List...>, NewElement> {
using type = Typelist<NewElement, List...>;
};
template<typename List, typename NewElement>
using push_front_t = typename push_front<List, NewElement>::type;
//
using listT = Typelist<char, bool, int>;
using typeT = push_front_t<listT, long >; // type = Typelist<long, char, bool, int>
4.在列表末尾添加一个非标准参数
push_back_value
template<typename List, auto NewElement>
struct push_back;
template<auto... List, auto NewElement>
struct push_back<Valuelist<List...>, NewElement>{
using type = Valuelist<List..., NewElement>;
};
template<typename List, auto NewElement>
using push_back_t = typename push_back<List, NewElement>::type;
//
using listV = Valuelist<9,8,7>;
using typeV = push_back_t<listV, 6>; // typeV = Valuelist<9,8,7,6>
5.检查清单是否为空
是空的
template<typename List>
struct is_empty{
static constexpr auto value = false;
};
// ,
template<>
struct is_empty<Typelist<>>{
static constexpr auto value = true;
};
template<typename List>
static constexpr auto is_empty_v = is_empty<List>::value;
//
using listT = Typelist<char, bool, int>;
constexpr auto value = is_empty_v<listT>; // value = false
6.查找列表中的项目数
size_of_list
// ,
// count, 2
template<typename List, std::size_t count = 0>
struct size_of_list : public size_of_list<pop_front_t<List>, count + 1>{};
//
template<std::size_t count>
struct size_of_list<Typelist<>, count>{
static constexpr std::size_t value = count;
};
//
template<std::size_t count>
struct size_of_list<Valuelist<>, count>{
static constexpr std::size_t value = count;
};
template<typename List>
static constexpr std::size_t size_of_list_v = size_of_list<List>::value;
//
using listT = Typelist<char, bool, int>;
constexpr auto value = size_of_list_v <listT>; // value = 3
现在已经定义了所有基本操作,接下来可以继续编写用于按位操作的元函数:或,和,xor,这是接口方法所必需的。
由于这些位转换是相同类型的,因此我们将尝试使实现尽可能通用,以避免代码重复。
对列表执行抽象操作的函数
list_operation
Lists – , , .
operation – , 2 Lists .
isEnd – , Lists.
(1) Lists 1 , (2).
– (3) (4) Lists, (6). (7) , (6), (5) Lists. (1).
template<template<typename first, typename second> class operation,
typename Lists, bool isEnd = size_of_list_v<Lists> == 1>
class lists_operation{
using first = front_t<Lists>; // (3)
using second = front_t<pop_front_t<Lists>>; // (4)
using next = pop_front_t<pop_front_t<Lists>>; // (5)
using result = operation<first, second>; // (6)
public:
using type = typename
lists_operation<operation, push_front_t<next, result>>::type; // (7)
};
template<template<typename first, typename second> class operation, typename List>
class lists_operation<operation, List, true>{ // (1)
public:
using type = front_t<List>; // (2)
};
Lists – , , .
operation – , 2 Lists .
isEnd – , Lists.
(1) Lists 1 , (2).
– (3) (4) Lists, (6). (7) , (6), (5) Lists. (1).
接下来,我们将实现前一个元函数的操作,该元函数将对来自两个列表的非典型参数执行逐项抽象操作:
valuelists_operation
List1 List2 – , .
operation – , .
Result – , .
(1), , Result.
(2) Result (3). (4) , .
template<template <auto value1, auto value2> typename operation,
typename List1, typename List2, typename Result = Valuelist<>>
struct operation_2_termwise_valuelists{
constexpr static auto newValue =
operation<front_v<List1>, front_v<List2>>::value; // (2)
using nextList1 = pop_front_t<List1>;
using nextList2 = pop_front_t<List2>;
using result = push_back_value_t<Result, newValue>; // (3)
using type = typename
operation_2_termwise_valuelists <operation, nextList1, nextList2, result>::type; // (4)
};
template<template <auto value1, auto value2> typename operation, typename Result>
struct operation_2_termwise_valuelists <operation, Valuelist<>, Valuelist<>, Result>{ // (1)
using type = Result;
};
List1 List2 – , .
operation – , .
Result – , .
(1), , Result.
(2) Result (3). (4) , .
位运算功能:
按位运算
template<auto value1, auto value2>
struct and_operation{ static constexpr auto value = value1 & value2;};
template<auto value1, auto value2>
struct or_operation{ static constexpr auto value = value1 | value2;};
template<auto value1, auto value2>
struct xor_operation{ static constexpr auto value = value1 ^ value2;};
仍然可以创建别名以便于使用:
别名
( ).
// 2
template<typename List1, typename List2>
using operation_and_termwise_t = typename
operation_2_termwise_valuelists<and_operation, List1, List2>::type;
template<typename List1, typename List2>
using operation_or_termwise_t = typename
operation_2_termwise_valuelists<or_operation, List1, List2>::type;
template<typename List1, typename List2>
using operation_xor_termwise_t = typename
operation_2_termwise_valuelists<xor_operation, List1, List2>::type;
//
template<typename... Lists>
using lists_termwise_and_t = typename
lists_operation<operation_and_termwise_t, Typelist<Lists...>>::type;
template<typename... Lists>
using lists_termwise_or_t= typename
lists_operation<operation_or_termwise_t, Typelist<Lists...>>::type;
template<typename... Lists>
using lists_termwise_xor_t = typename
lists_operation<operation_xor_termwise_t, Typelist<Lists...>>::type;
( ).
返回接口的实现
由于所使用的控制器和外围设备在编译阶段都是已知的,因此实现接口的逻辑选择是采用CRTP习惯用语的静态多态性。接口作为模板参数,接受特定控制器的适配器类,该适配器类又从该接口继承。
template<typename adapter>
struct IPower{
template<typename... Peripherals>
static void Enable(){
// , ‘power’
//
using tEnableList = lists_termwise_or_t<typename Peripherals::power...>;
// Valuelist<…>, 0,
//
using tDisableList = typename adapter::template fromValues<>::power;
// /
adapter:: template _Set<tEnableList , tDisableList>();
}
template<typename EnableList, typename ExceptList>
static void EnableExcept(){
using tXORedList = lists_termwise_xor_t <
typename EnableList::power, typename ExceptList::power>;
using tEnableList = lists_termwise_and_t <
typename EnableList::power, tXORedList>;
using tDisableList = typename adapter::template fromValues<>::power;
adapter:: template _Set<tEnableList , tDisableList>();
}
template<typename EnableList, typename DisableList>
static void Keep(){
using tXORedList = lists_termwise_xor_t <
typename EnableList::power, typename DisableList::power>;
using tEnableList = lists_termwise_and_t <
typename EnableList::power, tXORedList>;
using tDisableList = lists_termwise_and_t <
typename DisableList::power, tXORedList>;
adapter:: template _Set<tEnableList , tDisableList>();
}
template<typename... PeripheralsList>
struct fromPeripherals{
using power = lists_termwise_or_t<typename PeripheralsList::power...>;
};
};
而且,该接口包含一个内置的fromPeripherals类,该类使您可以将外围设备组合到一个列表中,然后可以在方法中使用它:
using listPower = Power::fromPeripherals<spi, uart>;
Power::Enable<listPower>();
禁用 方法的实现方式与此类似。
控制器适配器
在适配器类中,您需要设置时钟寄存器的地址并确定写入它们的顺序,然后将控制权直接传递给该类,该类将设置或清除指示寄存器的位。
struct Power: public IPower<Power>{
static constexpr uint32_t
_addressAHBENR = 0x40021014,
_addressAPB2ENR = 0x40021018,
_addressAPB1ENR = 0x4002101C;
using AddressesList = Valuelist<
_addressAHBENR, _addressAPB1ENR, _addressAPB2ENR>;
template<typename EnableList, typename DisableList>
static void _Set(){
// ,
HPower:: template ModifyRegisters<EnableList, DisableList, AddressesList>();
}
template<uint32_t valueAHBENR = 0, uint32_t valueAPB1ENR = 0, uint32_t valueAPB2ENR = 0>
struct fromValues{
using power = Valuelist<valueAHBENR, valueAPB1ENR, valueAPB2ENR>;
};
};
周边
我们使用适配器的fromValues结构为外围设备赋予power属性:
template<int identifier>
struct SPI{
// identifier
using power = Power::fromValues<
RCC_AHBENR_DMA1EN, // ,
RCC_APB1ENR_SPI2EN, //
RCC_APB2ENR_IOPBEN>::power;
};
template<int identifier>
struct UART{
using power = Power::fromValues<
RCC_AHBENR_DMA1EN,
0U,
RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN>::power;
};
写寄存器
该类由一个递归模板方法组成,该方法的任务是将值写入适配器传递的控制器寄存器中。
该方法接受3个非典型值列表<…>参数的列表作为参数:
- SetList和ResetList-要在寄存器中设置/重置的位值序列的列表
- AddressesList-寄存器地址的列表,先前参数的值将写入该地址
struct HPower{
template<typename SetList, typename ResetList, typename AddressesList>
static void ModifyRegisters(){
if constexpr (!is_empty_v<SetList> && !is_empty_v<ResetList> &&
!is_empty_v<AddressesList>){
//
constexpr auto valueSet = front_v<SetList>;
constexpr auto valueReset = front_v<ResetList>;
if constexpr(valueSet || valueReset){
constexpr auto address = front_v<AddressesList>;
using pRegister_t = volatile std::remove_const_t<decltype(address)>* const;
auto& reg = *reinterpret_cast<pRegister_t>(address);
// (!) ,
reg = (reg &(~valueReset)) | valueSet;
}
//
using tRestSet = pop_front_t<SetList>;
using tRestReset = pop_front_t<ResetList>;
using tRestAddress = pop_front_t<AddressesList>;
// ,
ModifyRegisters<tRestSet, tRestReset, tRestAddress>();
}
};
};
该类仅包含将在程序集列表中包含的代码行。
现在,该结构的所有块均已准备就绪,让我们继续进行测试。
测试代码
让我们回顾最后一个问题的条件:
- 启用SPI2和USART1
- 进入“省电模式”之前先关闭SPI2
- 退出“省电模式”后启用SPI2
//
using spi = SPI<2>;
using uart = UART<1>;
// ( )
using listPowerInit = Power::fromPeripherals<spi, uart>;
using listPowerDown = Power::fromPeripherals<spi>;
using listPowerWake = Power::fromPeripherals<uart>;
int main() {
// SPI2, UASRT1, DMA1, GPIOA, GPIOB
Power::Enable<listPowerInit>();
// Some code
// SPI2 GPIOB
Power::DisableExcept<listPowerDown, listPowerWake>();
//Sleep();
// SPI2 GPIOB
Power::EnableExcept<listPowerDown, listPowerWake>();
…
}
代码大小:68字节*,与直接写入寄存器的情况相同。
清单
main:
// AHBENR( DMA1)
ldr r3, .L3
ldr r2, [r3, #20]
orr r2, r2, #1
str r2, [r3, #20]
// APB1ENR( SPI2
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
// APB2ENR( GPIOA, GPIOB, USART1)
ldr r2, [r3, #24]
orr r2, r2, #16384
orr r2, r2, #12
str r2, [r3, #24]
// APB1ENR( SPI2)
ldr r2, [r3, #28]
bic r2, r2, #16384
str r2, [r3, #28]
// APB2ENR( GPIOB)
ldr r2, [r3, #24]
bic r2, r2, #8
str r2, [r3, #24]
// APB1ENR( SPI2
ldr r2, [r3, #28]
orr r2, r2, #16384
str r2, [r3, #28]
// APB2ENR( GPIOB)
ldr r2, [r3, #24]
orr r2, r2, #8
str r2, [r3, #24]
*使用GCC 9.2.1,比GCC 10.1.1多了8个字节。从清单中可以看到,添加了一些不必要的指令,例如,在读到地址(ldr)之前有一个add指令(adds),尽管这些指令可以替换为带偏移量的读取。新版本优化了这些操作。同时,clang生成相同的列表。
结果
已经达到了本文开头所设定的目标-执行速度和效率保持在直接写入寄存器的水平,并且将用户代码错误的可能性降到了最低。
也许源代码的数量和开发的复杂性似乎是多余的,但是,由于有如此众多的抽象,因此向新控制器的过渡将花费最少的精力:30行可理解的适配器代码+每外围单元5行。
完整的代码
type_traits_custom.hpp
#ifndef _TYPE_TRAITS_CUSTOM_HPP
#define _TYPE_TRAITS_CUSTOM_HPP
#include <type_traits>
/*!
@file
@brief Traits for metaprogramming
*/
/*!
@brief Namespace for utils.
*/
namespace utils{
/*-----------------------------------Basic----------------------------------------*/
/*!
@brief Basic list of types
@tparam Types parameter pack
*/
template<typename... Types>
struct Typelist{};
/*!
@brief Basic list of values
@tparam Values parameter pack
*/
template<auto... Values>
struct Valuelist{};
/*------------------------------End of Basic--------------------------------------*/
/*----------------------------------Front-------------------------------------------
Description: Pop front type or value from list
using listOfTypes = Typelist<int, short, bool, unsigned>;
using listOfValues = Valuelist<1,2,3,4,5,6,1>;
|-----------------|--------------------|----------|
| Trait | Parameters | Result |
|-----------------|--------------------|----------|
| front_t | <listOfTypes> | int |
|-----------------|--------------------|----------|
| front_v | <listOfValues> | 1 |
|-----------------|--------------------|----------| */
namespace{
template<typename List>
struct front;
template<typename Head, typename... Tail>
struct front<Typelist<Head, Tail...>>{
using type = Head;
};
template<auto Head, auto... Tail>
struct front<Valuelist<Head, Tail...>> {
static constexpr auto value = Head;
};
}
template<typename List>
using front_t = typename front<List>::type;
template<typename List>
static constexpr auto front_v = front<List>::value;
/*----------------------------------End of Front----------------------------------*/
/*----------------------------------Pop_Front---------------------------------------
Description: Pop front type or value from list and return rest of the list
using listOfTypes = Typelist<int, short, bool>;
using listOfValues = Valuelist<1,2,3,4,5,6,1>;
|-----------------|--------------------|------------------------|
| Trait | Parameters | Result |
|-----------------|--------------------|------------------------|
| pop_front_t | <listOfTypes> | Typelist<short, bool> |
|-----------------|--------------------|------------------------|
| pop_front_t | <listOfValues> | Valuelist<2,3,4,5,6,1> |
|-----------------|--------------------|------------------------| */
namespace{
template<typename List>
struct pop_front;
template<typename Head, typename... Tail>
struct pop_front<Typelist<Head, Tail...>> {
using type = Typelist<Tail...>;
};
template<auto Head, auto... Tail>
struct pop_front<Valuelist<Head, Tail...>> {
using type = Valuelist<Tail...>;
};
}
template<typename List>
using pop_front_t = typename pop_front<List>::type;
/*------------------------------End of Pop_Front----------------------------------*/
/*----------------------------------Push_Front--------------------------------------
Description: Push new element to front of the list
using listOfTypes = Typelist<short, bool>;
|-----------------------|--------------------------|-------------------------------|
| Trait | Parameters | Result |
|-----------------------|--------------------------|-------------------------------|
| push_front_t | <listOfTypes, float> | Typelist<float, short, bool> |
|-----------------------|--------------------------|-------------------------------| */
namespace{
template<typename List, typename NewElement>
struct push_front;
template<typename... List, typename NewElement>
struct push_front<Typelist<List...>, NewElement> {
using type = Typelist<NewElement, List...>;
};
}
template<typename List, typename NewElement>
using push_front_t = typename push_front<List, NewElement>::type;
/*------------------------------End of Push_Front---------------------------------*/
/*----------------------------------Push_Back---------------------------------------
Description: Push new value to back of the list
using listOfValues = Valuelist<1,2,3,4,5,6>;
|-----------------------|--------------------------|-------------------------------|
| Trait | Parameters | Result |
|-----------------------|--------------------------|-------------------------------|
| push_back_value_t | <listOfValues, 0> | Valuelist<1,2,3,4,5,6,0> |
|-----------------------|--------------------------|-------------------------------| */
namespace{
template<typename List, auto NewElement>
struct push_back_value;
template<auto... List, auto NewElement>
struct push_back_value<Valuelist<List...>, NewElement>{
using type = Valuelist<List..., NewElement>;
};
}
template<typename List, auto NewElement>
using push_back_value_t = typename push_back_value<List, NewElement>::type;
/*----------------------------------End of Push_Back------------------------------*/
/*-----------------------------------Is_Empty---------------------------------------
Description: Check parameters list for empty and return bool value
using listOfTypes = Typelist<int, short, bool, unsigned>;
using listOfValues = Valuelist<>;
|-------------------------|--------------------|----------|
| Trait | Parameters | Result |
|-------------------------|--------------------|----------|
| is_empty_v | <listOfTypes> | false |
|-------------------------|--------------------|----------|
| is_empty_v | <listOfValues> | true |
|-------------------------|--------------------|----------| */
namespace{
/*!
@brief Check the emptiness of the types in parameters. \n
E.g.: is_empty<int, short, bool>::value;
*/
template<typename List>
struct is_empty{
static constexpr auto value = false;
};
/*!
@brief Check the emptiness of the types in parameter. Specializatio for empty parameters \n
E.g.: is_empty<>::value;
*/
template<>
struct is_empty<Typelist<>>{
static constexpr auto value = true;
};
template<>
struct is_empty<Valuelist<>>{
static constexpr auto value = true;
};
}
/*!
@brief Check the emptiness of the types-list in parameter. \n
E.g.: using list = Typelist<int, short, bool>; is_empty_v<list>;
*/
template<typename List>
static constexpr auto is_empty_v = is_empty<List>::value;
/*--------------------------------End of Is_Empty---------------------------------*/
/*---------------------------------Size_Of_List-------------------------------------
Description: Return number of elements in list
using listOfTypes = Typelist<int, float, double, bool>;
|------------------|--------------------|----------|
| Trait | Parameters | Result |
|------------------|--------------------|----------|
| size_of_list_v | listOfTypes | 4 |
|------------------|--------------------|----------| */
namespace{
template<typename List, std::size_t count = 0U>
struct size_of_list : public size_of_list<pop_front_t<List>, count + 1>{};
template<std::size_t count>
struct size_of_list<Typelist<>, count>{
static constexpr std::size_t value = count;
};
template<std::size_t count>
struct size_of_list<Valuelist<>, count>{
static constexpr std::size_t value = count;
};
}
template<typename List>
static constexpr std::size_t size_of_list_v = size_of_list<List>::value;
/*-------------------------------End Size_Of_List---------------------------------*/
/*---------------------------------Lists Operation--------------------------------*/
/*Description: Operations with lists of values
using list1 = Valuelist<1, 4, 8, 16>;
using list2 = Valuelist<1, 5, 96, 17>;
|------------------------------|-------------------|---------------------------|
| Trait | Parameters | Result |
|------------------------------|-------------------|---------------------------|
| lists_termwise_and_t | <list1, list2> | Valuelist<1, 4, 0, 16> |
|------------------------------|-------------------|---------------------------|
| lists_termwise_or_t | <list1, list2> | Valuelist<1, 5, 104, 17> |
|---------------------------- -|-------------------|---------------------------|
| lists_termwise_xor_t | <list1, list2> | Valuelist<0, 1, 104, 1> |
|------------------------------|-------------------|---------------------------| */
namespace{
template<template <auto value1, auto value2> typename operation,
typename List1, typename List2, typename Result = Valuelist<>>
struct operation_2_termwise_valuelists{
constexpr static auto newValue = operation<front_v<List1>, front_v<List2>>::value;
using nextList1 = pop_front_t<List1>;
using nextList2 = pop_front_t<List2>;
using result = push_back_value_t<Result, newValue>;
using type = typename
operation_2_termwise_valuelists<operation, nextList1, nextList2, result>::type;
};
template<template <auto value1, auto value2> typename operation, typename Result>
struct operation_2_termwise_valuelists<operation, Valuelist<>, Valuelist<>, Result>{
using type = Result;
};
template<template <auto value1, auto value2> typename operation,
typename List2, typename Result>
struct operation_2_termwise_valuelists<operation, Valuelist<>, List2, Result>{
using type = typename
operation_2_termwise_valuelists<operation, Valuelist<0>, List2, Result>::type;
};
template<template <auto value1, auto value2> typename operation,
typename List1, typename Result>
struct operation_2_termwise_valuelists<operation, List1, Valuelist<>, Result>{
using type = typename
operation_2_termwise_valuelists<operation, List1, Valuelist<0>, Result>::type;
};
template<template<typename first, typename second> class operation,
typename Lists, bool isEnd = size_of_list_v<Lists> == 1>
class lists_operation{
using first = front_t<Lists>;
using second = front_t<pop_front_t<Lists>>;
using next = pop_front_t<pop_front_t<Lists>>;
using result = operation<first, second>;
public:
using type = typename lists_operation<operation, push_front_t<next, result>>::type;
};
template<template<typename first, typename second> class operation,
typename Lists>
class lists_operation<operation, Lists, true>{
public:
using type = front_t<Lists>;
};
template<auto value1, auto value2>
struct and_operation{ static constexpr auto value = value1 & value2;};
template<auto value1, auto value2>
struct or_operation{ static constexpr auto value = value1 | value2;};
template<auto value1, auto value2>
struct xor_operation{ static constexpr auto value = value1 ^ value2;};
template<typename List1, typename List2>
using operation_and_termwise_t = typename
operation_2_termwise_valuelists<and_operation, List1, List2>::type;
template<typename List1, typename List2>
using operation_or_termwise_t = typename
operation_2_termwise_valuelists<or_operation, List1, List2>::type;
template<typename List1, typename List2>
using operation_xor_termwise_t = typename
operation_2_termwise_valuelists<xor_operation, List1, List2>::type;
}
template<typename... Lists>
using lists_termwise_and_t =
typename lists_operation<operation_and_termwise_t, Typelist<Lists...>>::type;
template<typename... Lists>
using lists_termwise_or_t = typename
lists_operation<operation_or_termwise_t, Typelist<Lists...>>::type;
template<typename... Lists>
using lists_termwise_xor_t = typename
lists_operation<operation_xor_termwise_t, Typelist<Lists...>>::type;
/*--------------------------------End of Lists Operation----------------------------*/
} // !namespace utils
#endif //!_TYPE_TRAITS_CUSTOM_HPP
IPower.hpp
#ifndef _IPOWER_HPP
#define _IPOWER_HPP
#include "type_traits_custom.hpp"
#define __FORCE_INLINE __attribute__((always_inline)) inline
/*!
@brief Controller's peripherals interfaces
*/
namespace controller::interfaces{
/*!
@brief Interface for Power(Clock control). Static class. CRT pattern
@tparam <adapter> class of specific controller
*/
template<typename adapter>
class IPower{
IPower() = delete;
public:
/*!
@brief Enables peripherals Power(Clock)
@tparam <Peripherals> list of peripherals with trait 'power'
*/
template<typename... Peripherals>
__FORCE_INLINE static void Enable(){
using tEnableList = utils::lists_termwise_or_t<typename Peripherals::power...>;
using tDisableList = typename adapter::template fromValues<>::power;
adapter:: template _Set<tEnableList, tDisableList>();
}
/*!
@brief Enables Power(Clock) except listed peripherals in 'ExceptList'.
If Enable = Exception = 1, then Enable = 0, otherwise depends on Enable.
@tparam <EnableList> list to enable, with trait 'power'
@tparam <ExceptList> list of exception, with trait 'power'
*/
template<typename EnableList, typename ExceptList>
__FORCE_INLINE static void EnableExcept(){
using tXORedList = utils::lists_termwise_xor_t<typename EnableList::power, typename ExceptList::power>;
using tEnableList = utils::lists_termwise_and_t<typename EnableList::power, tXORedList>;
using tDisableList = typename adapter::template fromValues<>::power;
adapter:: template _Set<tEnableList, tDisableList>();
}
/*!
@brief Disables peripherals Power(Clock)
@tparam <Peripherals> list of peripherals with trait 'power'
*/
template<typename... Peripherals>
__FORCE_INLINE static void Disable(){
using tDisableList = utils::lists_termwise_or_t<typename Peripherals::power...>;
using tEnableList = typename adapter::template fromValues<>::power;
adapter:: template _Set<tEnableList, tDisableList>();
}
/*!
@brief Disables Power(Clock) except listed peripherals in 'ExceptList'.
If Disable = Exception = 1, then Disable = 0, otherwise depends on Disable.
@tparam <DisableList> list to disable, with trait 'power'
@tparam <ExceptList> list of exception, with trait 'power'
*/
template<typename DisableList, typename ExceptList>
__FORCE_INLINE static void DisableExcept(){
using tXORedList = utils::lists_termwise_xor_t<typename DisableList::power, typename ExceptList::power>;
using tDisableList = utils::lists_termwise_and_t<typename DisableList::power, tXORedList>;
using tEnableList = typename adapter::template fromValues<>::power;
adapter:: template _Set<tEnableList, tDisableList>();
}
/*!
@brief Disable and Enables Power(Clock) depends on values.
If Enable = Disable = 1, then Enable = Disable = 0, otherwise depends on values
@tparam <EnableList> list to enable, with trait 'power'
@tparam <DisableList> list to disable, with trait 'power'
*/
template<typename EnableList, typename DisableList>
__FORCE_INLINE static void Keep(){
using tXORedList = utils::lists_termwise_xor_t<typename EnableList::power, typename DisableList::power>;
using tEnableList = utils::lists_termwise_and_t<typename EnableList::power, tXORedList>;
using tDisableList = utils::lists_termwise_and_t<typename DisableList::power, tXORedList>;
adapter:: template _Set<tEnableList, tDisableList>();
}
/*!
@brief Creates custom 'power' list from peripherals. Peripheral driver should implement 'power' trait.
E.g.: using power = Power::makeFromValues<1, 512, 8>::power;
@tparam <PeripheralsList> list of peripherals with trait 'power'
*/
template<typename... PeripheralsList>
class fromPeripherals{
fromPeripherals() = delete;
using power = utils::lists_termwise_or_t<typename PeripheralsList::power...>;
friend class IPower<adapter>;
};
};
} // !namespace controller::interfaces
#undef __FORCE_INLINE
#endif // !_IPOWER_HPP
HPower.hpp
#ifndef _HPOWER_HPP
#define _HPOWER_HPP
#include "type_traits_custom.hpp"
#define __FORCE_INLINE __attribute__((always_inline)) inline
/*!
@brief Hardware operations
*/
namespace controller::hardware{
/*!
@brief Implements hardware operations with Power(Clock) registers
*/
class HPower{
HPower() = delete;
protected:
/*!
@brief Set or Reset bits in the registers
@tparam <SetList> list of values to set
@tparam <ResetList> list of values to reset
@tparam <AddressesList> list of registers addresses to operate
*/
template<typename SetList, typename ResetList, typename AddressesList>
__FORCE_INLINE static void ModifyRegisters(){
using namespace utils;
if constexpr (!is_empty_v<SetList> && !is_empty_v<ResetList> &&
!is_empty_v<AddressesList>){
constexpr auto valueSet = front_v<SetList>;
constexpr auto valueReset = front_v<ResetList>;
if constexpr(valueSet || valueReset){
constexpr auto address = front_v<AddressesList>;
using pRegister_t = volatile std::remove_const_t<decltype(address)>* const;
auto& reg = *reinterpret_cast<pRegister_t>(address);
reg = (reg &(~valueReset)) | valueSet;
}
using tRestSet = pop_front_t<SetList>;
using tRestReset = pop_front_t<ResetList>;
using tRestAddress = pop_front_t<AddressesList>;
ModifyRegisters<tRestSet, tRestReset, tRestAddress>();
}
};
};
} // !namespace controller::hardware
#undef __FORCE_INLINE
#endif // !_HPOWER_HPP
stm32f1_Power.hpp
#ifndef _STM32F1_POWER_HPP
#define _STM32F1_POWER_HPP
#include <cstdint>
#include "IPower.hpp"
#include "HPower.hpp"
#include "type_traits_custom.hpp"
#define __FORCE_INLINE __attribute__((always_inline)) inline
/*!
@brief Controller's peripherals
*/
namespace controller{
/*!
@brief Power managment for controller
*/
class Power: public interfaces::IPower<Power>, public hardware::HPower{
Power() = delete;
public:
/*!
@brief Creates custom 'power' list from values. Peripheral driver should implement 'power' trait.
E.g.: using power = Power::fromValues<1, 512, 8>::power;
@tparam <valueAHB=0> value for AHBENR register
@tparam <valueAPB1=0> value for APB1ENR register
@tparam <valueAPB2=0> value for APB1ENR register
*/
template<uint32_t valueAHBENR = 0, uint32_t valueAPB1ENR = 0, uint32_t valueAPB2ENR = 0>
struct fromValues{
fromValues() = delete;
using power = utils::Valuelist<valueAHBENR, valueAPB1ENR, valueAPB2ENR>;
};
private:
static constexpr uint32_t
_addressAHBENR = 0x40021014,
_addressAPB2ENR = 0x40021018,
_addressAPB1ENR = 0x4002101C;
using AddressesList = utils::Valuelist<_addressAHBENR, _addressAPB1ENR, _addressAPB2ENR>;
template<typename EnableList, typename DisableList>
__FORCE_INLINE static void _Set(){
HPower:: template ModifyRegisters<EnableList, DisableList, AddressesList>();
}
friend class IPower<Power>;
};
} // !namespace controller
#undef __FORCE_INLINE
#endif // !_STM32F1_POWER_HPP
stm32f1_SPI.hpp
#ifndef _STM32F1_SPI_HPP
#define _STM32F1_SPI_HPP
#include "stm32f1_Power.hpp"
namespace controller{
template<auto baseAddress>
class SPI{
static const uint32_t RCC_AHBENR_DMA1EN = 1;
static const uint32_t RCC_APB2ENR_IOPBEN = 8;
static const uint32_t RCC_APB1ENR_SPI2EN = 0x4000;
/*!
@brief Trait for using in Power class. Consists of Valueslist with
values for AHBENR, APB1ENR, APB2ENR registers
*/
using power = Power::fromValues<
RCC_AHBENR_DMA1EN,
RCC_APB1ENR_SPI2EN,
RCC_APB2ENR_IOPBEN>::power;
template<typename>
friend class interfaces::IPower;
};
}
#endif // !_STM32F1_SPI_HPP
stm32f1_UART.hpp
#ifndef _STM32F1_UART_HPP
#define _STM32F1_UART_HPP
#include "stm32f1_Power.hpp"
namespace controller{
template<auto baseAddress>
class UART{
static const uint32_t RCC_AHBENR_DMA1EN = 1;
static const uint32_t RCC_APB2ENR_IOPAEN = 4;
static const uint32_t RCC_APB2ENR_USART1EN = 0x4000;
/*!
@brief Trait for using in Power class. Consists of Valueslist with
values for AHBENR, APB1ENR, APB2ENR registers
*/
using power = Power::fromValues<
RCC_AHBENR_DMA1EN,
0U,
RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN>::power;
template<typename>
friend class interfaces::IPower;
};
}
#endif // !_STM32F1_UART_HPP
main.cpp
#include "stm32f1_Power.hpp"
#include "stm32f1_UART.hpp"
#include "stm32f1_SPI.hpp"
using namespace controller;
using spi = SPI<2>;
using uart = UART<1>;
using listPowerInit = Power::fromPeripherals<spi, uart>;
using listPowerDown = Power::fromPeripherals<spi>;
using listPowerWake = Power::fromPeripherals<uart>;
int main(){
Power::Enable<listPowerInit>();
//Some code
Power::DisableExcept<listPowerDown, listPowerWake>();
//Sleep();
Power::EnableExcept<listPowerDown, listPowerWake>();
while(1);
return 1;
};
Github