MCU:STM32F429ZIT6

开发环境:STM32CubeMX+MDK5

外购了一个SPI接口的SD Card模块,想要实现SD卡存储数据的功能。

首先需要打开STM32CubeMX工具。输入开发板MCU对应型号,找到开发板对应封装的MCU型号,双击打开(图中第三)。

此时,双击完后会关闭此界面,然后打开一个新界面。

然后,我们开始基本配置。

现在我们选择一个LED作为系统LED,该步骤可以忽略,只是本人喜欢这样子。以硬件原理图的LD3为例子。

基本配置除了时钟树外,基本上已经配置好了。

现在配置时钟树

基本配置已经配置完,现在开始配置实验使用的内容。

配置USART1,重定向printf函数作为串口输出。

然后配置SPI1,作为驱动SD Card读写的接口。

然后配置文件系统,可以让文件的使用更方便。

现在配置按键,触发中断处理一些事情。

配置完成,完善工程,生成工程。

到此,STM32CubeMX工具的使用结束!可以发现在桌面已经生成了SDCard_rw工程。

使用MDK5打开SDCard_rw工程打开。点击魔法棒,勾选微库。选择对应的下载器,勾选下载完复位允许。USB线一端接开发板USB_Device,一端接PC。

现在可以开始实验了

在usart.c中重定向printf函数,并在usart.h中声明。

1 //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
2 int fputc(int ch, FILE *f)
3 {
4 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
5 return (ch);
6 }

在sdcard_write工程下创建UserCode文件夹,编写drive_spisd.c和drive_spisd.h。

然后在MDK5这里的SDCard_rw工程添加一个新文件夹UserCode,装入drive_spisd.c。并在魔法棒这里加入头文件路径。

drive_spisd.c如下

  1 /* Includes ------------------------------------------------------------------*/
2 #include "drive_spisd.h"
3 /* Private includes ----------------------------------------------------------*/
4 #include "spi.h"
5 #include "ff.h"
6 #include "usart.h"
7 /* Private typedef -----------------------------------------------------------*/
8
9 /* Private define ------------------------------------------------------------*/
10
11 /* Private macro -------------------------------------------------------------*/
12
13 /* Private variables ---------------------------------------------------------*/
14 uint8_t test;
15 uint8_t SD_TYPE = 0x00;
16 MSD_CARDINFO SD0_CardInfo;
17 char SD_FileName[] = "hello.txt";
18 /* Private function prototypes -----------------------------------------------*/
19 static int SD_SendCMD(uint8_t cmd, uint32_t arg, uint8_t crc);
20 static uint8_t SD_ReceiveData(uint8_t *data, uint16_t len);
21 static uint8_t SD_SendBlock(uint8_t*buf, uint8_t cmd);
22 /* Private user code ---------------------------------------------------------*/
23
24 /**
25 * @brief SPI_CS片选
26 * @note None
27 * @retval None
28 */
29 void SPISD_CS(uint8_t p)
30 {
31 HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, (p==0)?GPIO_PIN_SET:GPIO_PIN_RESET);
32 }
33
34 /**
35 * @brief 发送命令(CMD0~CMD63),发完释放
36 * @note 命令格式:0+传输标志(1-命令、0-响应)+CONTENT(6+32)+7CRC+1
37 * @retval None
38 */
39 static int SD_SendCMD(uint8_t cmd, uint32_t arg, uint8_t crc)
40 {
41 uint8_t r1,retry;
42
43 SPISD_CS(0); //取消片选
44 HAL_Delay(20);
45 SPISD_CS(1); //选通
46
47 //SD卡的SPI通信协议规定,每个命令操作之前都需要发送至少8个时钟周期
48 do
49 {
50 retry = SPI_ReadWrite(0xFF);
51 }while(retry != 0xFF);
52
53 SPI_ReadWrite(cmd | 0x40);
54 SPI_ReadWrite(arg >> 24);
55 SPI_ReadWrite(arg >> 16);
56 SPI_ReadWrite(arg >> 8);
57 SPI_ReadWrite(arg);
58 SPI_ReadWrite(crc);
59
60 if(cmd == CMD12)
61 SPI_ReadWrite(0xFF);
62 do
63 {
64 r1 = SPI_ReadWrite(0xFF);
65 }while(r1 & 0x80);
66
67 return r1;
68 }
69
70 //SD卡初始化
71 uint8_t SD_Init(void)
72 {
73 uint8_t r1,i;
74 uint8_t buff[6] = {0};
75 uint16_t retry;
76
77 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_256);
78 SPISD_CS(0);
79 for(retry=0;retry<10;retry++)
80 SPI_ReadWrite(0xFF);
81
82 //SD卡进入IDLE状态
83 do
84 {
85 r1 = SD_SendCMD(CMD0 ,0, 0x95);
86 }while(r1 != 0x01);
87
88 //查看SD卡的类型
89 SD_TYPE = 0;
90 r1 = SD_SendCMD(CMD8, 0x1AA, 0x87);
91 if(r1 == 0x01)
92 {
93 for(i=0;i<4;i++)
94 buff[i] = SPI_ReadWrite(0xFF); //Get trailing return value of R7 resp
95 if( buff[2]==0X01 && buff[3]==0XAA ) //卡是否支持2.7~3.6V
96 {
97 retry = 0XFFFE;
98 do
99 {
100 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55
101 r1 = SD_SendCMD(CMD41, 0x40000000, 0X01); //发送CMD41
102 }while(r1&&retry--);
103
104 if(retry && SD_SendCMD(CMD58, 0, 0X01) == 0) //鉴别SD2.0卡版本开始
105 {
106 for(i=0;i<4;i++)
107 buff[i] = SPI_ReadWrite(0XFF); //得到OCR值
108 SD_TYPE = (buff[0]&0x40) ? V2HC:V2;
109 }
110 }else
111 {
112 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55
113 r1 = SD_SendCMD(CMD41, 0, 0X01); //发送CMD41
114 if(r1<=1)
115 {
116 SD_TYPE = V1;
117 retry = 0XFFFE;
118 do //等待退出IDLE模式
119 {
120 SD_SendCMD(CMD55, 0, 0X01); //发送CMD55
121 r1 = SD_SendCMD(CMD41, 0, 0X01); //发送CMD41
122 }while(r1&&retry--);
123 }else //MMC卡不支持CMD55+CMD41识别
124 {
125 SD_TYPE = MMC; //MMC V3
126 retry = 0XFFFE;
127 do //等待退出IDLE模式
128 {
129 r1 = SD_SendCMD(CMD1, 0, 0X01); //发送CMD1
130 }while(r1&&retry--);
131 }
132 if( retry==0 || SD_SendCMD(CMD16, 512, 0X01)!=0 )
133 SD_TYPE = ERR; //错误的卡
134 }
135 }
136 SPISD_CS(0);
137 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_4);
138
139 return SD_TYPE?0:1;
140 }
141
142 void FileSystem_Init(void)
143 {
144 FATFS *fs;
145 DWORD fre_clust, AvailableSize, UserSize;
146 uint8_t res;
147 uint8_t *work;
148 uint16_t TotalSpace;
149
150 res = SD_Init();
151 if(res == 1)
152 printf("SD卡初始化失败! \r\n");
153
154 res = f_mount(&USERFatFS, USERPath, 1); //挂载
155 if(res == FR_NO_FILESYSTEM) //没有文件系统,格式化
156 {
157 printf("没有文件系统! \r\n");
158
159 work = malloc(_MIN_SS);
160 res = f_mkfs(USERPath, FM_FAT, 0, work, _MIN_SS); //格式化sd卡
161 free(work);
162
163 if(res == FR_OK)
164 {
165 res = f_mount(NULL, USERPath, 1); //格式化后先取消挂载
166 res = f_mount(&USERFatFS, USERPath, 1); //重新挂载
167 if(res == FR_OK)
168 {
169 printf("SD卡已经成功挂载,可以进进行文件写入测试! \r\n");
170 }
171 }
172 else
173 {
174 printf("格式化失败! \r\n");
175 }
176 }else if(res == FR_OK)
177 {
178 printf("挂载成功! \r\n");
179 }else
180 {
181 printf("挂载失败! (%d)\r\n", res);
182 }
183
184 res = f_getfree(USERPath, &fre_clust, &fs); /* 根目录 */
185 if ( res == FR_OK )
186 {
187 TotalSpace = (uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024);
188 AvailableSize = (uint16_t)((fre_clust * fs->csize) / 2 /1024);
189 UserSize = TotalSpace - AvailableSize;
190 /* Print free space in unit of MB (assuming 512 bytes/sector) */
191 printf("\r\n%d MB total drive space.\r\n%ld MB available.\r\n%ld MB used.\r\n",TotalSpace, AvailableSize, UserSize);
192 }
193 else
194 {
195 printf("Get SDCard Capacity Failed (%d)\r\n", res);
196 }
197 // f_mount(NULL, USERPath, 1); //取消挂载
198 }
199
200 /**
201 * @brief 读取指定长度数据
202 * @note None
203 * @retval None
204 */
205 static uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
206 {
207 uint8_t r1;
208
209 SPISD_CS(1);
210 do
211 {
212 r1 = SPI_ReadWrite(0xFF);
213 HAL_Delay(100);
214 }while(r1 != 0xFE);
215
216 while(len--)
217 {
218 *data = SPI_ReadWrite(0xFF);
219 data++;
220 }
221 SPI_ReadWrite(0xFF);
222 SPI_ReadWrite(0xFF);
223
224 return 0;
225 }
226
227 /**
228 * @brief 向SD卡写入一个数据包(512字节)的内容
229 * @note None
230 * @retval None
231 */
232 static uint8_t SD_SendBlock(uint8_t*buf, uint8_t cmd)
233 {
234 uint8_t r1;
235 uint16_t t;
236
237 do{
238 r1 = SPI_ReadWrite(0xFF);
239 }while(r1!=0xFF);
240
241 SPI_ReadWrite(cmd);
242 if(cmd != 0XFD) //不是结束指令
243 {
244 for(t=0; t<512; t++)
245 SPI_ReadWrite(buf[t]); //提高速度,减少函数传参时间
246 SPI_ReadWrite(0xFF); //忽略crc
247 SPI_ReadWrite(0xFF);
248 t = SPI_ReadWrite(0xFF); //接收响应
249 if( (t&0x1F) != 0x05 )
250 return 2; //响应错误
251 }
252
253 return 0; //写入成功
254 }
255
256 /**
257 * @brief CSD,卡的操作条件信息,128bit
258 * @note None
259 * @retval None
260 */
261 uint8_t SD_GetCSD(uint8_t *csd_data)
262 {
263 uint8_t r1;
264
265 r1 = SD_SendCMD(CMD9, 0, 0x01); //读取CSD寄存器
266 if(r1 == 0x00)
267 r1 = SD_ReceiveData(csd_data, 16); //接收16个字节的数据
268 SPISD_CS(0); //取消片选
269
270 return r1?1:0;
271 }
272
273 /**
274 * @brief CID,卡识别号,128bit
275 * @note None
276 * @retval None
277 */
278 uint8_t SD_GetCID(uint8_t *cid_data)
279 {
280 uint8_t r1;
281
282 r1 = SD_SendCMD(CMD10, 0, 0x01); //读取CID寄存器
283 if(r1==0x00)
284 r1 = SD_ReceiveData(cid_data, 16); //接收16个字节的数据
285 SPISD_CS(0); //取消片选
286
287 return r1?1:0;
288 }
289
290 //获取SD卡的总扇区数
291 uint32_t SD_GetSectorCount(void)
292 {
293 uint8_t n;
294 uint8_t csd[16];
295 uint16_t csize;
296 uint32_t Capacity;
297
298 if(SD_GetCSD(csd) != 0) //取CSD信息,如果期间出错,返回0
299 return 0;
300
301 if( (csd[0]&0xC0) == 0x40 ) //如果为SDHC卡,按照下面方式计算。V2.00的卡
302 {
303 csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
304 Capacity = (uint32_t)csize << 10; //得到扇区数
305 }else //V1.xx的卡
306 {
307 n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
308 csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
309 Capacity = (uint32_t)csize << (n - 9); //得到扇区数
310 }
311
312 return Capacity;
313 }
314
315 int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)
316 {
317 uint8_t r1;
318 uint8_t CSD_Tab[16], CID_Tab[16];
319
320 /* Send CMD9, Read CSD */
321 r1 = SD_SendCMD(CMD9, 0, 0xFF);
322 if(r1 != 0x00)
323 return r1;
324 if(SD_ReceiveData(CSD_Tab, 16))
325 return 1;
326
327 /* Send CMD10, Read CID */
328 r1 = SD_SendCMD(CMD10, 0, 0xFF);
329 if(r1 != 0x00)
330 return r1;
331 if(SD_ReceiveData(CID_Tab, 16))
332 return 2;
333
334 /* Byte 0 */
335 SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
336 SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
337 SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;
338 /* Byte 1 */
339 SD0_CardInfo->CSD.TAAC = CSD_Tab[1] ;
340 /* Byte 2 */
341 SD0_CardInfo->CSD.NSAC = CSD_Tab[2];
342 /* Byte 3 */
343 SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];
344 /* Byte 4 */
345 SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;
346 /* Byte 5 */
347 SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
348 SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
349 /* Byte 6 */
350 SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
351 SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
352 SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
353 SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
354 SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */
355 SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
356 /* Byte 7 */
357 SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
358 /* Byte 8 */
359 SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
360 SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
361 SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
362 /* Byte 9 */
363 SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
364 SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
365 SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
366 /* Byte 10 */
367 SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
368 SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
369 SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
370 /* Byte 11 */
371 SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
372 SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
373 /* Byte 12 */
374 SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
375 SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
376 SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
377 SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
378 /* Byte 13 */
379 SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
380 SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
381 SD0_CardInfo->CSD.Reserved3 = 0;
382 SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
383 /* Byte 14 */
384 SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
385 SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
386 SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
387 SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
388 SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
389 SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);
390 /* Byte 15 */
391 SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
392 SD0_CardInfo->CSD.Reserved4 = 1;
393
394 if(SD0_CardInfo->CardType == V2HC)
395 {
396 /* Byte 7 */
397 SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) *256;
398 /* Byte 8 */
399 SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9] ;
400 }
401
402 SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;
403 SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;
404
405 /* Byte 0 */
406 SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];
407 /* Byte 1 */
408 SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;
409 /* Byte 2 */
410 SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];
411 /* Byte 3 */
412 SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;
413 /* Byte 4 */
414 SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;
415 /* Byte 5 */
416 SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;
417 /* Byte 6 */
418 SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];
419 /* Byte 7 */
420 SD0_CardInfo->CID.ProdName2 = CID_Tab[7];
421 /* Byte 8 */
422 SD0_CardInfo->CID.ProdRev = CID_Tab[8];
423 /* Byte 9 */
424 SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;
425 /* Byte 10 */
426 SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;
427 /* Byte 11 */
428 SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;
429 /* Byte 12 */
430 SD0_CardInfo->CID.ProdSN |= CID_Tab[12];
431 /* Byte 13 */
432 SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
433 /* Byte 14 */
434 SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
435 /* Byte 15 */
436 SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];
437 /* Byte 16 */
438 SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
439 SD0_CardInfo->CID.Reserved2 = 1;
440
441 return 0;
442 }
443
444
445 //写SD卡
446 //buf:数据缓存区
447 //sector:起始扇区
448 //cnt:扇区数
449 //返回值:0,ok;其他,失败.
450 uint8_t SD_WriteDisk(uint8_t *buf, uint32_t sector, uint8_t cnt)
451 {
452 uint8_t r1;
453
454 if(SD_TYPE != V2HC)
455 sector *= 512; //转换为字节地址
456 if(cnt == 1)
457 {
458 r1 = SD_SendCMD(CMD24, sector, 0x01); //读命令
459 if(r1 == 0) //指令发送成功
460 r1=SD_SendBlock(buf, 0xFE); //写512个字节
461 }else
462 {
463 if(SD_TYPE != MMC)
464 {
465 SD_SendCMD(CMD55, 0, 0x01);
466 SD_SendCMD(CMD23, cnt, 0x01); //发送指令
467 }
468 r1 = SD_SendCMD(CMD25, sector, 0x01); //连续读命令
469 if(r1 == 0)
470 {
471 do
472 {
473 r1 = SD_SendBlock(buf,0xFC); //接收512个字节
474 buf += 512;
475 }while(--cnt && r1==0);
476 r1 = SD_SendBlock(0,0xFD); //接收512个字节
477 }
478 }
479 SPISD_CS(0); //取消片选
480
481 return r1;
482 }
483
484 //读SD卡
485 //buf:数据缓存区
486 //sector:扇区
487 //cnt:扇区数
488 //返回值:0,ok;其他,失败.
489 uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
490 {
491 uint8_t r1;
492
493 if(SD_TYPE != V2HC)
494 sector <<= 9; //转换为字节地址
495 if(cnt == 1)
496 {
497 r1 = SD_SendCMD(CMD17, sector, 0x01); //读命令
498 if(r1 == 0)
499 r1 = SD_ReceiveData(buf, 512); //接收512个字节
500 }else
501 {
502 r1 = SD_SendCMD(CMD18, sector, 0x01); //连续读命令
503 do
504 {
505 r1 = SD_ReceiveData(buf, 512); //接收512个字节
506 buf += 512;
507 }while(--cnt && r1==0);
508 SD_SendCMD(CMD12, 0, 0x01); //发送停止命令
509 }
510 SPISD_CS(0); //取消片选
511
512 return r1;
513 }
514
515 uint8_t SPI_ReadWrite(uint8_t Txdata)
516 {
517 uint8_t Rxdata;
518 HAL_SPI_TransmitReceive(&hspi1, &Txdata, &Rxdata, 1, 100);
519 return Rxdata;
520 }
521
522 //SPI1波特率设置
523 void SPI_SetSpeed(uint8_t speed)
524 {
525 hspi1.Init.BaudRatePrescaler = speed;
526 if (HAL_SPI_Init(&hspi1) != HAL_OK)
527 {
528 Error_Handler();
529 }
530 }
531 ///////////////////////////END//////////////////////////////////////

drive_spisd.h如下

  1 /* Define to prevent recursive inclusion -------------------------------------*/
2 #ifndef __DRIVE_SPISD_H
3 #define __DRIVE_SPISD_H
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif
8
9 /* Includes ------------------------------------------------------------------*/
10 #include "main.h"
11 /* Private includes ----------------------------------------------------------*/
12 #include "fatfs.h"
13 /* Exported types ------------------------------------------------------------*/
14 enum _CD_HOLD
15 {
16 HOLD = 0,
17 RELEASE = 1,
18 };
19
20 typedef struct /* Card Specific Data */
21 {
22 uint8_t CSDStruct; /* CSD structure */
23 uint8_t SysSpecVersion; /* System specification version */
24 uint8_t Reserved1; /* Reserved */
25 uint8_t TAAC; /* Data read access-time 1 */
26 uint8_t NSAC; /* Data read access-time 2 in CLK cycles */
27 uint8_t MaxBusClkFrec; /* Max. bus clock frequency */
28 uint16_t CardComdClasses; /* Card command classes */
29 uint8_t RdBlockLen; /* Max. read data block length */
30 uint8_t PartBlockRead; /* Partial blocks for read allowed */
31 uint8_t WrBlockMisalign; /* Write block misalignment */
32 uint8_t RdBlockMisalign; /* Read block misalignment */
33 uint8_t DSRImpl; /* DSR implemented */
34 uint8_t Reserved2; /* Reserved */
35 uint32_t DeviceSize; /* Device Size */
36 uint8_t MaxRdCurrentVDDMin; /* Max. read current @ VDD min */
37 uint8_t MaxRdCurrentVDDMax; /* Max. read current @ VDD max */
38 uint8_t MaxWrCurrentVDDMin; /* Max. write current @ VDD min */
39 uint8_t MaxWrCurrentVDDMax; /* Max. write current @ VDD max */
40 uint8_t DeviceSizeMul; /* Device size multiplier */
41 uint8_t EraseGrSize; /* Erase group size */
42 uint8_t EraseGrMul; /* Erase group size multiplier */
43 uint8_t WrProtectGrSize; /* Write protect group size */
44 uint8_t WrProtectGrEnable; /* Write protect group enable */
45 uint8_t ManDeflECC; /* Manufacturer default ECC */
46 uint8_t WrSpeedFact; /* Write speed factor */
47 uint8_t MaxWrBlockLen; /* Max. write data block length */
48 uint8_t WriteBlockPaPartial; /* Partial blocks for write allowed */
49 uint8_t Reserved3; /* Reserded */
50 uint8_t ContentProtectAppli; /* Content protection application */
51 uint8_t FileFormatGrouop; /* File format group */
52 uint8_t CopyFlag; /* Copy flag (OTP) */
53 uint8_t PermWrProtect; /* Permanent write protection */
54 uint8_t TempWrProtect; /* Temporary write protection */
55 uint8_t FileFormat; /* File Format */
56 uint8_t ECC; /* ECC code */
57 uint8_t CSD_CRC; /* CSD CRC */
58 uint8_t Reserved4; /* always 1*/
59 }MSD_CSD;
60
61 typedef struct /*Card Identification Data*/
62 {
63 uint8_t ManufacturerID; /* ManufacturerID */
64 uint16_t OEM_AppliID; /* OEM/Application ID */
65 uint32_t ProdName1; /* Product Name part1 */
66 uint8_t ProdName2; /* Product Name part2*/
67 uint8_t ProdRev; /* Product Revision */
68 uint32_t ProdSN; /* Product Serial Number */
69 uint8_t Reserved1; /* Reserved1 */
70 uint16_t ManufactDate; /* Manufacturing Date */
71 uint8_t CID_CRC; /* CID CRC */
72 uint8_t Reserved2; /* always 1 */
73 }MSD_CID;
74
75 typedef struct
76 {
77 MSD_CSD CSD;
78 MSD_CID CID;
79 uint32_t Capacity; /* Card Capacity */
80 uint32_t BlockSize; /* Card Block Size */
81 uint16_t RCA;
82 uint8_t CardType;
83 uint32_t SpaceTotal; /* Total space size in file system */
84 uint32_t SpaceFree; /* Free space size in file system */
85 }MSD_CARDINFO, *PMSD_CARDINFO;
86
87 extern MSD_CARDINFO SD0_CardInfo;
88 /* Exported constants --------------------------------------------------------*/
89
90 /* Exported macro ------------------------------------------------------------*/
91 //SD卡类型
92 #define ERR 0x00
93 #define MMC 0x01
94 #define V1 0x02
95 #define V2 0x04
96 #define V2HC 0x06
97
98 #define DUMMY_BYTE 0xFF
99 #define MSD_BLOCKSIZE 512
100
101 /*
102 CMD定义
103 bc:发送到所有卡,不返回响应
104 bcr:发送到所有卡,同时接收所有卡的响应
105 ac:发送到选定卡,无数据传输
106 adtc:发送到选定卡,有数据传输
107 */
108 #define CMD0 0 //bc,卡复位到IDLE状态
109 #define CMD1 1
110 #define CMD8 8 //bcr,读SD卡接口条件,包含主机支持的电压信息,并询问卡是否支持
111 #define CMD9 9 //ac,读CSD数据
112 #define CMD10 10 //ac,读CID数据
113 #define CMD12 12 //ac,停止数据传输
114 #define CMD16 16 //ac,设置块长度(对于SDHC卡块命令长度固定为512字节) 应返回0x00
115 #define CMD17 17 //adtc,读单个块
116 #define CMD18 18 //adtc,读多个块。连续读块,直到被CMD12中断。
117 #define CMD23 23 //设置多sector写入前预先擦除N个block
118 #define CMD24 24 //adtc,写单个块
119 #define CMD25 25 //adtc,写多个块。连续写块,直到被CMD12中断。
120 #define CMD41 41 //应返回0x00
121 #define CMD55 55 //ac,指定下个命令为特定应用命令(ACMD),不是标准命令,应返回0x01
122 #define CMD58 58 //读OCR信息
123 #define CMD59 59 //使能/禁止CRC,应返回0x00
124
125 //数据写入回应字意义
126 #define MSD_DATA_OK 0x05
127 #define MSD_DATA_CRC_ERROR 0x0B
128 #define MSD_DATA_WRITE_ERROR 0x0D
129 #define MSD_DATA_OTHER_ERROR 0xFF
130
131 //SD卡回应标记字
132 #define MSD_RESPONSE_NO_ERROR 0x00
133 #define MSD_IN_IDLE_STATE 0x01
134 #define MSD_ERASE_RESET 0x02
135 #define MSD_ILLEGAL_COMMAND 0x04
136 #define MSD_COM_CRC_ERROR 0x08
137 #define MSD_ERASE_SEQUENCE_ERROR 0x10
138 #define MSD_ADDRESS_ERROR 0x20
139 #define MSD_PARAMETER_ERROR 0x40
140 #define MSD_RESPONSE_FAILURE 0xFF
141
142 /* Exported functions prototypes ---------------------------------------------*/
143 void SPISD_CS(uint8_t p);
144 uint8_t SD_Init(void);
145 void WritetoSD(BYTE write_buff[],uint8_t bufSize);
146 void FileSystem_Init(void);
147 uint32_t SD_GetSectorCount(void);
148 uint8_t SD_GetCID(uint8_t *cid_data);
149 uint8_t SD_GetCSD(uint8_t *csd_data);
150 int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo);
151 uint8_t SD_ReadDisk(uint8_t*buf, uint32_t sector, uint8_t cnt);
152 uint8_t SD_WriteDisk(uint8_t*buf, uint32_t sector, uint8_t cnt);
153 void SPI_SetSpeed(uint8_t speed);
154 uint8_t SPI_ReadWrite(uint8_t Txdata);
155
156 /* Private defines -----------------------------------------------------------*/
157 extern uint8_t SD_TYPE;
158 #ifdef __cplusplus
159 }
160 #endif
161
162 #endif /* __DRIVE_SPISD_H */
163
164 /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

然后在文件系统文件夹里修改内容。

user_diskio.c如下

  1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file user_diskio.c
5 * @brief This file includes a diskio driver skeleton to be completed by the user.
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19
20 #ifdef USE_OBSOLETE_USER_CODE_SECTION_0
21 /*
22 * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
23 * To be suppressed in the future.
24 * Kept to ensure backward compatibility with previous CubeMx versions when
25 * migrating projects.
26 * User code previously added there should be copied in the new user sections before
27 * the section contents can be deleted.
28 */
29 /* USER CODE BEGIN 0 */
30 /* USER CODE END 0 */
31 #endif
32
33 /* USER CODE BEGIN DECL */
34
35 /* Includes ------------------------------------------------------------------*/
36 #include <string.h>
37 #include "ff_gen_drv.h"
38
39 /* Private typedef -----------------------------------------------------------*/
40 #include "drive_spisd.h"
41 /* Private define ------------------------------------------------------------*/
42
43 /* Private variables ---------------------------------------------------------*/
44 /* Disk status */
45 static volatile DSTATUS Stat = STA_NOINIT;
46
47 /* USER CODE END DECL */
48
49 /* Private function prototypes -----------------------------------------------*/
50 DSTATUS USER_initialize (BYTE pdrv);
51 DSTATUS USER_status (BYTE pdrv);
52 DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
53 #if _USE_WRITE == 1
54 DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
55 #endif /* _USE_WRITE == 1 */
56 #if _USE_IOCTL == 1
57 DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
58 #endif /* _USE_IOCTL == 1 */
59
60 Diskio_drvTypeDef USER_Driver =
61 {
62 USER_initialize,
63 USER_status,
64 USER_read,
65 #if _USE_WRITE
66 USER_write,
67 #endif /* _USE_WRITE == 1 */
68 #if _USE_IOCTL == 1
69 USER_ioctl,
70 #endif /* _USE_IOCTL == 1 */
71 };
72
73 /* Private functions ---------------------------------------------------------*/
74
75 /**
76 * @brief Initializes a Drive
77 * @param pdrv: Physical drive number (0..)
78 * @retval DSTATUS: Operation status
79 */
80 DSTATUS USER_initialize (
81 BYTE pdrv /* Physical drive nmuber to identify the drive */
82 )
83 {
84 /* USER CODE BEGIN INIT */
85 uint8_t res;
86 res = SD_Init();
87 if(res) //STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
88 {
89 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_256);
90 SPI_ReadWrite(0xff); //提供额外的8个时钟
91 SPI_SetSpeed(SPI_BAUDRATEPRESCALER_4);
92 }
93 if(res)
94 return STA_NOINIT;
95 else
96 return RES_OK;
97 /* USER CODE END INIT */
98 }
99
100 /**
101 * @brief Gets Disk Status
102 * @param pdrv: Physical drive number (0..)
103 * @retval DSTATUS: Operation status
104 */
105 DSTATUS USER_status (
106 BYTE pdrv /* Physical drive number to identify the drive */
107 )
108 {
109 /* USER CODE BEGIN STATUS */
110 switch (pdrv)
111 {
112 case 0 :
113 return RES_OK;
114 case 1 :
115 return RES_OK;
116 case 2 :
117 return RES_OK;
118 default:
119 return STA_NOINIT;
120 }
121 /* USER CODE END STATUS */
122 }
123
124 /**
125 * @brief Reads Sector(s)
126 * @param pdrv: Physical drive number (0..)
127 * @param *buff: Data buffer to store read data
128 * @param sector: Sector address (LBA)
129 * @param count: Number of sectors to read (1..128)
130 * @retval DRESULT: Operation result
131 */
132 DRESULT USER_read (
133 BYTE pdrv, /* Physical drive nmuber to identify the drive */
134 BYTE *buff, /* Data buffer to store read data */
135 DWORD sector, /* Sector address in LBA */
136 UINT count /* Number of sectors to read */
137 )
138 {
139 /* USER CODE BEGIN READ */
140 uint8_t res;
141 if( !count )
142 {
143 return RES_PARERR; /* count不能等于0,否则返回参数错误 */
144 }
145 switch (pdrv)
146 {
147 case 0:
148 res = SD_ReadDisk(buff,sector,count);
149 if(res == 0)
150 return RES_OK;
151 else
152 return RES_ERROR;
153 default:
154 return RES_ERROR;
155 }
156 /* USER CODE END READ */
157 }
158
159 /**
160 * @brief Writes Sector(s)
161 * @param pdrv: Physical drive number (0..)
162 * @param *buff: Data to be written
163 * @param sector: Sector address (LBA)
164 * @param count: Number of sectors to write (1..128)
165 * @retval DRESULT: Operation result
166 */
167 #if _USE_WRITE == 1
168 DRESULT USER_write (
169 BYTE pdrv, /* Physical drive nmuber to identify the drive */
170 const BYTE *buff, /* Data to be written */
171 DWORD sector, /* Sector address in LBA */
172 UINT count /* Number of sectors to write */
173 )
174 {
175 /* USER CODE BEGIN WRITE */
176 /* USER CODE HERE */
177 uint8_t res;
178 if( !count )
179 return RES_PARERR; /* count不能等于0,否则返回参数错误 */
180 switch (pdrv)
181 {
182 case 0:
183 res=SD_WriteDisk((uint8_t *)buff,sector,count);
184 if(res == 0)
185 return RES_OK;
186 else
187 return RES_ERROR;
188 default:
189 return RES_ERROR;
190 }
191 /* USER CODE END WRITE */
192 }
193 #endif /* _USE_WRITE == 1 */
194
195 /**
196 * @brief I/O control operation
197 * @param pdrv: Physical drive number (0..)
198 * @param cmd: Control code
199 * @param *buff: Buffer to send/receive control data
200 * @retval DRESULT: Operation result
201 */
202 #if _USE_IOCTL == 1
203 DRESULT USER_ioctl (
204 BYTE pdrv, /* Physical drive nmuber (0..) */
205 BYTE cmd, /* Control code */
206 void *buff /* Buffer to send/receive control data */
207 )
208 {
209 /* USER CODE BEGIN IOCTL */
210 DRESULT res;
211 switch(cmd)
212 {
213 case CTRL_SYNC:
214 SPISD_CS(1);
215 do{
216 HAL_Delay(20);
217 }while(SPI_ReadWrite(0xFF)!=0xFF);
218 res=RES_OK;
219 SPISD_CS(0);
220 break;
221 case GET_SECTOR_SIZE:
222 *(WORD*)buff = 512;
223 res = RES_OK;
224 break;
225 case GET_BLOCK_SIZE:
226 *(WORD*)buff = 8;
227 res = RES_OK;
228 break;
229 case GET_SECTOR_COUNT:
230 *(DWORD*)buff = SD_GetSectorCount();
231 res = RES_OK;
232 break;
233 default:
234 res = RES_PARERR;
235 break;
236 }
237 return res;
238 /* USER CODE END IOCTL */
239 }
240 #endif /* _USE_IOCTL == 1 */

main.c

  1 /* USER CODE BEGIN Header */
2 /**
3 ******************************************************************************
4 * @file : main.c
5 * @brief : Main program body
6 ******************************************************************************
7 * @attention
8 *
9 * Copyright (c) 2023 STMicroelectronics.
10 * All rights reserved.
11 *
12 * This software is licensed under terms that can be found in the LICENSE file
13 * in the root directory of this software component.
14 * If no LICENSE file comes with this software, it is provided AS-IS.
15 *
16 ******************************************************************************
17 */
18 /* USER CODE END Header */
19 /* Includes ------------------------------------------------------------------*/
20 #include "main.h"
21 #include "fatfs.h"
22 #include "spi.h"
23 #include "usart.h"
24 #include "gpio.h"
25
26 /* Private includes ----------------------------------------------------------*/
27 /* USER CODE BEGIN Includes */
28 #include "drive_spisd.h"
29 /* USER CODE END Includes */
30
31 /* Private typedef -----------------------------------------------------------*/
32 /* USER CODE BEGIN PTD */
33
34 /* USER CODE END PTD */
35
36 /* Private define ------------------------------------------------------------*/
37 /* USER CODE BEGIN PD */
38 /* USER CODE END PD */
39
40 /* Private macro -------------------------------------------------------------*/
41 /* USER CODE BEGIN PM */
42
43 /* USER CODE END PM */
44
45 /* Private variables ---------------------------------------------------------*/
46
47 /* USER CODE BEGIN PV */
48 uint8_t status = 0;
49 uint8_t writeBuf[] = "demo program forever no bug!!!\r\n";
50 /* USER CODE END PV */
51
52 /* Private function prototypes -----------------------------------------------*/
53 void SystemClock_Config(void);
54 /* USER CODE BEGIN PFP */
55
56 /* USER CODE END PFP */
57
58 /* Private user code ---------------------------------------------------------*/
59 /* USER CODE BEGIN 0 */
60
61 /* USER CODE END 0 */
62
63 /**
64 * @brief The application entry point.
65 * @retval int
66 */
67 int main(void)
68 {
69 /* USER CODE BEGIN 1 */
70 UINT Bw;
71 FIL file;
72 uint16_t cb_task = 0;
73 uint8_t res1 = 0, res2 = 0;
74 /* USER CODE END 1 */
75
76 /* MCU Configuration--------------------------------------------------------*/
77
78 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
79 HAL_Init();
80
81 /* USER CODE BEGIN Init */
82
83 /* USER CODE END Init */
84
85 /* Configure the system clock */
86 SystemClock_Config();
87
88 /* USER CODE BEGIN SysInit */
89
90 /* USER CODE END SysInit */
91
92 /* Initialize all configured peripherals */
93 MX_GPIO_Init();
94 MX_SPI1_Init();
95 MX_USART1_UART_Init();
96 MX_FATFS_Init();
97 /* USER CODE BEGIN 2 */
98 FileSystem_Init(); //初始化文件系统
99
100 status = 1;
101 res1 = f_open(&file, "sdRW1.txt", FA_OPEN_ALWAYS | FA_WRITE);
102 if((res1 & FR_DENIED) == FR_DENIED)
103 printf("卡存储已满,写入失败! \r\n");
104 /* USER CODE END 2 */
105
106 /* Infinite loop */
107 /* USER CODE BEGIN WHILE */
108 while (1)
109 {
110 /* USER CODE END WHILE */
111
112 /* USER CODE BEGIN 3 */
113 if(status == 1)
114 {
115 if(res1 == FR_OK)
116 {
117 f_lseek(&file, f_size(&file)); //确保写入不会覆盖之前的数据
118 res2 = f_write(&file, writeBuf, sizeof(writeBuf), &Bw); //写数据到SD卡
119 if(res2 != FR_OK)
120 {
121 printf("文件写入失败! \r\n");
122 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_RESET);
123 }else
124 {
125 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_SET);
126 }
127 }
128 else
129 {
130 printf("打开文件失败! %d\r\n",res1);
131 }
132 if(++cb_task%4096==0)
133 f_sync(&file);
134 }else if(status == 2)
135 {
136 f_close(&file);
137 f_mount(NULL, USERPath, 1); //取消挂载
138 HAL_GPIO_WritePin(User_led_GPIO_Port, User_led_Pin, GPIO_PIN_RESET);
139 status = 0;
140 }
141 HAL_Delay(10);
142 }
143 /* USER CODE END 3 */
144 }
145
146 /**
147 * @brief System Clock Configuration
148 * @retval None
149 */
150 void SystemClock_Config(void)
151 {
152 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
153 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
154
155 /** Configure the main internal regulator output voltage
156 */
157 __HAL_RCC_PWR_CLK_ENABLE();
158 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
159 /** Initializes the RCC Oscillators according to the specified parameters
160 * in the RCC_OscInitTypeDef structure.
161 */
162 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
163 RCC_OscInitStruct.HSEState = RCC_HSE_ON;
164 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
165 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
166 RCC_OscInitStruct.PLL.PLLM = 4;
167 RCC_OscInitStruct.PLL.PLLN = 168;
168 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
169 RCC_OscInitStruct.PLL.PLLQ = 4;
170 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
171 {
172 Error_Handler();
173 }
174 /** Initializes the CPU, AHB and APB buses clocks
175 */
176 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
177 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
178 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
179 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
180 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
181 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
182
183 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
184 {
185 Error_Handler();
186 }
187 }
188
189 /* USER CODE BEGIN 4 */
190 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
191 {
192 if(GPIO_Pin == User_Key_Pin)
193 {
194 status = 2;
195 }
196 }
197 /* USER CODE END 4 */
198
199 /**
200 * @brief This function is executed in case of error occurrence.
201 * @retval None
202 */
203 void Error_Handler(void)
204 {
205 /* USER CODE BEGIN Error_Handler_Debug */
206 /* User can add his own implementation to report the HAL error return state */
207 __disable_irq();
208 while (1)
209 {
210 }
211 /* USER CODE END Error_Handler_Debug */
212 }
213
214 #ifdef USE_FULL_ASSERT
215 /**
216 * @brief Reports the name of the source file and the source line number
217 * where the assert_param error has occurred.
218 * @param file: pointer to the source file name
219 * @param line: assert_param error line source number
220 * @retval None
221 */
222 void assert_failed(uint8_t *file, uint32_t line)
223 {
224 /* USER CODE BEGIN 6 */
225 /* User can add his own implementation to report the file name and line number,
226 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
227 /* USER CODE END 6 */
228 }
229 #endif /* USE_FULL_ASSERT */

实测是可以的,但是写速度并没有网上说的1M/s那么快,也有可能是我引线的问题。

STM32F429 Discovery开发板应用:实现SPI-SD Card文件写入(搭载FatFS文件系统)的更多相关文章

  1. 小白自制Linux开发板 四. 通过SPI使用ESP8266做无线网卡

    本文章基于 WhyCan Forum(哇酷开发者社区) https://whycan.com/t_4149.htmlhttps://whycan.com/t_5870.html整理而成. 为了尊重原作 ...

  2. [CortexM0--stm32f0308]discovery开发板

        问题描写叙述:stm32提供了很多IC入门级开发板,价格还是蛮廉价的. stm32f0308-discovery就是一款cortex-m0架构的入门级开发板. 例如以下对其进行下简介. IO便 ...

  3. 在秉火STM32F429挑战者开发板上移植Huawei LiteOS指南

    昨天在B站上突然看到了一个短视频,是在正点原子的战舰V3开发板上移植华为的Huawei LiteOS操作系统,就是这个视频:看完鸿蒙OS发布会,试用华为的物联网操作系统Lite OS(B站),于是呢, ...

  4. 【AT91SAM3S】英蓓特EM-SAM3S开发板例子工程中的启动文件分析

    手上一块英倍特的EM-SAM3S开发板,拿到已经有一个月了.本来是做uLoong活动使用的板子,可当初由于不熟悉这个芯片,使用了STM32F4当作了替代.最近准备抽点时间折腾下这个板子. 这个板子的资 ...

  5. u-boot-2011.06在基于s3c2440开发板的移植之引导内核与加载根文件系统

    http://www.linuxidc.com/Linux/2012-09/70510.htm  来源:Linux社区  作者:赵春江 uboot最主要的功能就是能够引导内核启动.本文就介绍如何实现该 ...

  6. 开发板和centos服务器tftp传文件

    CentOS下使用TFTP向目标板传送文件http://www.linuxidc.com/Linux/2010-10/29218.htm 1.安装相关软件包 为了使主机支持TFTP,必须确保TFTP后 ...

  7. iMX6UL配置MCP2515模块(SPI转CAN)——基于迅为iTOP-iMX6UL开发板

    写在前面   在文章"嵌入式Linux的CAN总线配置--基于迅为iTOP-4412开发板"中我给4412开发板配置了SPI转CAN模块,使用的是不带设备树的内核.在本篇文章中,要 ...

  8. 第37章 基于SD卡的FatFs文件系统—零死角玩转STM32-F429系列

    第37章     基于SD卡的FatFs文件系统 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.co ...

  9. 【嵌入式开发】写入开发板Linux系统-模型S3C6410

    笔者 : 万境绝尘 转载请著名出处 最终拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...

  10. 【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410

    作者 : 万境绝尘 转载请著名出处 终于拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...

随机推荐

  1. 分享我通过 API 赚钱的思路

    写在最前 我们经常看到非常多的 API 推荐,但又经常收藏到收藏夹里吃灰,仿佛收藏了就是用了. 很多时候没有用起来,可能是因为想不到某类 API 可以用来做什么或者能应用在哪里. 下面我将我思考的一些 ...

  2. SpringBoot集成海康网络设备SDK

    目录 SDK介绍 概述 功能 下载 对接指南 集成 初始化项目 初始化SDK 初始化SDK概述 新建AppRunner 新建SdkInitService 新建InitSdkTask 新建 HCNetS ...

  3. ICMP隐蔽隧道攻击分析与检测(二)

    • ICMP协议流量特征分析 一.ASCII与HEX对照转换表 二.ICMP正常流量分析 经常使用的ping命令就是基于ICMP协议,Windows系统下ping默认传输的是:"abcdef ...

  4. golang 中的 cronjob

    引言 最近做了一个需求,是定时任务相关的.以前定时任务都是通过 linux crontab 去实现的,现在服务上云(k8s)了,尝试了 k8s 的 CronJob,由于公司提供的是界面化工具,使用.查 ...

  5. 中英文拼写检测纠正开源项目使用入门 word-checker 1.1.0

    项目简介 word-checker 本项目用于单词拼写检查.支持英文单词拼写检测,和中文拼写检测. 特性说明 可以迅速判断当前单词是否拼写错误 可以返回最佳匹配结果 可以返回纠正匹配列表,支持指定返回 ...

  6. 修复Joe主题静态资源为国内地址

    背景 Typecho 是由 type 和 echo 两个词合成的,来自于开发团队的头脑风暴. Type,有打字的意思,博客这个东西,正是一个让我们通过打字,在网络上表达自己的平台.Echo,意思是回声 ...

  7. [bx] 和 Loop指令

    在masm编译器中不同于debug的命令 如:在debug中 mov ax,[0] --> 是说将 偏移地址为 0 中的数据送入ax中 而在汇编语言中 mov ax,[0] --> 是说将 ...

  8. 1778D Flexible String Revisit

    1778D Flexible String Revisit 目录 1778D Flexible String Revisit 题目大意: 做法: dp 注意 code 题目大意: 给你两个长度均为\( ...

  9. HTML+CSS仿写的登录页面

    仿写的登录页面 使用HTML+CSS,感觉很简单,记录下 话不多说,直接上代码 <!DOCTYPE html> <html lang="en">     & ...

  10. 对doccano自动标注使用的默认UIE模型进行微调以提高特定领域的实体识别能力,提高标注速度

    虽然doccano的自动标注使用默认的UIE模型可以识别出一定的实体,但是在特定领域或者因为实体类别名不能被理解很多实体是识别不了的,所以我们可以通过自己标注的数据对模型进行微调来满足我们Auto L ...