一、SD卡协议原理
1、SD卡简介
SD存储卡是一种基于半导体快闪记忆器的新一代记忆设备,由于它体积小、数据传输速度快、可热插拔等优良的特性,被广泛地于便携式装置上使用,例如数码相机、平板电脑和多媒体播放器等。
SD存储卡(Secure Digital Memory Card)是一种基于半导体快闪存储器的新一代高速存储设备。SD存储卡的技术是从MMC卡(MultiMedia Card格式上发展而来,在兼容SD存储卡基础上发展了SDIO(SD Input/ Output)卡,此兼容性包括机械,电子,电力,信号和软件,通常将SD、SDIO卡俗称SD存储卡。 [2]
SD卡具有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性,它被广泛地应用于便携式装置上,例如数码相机、平板电脑和多媒体播放器等。 [2]
SD卡的结构能保证数字文件传送的安全性,也很容易重新格式化,所以有着广泛的应用领域。音乐、电影等多媒体文件都可以方便地保存到SD卡中。目前市场上SD卡的品牌很多诸如:SANDISK、Kingmax、Panasonic和Kingston。 [2]
SD卡作为一种新型的存储设备,具有以下特点: [2]
●高存储容量,最常用的容量:8GB、16GB、32GB、128GB、256GB等。
●内置加密技术,适应基于SDMI协议的著作版权保护功能。
●高速数据传送;最大读写速率为100MB/s。
●体积轻小,便于携带,具有很强的抗冲击能力。
2、原理内部结构
?
3、SD卡操作模式
SD卡一般都支持 SDIO 和 SPI 这两种接口。 其中SD卡模式的信号线有:CLK、CMD、DAT0-DAT3,6根线。 SPI模式的信号线有:CS、CLK、MISO(DATAOUT)、MOSI(DATAIN),4根线。 SD卡的命令格式:命令CMD0就是0,CMD16就是16,以此类推。 SD卡的命令总共有12类,下表为几个比较重要的命令: 命令?? ?参数?? ?回应?? ?描述 CMD0(0X00)?? ?NONE?? ?R1?? ?复位SD卡 CMD8(0X08)?? ?VHS+Check Pattern?? ?R7?? ?发送接口状态命令 CMD9(0X09)?? ?NONE?? ?R1?? ?读取卡特定数据寄存器 CMD10(0X0A)?? ?NONE?? ?R1?? ?读取卡标志数据寄存器 CMD16(0X10)?? ?块大小?? ?R1?? ?设置块大小(字节数) CMD17(0X11)?? ?地址?? ?R1?? ?读取一个块的数据 CMD24(0X18)?? ?地址?? ?R1?? ?写入一个块的数据 CMD41(0X29)?? ?NONE?? ?R3?? ?发送给主机容量支持信息和激活卡初始化过程 CMD55(0X37)?? ?NONE?? ?R1?? ?告诉SD卡,下一个是特定应用命令 CMD58(0X3A)?? ?NONE?? ?R3?? ?读取OCR寄存器 ?
二、实验操作
1、实验器材:SD模块及SD卡,STM32f103c8t6,杜邦线若干,电源线一根
2、硬件连接:
stm32 | SD卡模块 | PA4 | SDCS | PA5 | SCK | PA7 | MOSI | PA6 | MISO | VCC | VCC | GND | GND |
3、HAL库配置(stm32CubeMX)
?设置RCC,SYS,在以往博客都有,不在此陈述。
?4、完整代码分析
完整代码链接:https://pan.baidu.com/s/1V-u1Mt1gUFCc70Jw8v6PtA 提取码:1234
主函数:
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration---------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_FATFS_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1); //enable uart
printf(" main \r\n");
Get_SDCard_Capacity(); //得到使用内存并选择格式化
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
WritetoSD(WriteBuffer,sizeof(WriteBuffer));
HAL_Delay(500);
WriteBuffer[0] = WriteBuffer[0] +10;
WriteBuffer[1] = WriteBuffer[1] +10;
write_cnt ++;
while(write_cnt > 10)
{
printf(" while \r\n");
HAL_Delay(500);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
?写入函数
void WritetoSD(BYTE write_buff[],uint8_t bufSize)
{
FATFS fs;
FIL file;
uint8_t res=0;
UINT Bw;
res = SD_init(); //SD卡初始化
if(res == 1)
{
printf("SD卡初始化失败! \r\n");
}
else
{
printf("SD卡初始化成功! \r\n");
}
res=f_mount(&fs,"0:",1); //挂载
// if(test_sd == 0) //用于测试格式化
if(res == FR_NO_FILESYSTEM) //没有文件系统,格式化
{
// test_sd =1; //用于测试格式化
printf("没有文件系统! \r\n");
res = f_mkfs("", 0, 0); //格式化sd卡
if(res == FR_OK)
{
printf("格式化成功! \r\n");
res = f_mount(NULL,"0:",1); //格式化后先取消挂载
res = f_mount(&fs,"0:",1); //重新挂载
if(res == FR_OK)
{
printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
}
}
else
{
printf("格式化失败! \r\n");
}
}
else if(res == FR_OK)
{
printf("挂载成功! \r\n");
}
else
{
printf("挂载失败! \r\n");
}
res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);
if((res & FR_DENIED) == FR_DENIED)
{
printf("卡存储已满,写入失败!\r\n");
}
f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据
if(res == FR_OK)
{
printf("打开成功/创建文件成功! \r\n");
res = f_write(&file,write_buff,bufSize,&Bw); //写数据到SD卡
if(res == FR_OK)
{
printf("文件写入成功! \r\n");
}
else
{
printf("文件写入失败! \r\n");
}
}
else
{
printf("打开文件失败!\r\n");
}
f_close(&file); //关闭文件
f_mount(NULL,"0:",1); //取消挂载
}
?实验注意事项:一定要接5V电源,杜邦线最好短一点的。
初始化成功(其中很多辛酸泪)
?读出数据
?
?在这里更改写入内容
?
?
?
三、Ubuntu下堆、栈、全局、局部等变量的分配地址
主要代码:
#include <stdio.h>
#include <stdlib.h>
//定义全局变量
int init_global_a = 1;
int uninit_global_a;
static int inits_global_b = 2;
static int uninits_global_b;
void output(int a)
{
printf("hello");
printf("%d",a);
printf("\n");
}
int main( )
{
//定义局部变量
int a=2;
static int inits_local_c=2, uninits_local_c;
int init_local_d = 1;
output(a);
char *p;
char str[10] = "lmy";
//定义常量字符串
char *var1 = "1234567890";
char *var2 = "qwertyuiop";
//动态分配
int *p1=malloc(4);
int *p2=malloc(4);
//释放
free(p1);
free(p2);
printf("栈区-变量地址\n");
printf(" a:%p\n", &a);
printf(" init_local_d:%p\n", &init_local_d);
printf(" p:%p\n", &p);
printf(" str:%p\n", str);
printf("\n堆区-动态申请地址\n");
printf(" %p\n", p1);
printf(" %p\n", p2);
printf("\n全局区-全局变量和静态变量\n");
printf("\n.bss段\n");
printf("全局外部无初值 uninit_global_a:%p\n", &uninit_global_a);
printf("静态外部无初值 uninits_global_b:%p\n", &uninits_global_b);
printf("静态内部无初值 uninits_local_c:%p\n", &uninits_local_c);
printf("\n.data段\n");
printf("全局外部有初值 init_global_a:%p\n", &init_global_a);
printf("静态外部有初值 inits_global_b:%p\n", &inits_global_b);
printf("静态内部有初值 inits_local_c:%p\n", &inits_local_c);
printf("\n文字常量区\n");
printf("文字常量地址 :%p\n",var1);
printf("文字常量地址 :%p\n",var2);
printf("\n代码区\n");
printf("程序区地址 :%p\n",&main);
printf("函数地址 :%p\n",&output);
return 0;
}
输入一下命令:并且在创建的文件下
sudo nano text.c
gcc text.c -o text
./text
?
?可以发现,Ubuntu在栈区和堆区的地址值都是从上到下增长的。
四、stm32下的C程序中堆、栈、全局、局部等变量的分配地址
1、内存分配
分区?? ?特点 栈区(stack)?? ?由编译器自动分配释放,存放函数的参数值,局部变量的值等。 堆区(heap)?? ?一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收 。 全局区(静态区)(static)?? ?全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量、未初始化的静态变量在相邻的另一块区域。当程序结束后,变量由系统释放 。 文字常量区?? ?存放常量字符串。当程序结束后,常量字符串由系统释放 。 程序代码区?? ?存放函数体的二进制代码。 存储顺序(由上到下) 栈区 堆区 全局区 常量区 代码区 2、全局变量与局部变量
全局变量: 在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件。 局部变量: 定义在函数体内部的变量,作用域仅限于函数体内部。离开函数体就会无效,再调用就是出错。 区别如下: 全局变量?? ?局部变量 定义在方法外?? ?定义在方法内 整个类都能用?? ?只能在方法中使用 不赋值也会有默认值?? ?不赋值就没有默认值 位于堆内存?? ?位于栈内存 ?
直接用以前的串口通信改写
main.c
#include "usart.h"
#include <stdio.h>
#include <stdlib.h>
int init_global_a = 1;
int uninit_global_a;
static int inits_global_b = 2;
static int uninits_global_b;
void output(int a)
{
printf("hello");
printf("%d",a);
printf("\n");
}
int main(void)
{
uart_init(115200);
while(1)
{
//定义局部变量
int a=2;
static int inits_local_c=2, uninits_local_c;
int init_local_d = 1;
char *p;
char str[10] = "zls";
//定义常量字符串
char *var1 = "1234567890";
char *var2 = "qwertyuiop";
//动态分配
int *p1=malloc(4);
int *p2=malloc(4);
output(a);
//释放
free(p1);
free(p2);
printf("栈区-变量地址\n");
printf(" a:%p\n", &a);
printf(" init_local_d:%p\n", &init_local_d);
printf(" p:%p\n", &p);
printf(" str:%p\n", str);
printf("\n堆区-动态申请地址\n");
printf(" %p\n", p1);
printf(" %p\n", p2);
printf("\n全局区-全局变量和静态变量\n");
printf("\n.bss段\n");
printf("全局外部无初值 uninit_global_a:%p\n", &uninit_global_a);
printf("静态外部无初值 uninits_global_b:%p\n", &uninits_global_b);
printf("静态内部无初值 uninits_local_c:%p\n", &uninits_local_c);
printf("\n.data段\n");
printf("全局外部有初值 init_global_a:%p\n", &init_global_a);
printf("静态外部有初值 inits_global_b:%p\n", &inits_global_b);
printf("静态内部有初值 inits_local_c:%p\n", &inits_local_c);
printf("\n文字常量区\n");
printf("文字常量地址 :%p\n",var1);
printf("文字常量地址 :%p\n",var2);
printf("\n代码区\n");
printf("程序区地址 :%p\n",&main);
printf("函数地址 :%p\n",&output);
return 0;
}
}
?STM32 在栈区和堆区的地址值从上往下是变小的,与Ubuntu下刚好相反。
五、总结
在此次实验中学会了SD卡的写入,以及堆栈等概念。
六、参考
基于ubuntu,树莓派和stm32的C程序的内存分配问题_Harriet的博客-CSDN博客
【IoT】STM32 内存分配详解_简一商业-CSDN博客_stm32内存分配
基于STM32分析栈、堆、全局区、常量区、代码区、RAM、ROM - 学以解忧的个人空间 - OSCHINA - 中文开源技术交流社区
STM32 KEIL下的堆栈设置
STM32 KEIL下的堆栈设置_nancy的专栏-CSDN博客_stm32设置堆栈
|