1、问题引入和结论
在CortexM0单片机中,在使用指针进行内存访问的时候需要特别留意访问地址的合法性问题,否则稍有不慎就会进入HardFault,痛苦不堪。
这里先将结论抛出来:
1、u8类型的指针,读写任何地址均是合法的,不会进入HardFault
2、u16类型的指针,读写地址必须是2的倍数,否则会进入HardFault
3、u32类型的指针,读写地址必须是4的倍数,否则会进入HardFault
我们针对以上三个结论分别做测试。
PS:本试验涉及到结构体的字节对齐知识点,如果有疑惑的地方可以查看:结构体的字节对齐规则
2、测试平台
CortexM0 32位单片机
3、u8指针读写内存测试
首先是u8类型指针的测试,以下是测试代码,实际测试时,可以发现代码可以顺利跑下来,而不会进入HardFault。
#pragma pack(4) //指定对齐是4字节
typedef struct
{
u8 len; //自身对齐是1,指定对齐是4,因此有效对齐是1, 0地址存放len
u8 data[12]; //自身对齐是1,指定对齐是4,因此有效对齐是1, 1地址存放data[0],2地址存放data[1]...
}TEST;
#pragma pack()
TEST test;
u8 *p8;
u16 *p16;
u32 *p32;
volatile u32 temp;
void align_test_p8(void)
{
p8 = (u8 *)&test.data[0]; //根据结构体字节对齐的规则,test.data[0]的地址肯定是奇数,因为编译器分配的test内存的时候,test的起始地址肯定是4字节对齐的,而len占用1字节,因此data[0]的地址肯定是奇数
temp = p8[0];//访问非对齐地址
temp = p8[1];//访问2字节对齐的地址
temp = p8[2];//访问非对齐地址
temp = p8[3];//访问4字节对齐地址
}
4、u16指针读写内存测试
接下来是u16类型指针的测试,以下是测试代码。
void align_test_p16(void)
{
p16 = (u16 *)&test.data[0]; //根据结构体字节对齐的规则,test.data[0]的地址肯定是奇数,因为编译器分配的test内存的时候,test的起始地址肯定是4字节对齐的,而len占用1字节,因此data[0]的地址肯定是奇数
temp = p16[0];//访问非2字节对齐的地址,肯定会进入HardFault
p16 = (u16 *)&test.data[1]; //根据结构体字节对齐的规则,test.data[1]的地址肯定是2的倍数
temp = p16[0];//访问2字节对齐地址,不会进入HardFault
p16 = (u16 *)&test.data[2]; //根据结构体字节对齐的规则,test.data[2]的地址肯定不是2的倍数
temp = p16[0];//访问非2字节对齐地址,会进入HardFault
p16 = (u16 *)&test.data[3]; //根据结构体字节对齐的规则,test.data[3]的地址肯定是2的倍数
temp = p16[0];//访问2字节对齐地址,不会进入HardFault
}
会进入HardFault的情况
不会进入HardFault的情况
5、u32指针读写内存测试
最后是u32类型指针的测试,以下是测试代码,实际调试的时候,程序在跑到断点处后,就会进入HardFault。
void align_test_p32(void)
{
p32 = (u32 *)&test.data[0]; //根据结构体字节对齐的规则,test.data[0]的地址肯定是奇数,因为编译器分配的test内存的时候,test的起始地址肯定是4字节对齐的,而len占用1字节,因此data[0]的地址肯定是奇数
temp = p32[0];//访问非4字节对齐的地址,进入HardFault
p32 = (u32 *)&test.data[1]; //根据结构体字节对齐的规则,test.data[1]的地址肯定是2的倍数,但是不是4的倍数
temp = p32[0];//访问2字节对齐地址,但不是4字节对齐地址,会进入HardFault
p32 = (u32 *)&test.data[2]; //根据结构体字节对齐的规则,test.data[2]的地址肯定不是2的倍数
temp = p32[0];//访问非4字节对齐地址,进入HardFault
p32 = (u32 *)&test.data[3]; //根据结构体字节对齐的规则,test.data[3]的地址肯定是2的倍数,是4的倍数
temp = p32[0];//访问4字节对齐地址,不进入HardFault
}
会进入HardFault的情况
不会进入HardFault的情况
6、结论
在CortexM0单片机中,在使用指针进行内存访问的时候需要特别留意访问地址的合法性问题,否则稍有不慎就会进入HardFault。
1、u8类型的指针,读写任何地址均是合法的,不会进入HardFault
2、u16类型的指针,读写地址必须是2的倍数,否则会进入HardFault
3、u32类型的指针,读写地址必须是4的倍数,否则会进入HardFault