历史上的今天
今天是:2024年12月29日(星期日)
2021年12月29日 | STM32F103单片机读取芯片自身ID
2021-12-29 来源:eefocus
每一个STM32单片机出厂的时候都有全球唯一的ID,当在数据安全性比较高的地方,需要对每一个接入系统的芯片进行身份验证,那么这个芯片自身的ID号就可以作为它的身份信息。
产品唯一的身份标识非常适合:
用来作为序列号(例如USB字符序列号或者其他的终端应用)
用来作为密码,在编写闪存时,将此唯一标识与软件加解密算法结合使用,提高代码在闪存存储器内的安全性。
用来激活带安全机制的自举过程
96位的产品唯一身份标识所提供的参考号码对任意一个STM32微控制器,在任何情况下都是唯一的。用户在何种情况下,都不能修改这个身份标识。
这个96位的产品唯一身份标识,按照用户不同的用法,可以以字节(8位)为单位读取,也可以以半字(16位)或者全字(32位)读取。




不仅可以读取到芯片的ID,还能读出芯片的存储器容量。

芯片型号也同样可以读出来。


下面就通过代码来读取这几个数据。
//读取芯片ID
void GetChipID ( void )
{
u32 CpuID[3]; //小端模式
CpuID[0] = * ( vu32 * ) ( 0x1ffff7e8 ); //高32位地址
CpuID[1] = * ( vu32 * ) ( 0x1ffff7ec ); //中32位地址
CpuID[2] = * ( vu32 * ) ( 0x1ffff7f0 ); //低32位地址
printf ( "此芯片唯一ID为:0x%x-%x-%x rn", CpuID[0], CpuID[1], CpuID[2] );
}
//获取芯片Flash大小
void GetFlashSize ( void )
{
u16 stm32_Flash_Size;
stm32_Flash_Size = * ( u16 * ) ( 0x1FFFF7E0 ); //闪存容量寄存器
printf ( "芯片闪存容量大小为:%x rn", stm32_Flash_Size );
}
//读取芯片型号
void Main_Chip_Type(void)
{
u32 type;
type = *( unsigned int * )( 0xE0042000 ); //MCU ID
printf ( "芯片型号为:0x%x rn", type );
}
int main ( void )
{
NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 );
uart_init ( 115200 );
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED连接的硬件接口
GetChipID();
GetFlashSize();
Main_Chip_Type();
while(1);
}
通过串口打印出芯片的ID
此芯片唯一ID为: 0x66eff51-51578248-87074312
芯片闪存容量大小为: 40
芯片型号为: 0x20036410

然后通过单步调试,直接观察内存中的数据。

然后观察内存中地址0x1FFFF7E0开始的数据

可以看出0x1FFFF7E0开始的16位数据为0x0040换算为10进制数据就是64,说明当前用的芯片的容量是64K。接下来从0x1ffff7e8地址开始的12个字节,也就是96位存储的就是芯片的ID。内存中ID号存储为低位在前高位在后,而打印出来的数据是高位在前,低位在后。打印的前32位是 06 6e ff 51对应在内存中的存储顺序51应该是第一位,06是最后一位。
最后在0xE0042000地址查看单片机的型号。

低12位数据是 0x410,也就是设备编码为0x410,通过对比文档可以看出这个是中容量产品。高16位是0x2003,对比文档可以看出,这个是中容量产品,版本号为Y。
由于这些ID号都存储在指定的内存地址中,而系统没有提供专用的读取这些ID号的函数,所以读取数据的时候,只能通过指针的方式去读取,首先将数据加一级指针转换为地址,然后加二级指针,表示读取当前地址中存储的内容。这种操作方法在库函数的头文件中使用的比较多。比如在stm32f10x.h中就可以看到对GPIOA的定义。
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
将地址值逐个替换后为 #define GPIOA ((GPIO_TypeDef *) 0x4001 0800),相当于在 0x4001 0800 地址处定义了GPIOA的结构体。查阅芯片资料可知,GPIOA的起始地址就是0x4001 0800。

这样操作GPIOA的的时候就和上面读取ID的方法是一样的,相当于直接读写对应地址中的值。
史海拾趣
|
现有固定座标100个(路径是一个大椭圆形,公交車的行駛路線) 和 现在座标1个(移动的) 如何得知目前的座标是往哪个固定座标移动中? 这问题看似简单,可是很复杂阿! 找出现在座标离最近的一站是可以找的出来,没啥, 就是比对100个固定座标和现在 ...… 查看全部问答> |
|
为什么GPIO中有的Pin要设为Pull_Up/Pull_Down? 如题,其中有的pin为输入pin,有的pin为输出pin,但是为什么要设置一些pin的属性为pull up或者为pull down,pull up/pull down到底是干吗用的?根据什么来设的呢,聆听各位大虾的教诲!!… 查看全部问答> |
|
INT8U OSQPost (OS_EVENT *pevent, void *msg) 1、这个函数是发送消息的函数,如果每次都发送一个消息过来,这个消息是指针,那么我认为这个消息一定是一个占一定内存空间的结构。 2、而清空消息队列的函数只是将消息队列的信息给置成空了,但是 ...… 查看全部问答> |
|
看了DIY板块,发现自己对一些小制作十分有兴趣,高手可以推荐自己动手做一些东西方便入门嘛~~在学校学习了数模电,但是由于课程安排,实验在下个学期上,我想自己在寒假的时候也能进行些小实验。。 手头上有个avr的学习版, 不知道能不 ...… 查看全部问答> |
|
今天早上九点多查看六级成绩,过了!呵呵····· 今天晚上在外边吃饭,刚吃完手机响了,原来是在论坛申请的“eZ430-RF2500开发套件”到了! 今天我们学校的一卡通正式开通了; 今天重庆 ...… 查看全部问答> |
|
我在用CAN发送报文的时候,模式设置和初始化部分 我能确定都没有问题 但是报文发送时始终出现错误 我读取寄存器时,发现发送错误位检测为1. 问各位一个问题报文发送期间会发生总线错误有哪些原因呢 我用的是MSP430操作MC ...… 查看全部问答> |




