stm32的 chn 720是stm32f103x什么意思思

小马哥STM32F1主控720空心杯四轴飞行器小马哥STM32F1主控720空心杯四轴飞行器观燕秘闻百家号电路城小马哥四轴飞行器经验证可靠,可以飞行,自重50g,能携带负载15g,380mah电池续航时间为4分钟左右,有焊接组装调试相关资料,有源代码,在摩尔吧提供相应的教程,是学习STM32的最佳利器。小马哥STM32F1主控720空心杯四轴飞行器相关视频课程:第一讲(原理图讲解)第二讲(四轴软件知识体系梳理)第六讲(软件进阶一I2C协议,数据通讯时序讲解)第七讲(软件进阶二I2C协议软件实现)第八讲(中断系统)第十一讲(MPU6050 官方DMP库的移植)第十二讲(MPU6050获取姿态角的代码实现)第十三讲(MPU6050姿态角的使用)第十四讲(SPI配置与数据通讯)第十七讲(四轴与遥控器通讯协议)第十八讲(PID控制理论知识准备)第十九讲(四轴系统分析及其PID算法讲解)第二十讲(四轴PID算法的代码实现)第二十一讲(四轴PID控制算法的参数整定及其方法)……想获取相关资料或者观看学习视频,可至电路城搜索“小马哥STM32F1主控720空心杯四轴飞行器资料”获得!本文仅代表作者观点,不代表百度立场。系作者授权百家号发表,未经许可不得转载。观燕秘闻百家号最近更新:简介:最好看的新闻、新鲜事、热点,尽在这里作者最新文章相关文章Categories实测STM32F4中printf的效率问题
一直认为printf所做的工作就是格式化字符串,然后依次调用fputc函数发送出去。于是以前都认为printf函数的瓶颈是在fputc这里,是因为发送一个字节所占的时间太长,才导致printf效率慢。也就是说,一直认为如果串口的波特率设置成115200的话,printf至少也是能达到115200的波特率的。
而这几天在学习ucOS,于是想到,如果printf的瓶颈是在等在串口发送完成的话,那么我在等待串口发送完成中断的时候是不是挂起一个信号量,然后就可以去做点别的事情了呢?这也正是RTOS的目的之一嘛————最大化利用CPU。
花了点时间时间试了下之后,发现完全没有改善!于是有了测试两次进入fputc间隔时间的想法。
测试环境:MDK5.12 + 自带STM32F4 HAL层 + UCOS3.04.04硬件平台:安富莱V5板卡,主芯片STM32F407IG,USB转串口线CH430芯片,ThinkPad T420
代码如下:
int fputc(int ch, FILE *f) {
OS_ERR err;
CPU_TS ts;
Dbg_printf_time[Dbg_printf_cnt++] = STK_VAL_REG;
//进入函数前读取systick的寄存器值
Dbg_printf_inter[Dbg_printf_cnt] = Dbg_printf_time[Dbg_printf_cnt-2] - Dbg_printf_time[Dbg_printf_cnt-1];//用刚读取的寄存器值减去上一次退出该函数的寄存器值,从而得到两次进入该函数之间的时间
while (HAL_UART_Transmit_IT(&huart, (uint8_t *)&ch, 1) != HAL_OK)
OSTaskSemPend(10, OS_OPT_PEND_BLOCKING, &ts, &err);
//check “err”
Dbg_printf_time[Dbg_printf_cnt++] = STK_VAL_REG; //退出函数前读取systick的寄存器值
Dbg_printf_inter[Dbg_printf_cnt] = Dbg_printf_time[Dbg_printf_cnt-2] - Dbg_printf_time[Dbg_printf_cnt-1];//用刚读取的寄存器值减去进入该函数的寄存器值,从而得到两次进入该函数之间的时间
if (Dbg_printf_cnt & 90)
Dbg_printf_cnt = 1;
return ch; }重点关注Dbg_printf_inter的值,结果如下:可以看到进入两次fputc之间的间隔达到了15937个tick,考虑到主频为168M
由time=tick/sysfreq可以计算得出time=94.86us。
而115200波特率的串口在发送两个Byte之间的时间是:86.5us
也就是说:用115200波特率的速度打印printf就已经把STM32的CPU都耗光了!
实在是好奇printf里面都做了什么??作为库函数,它不应该是精简,效率的典范吗?怎么会这么慢?
抛出这个问题,求高手解答,同时也欢迎像我这么无聊的同学在别的平台上验证两次fputc之间的时间差。
阅读(...) 评论()豆丁微信公众号
君,已阅读到文档的结尾了呢~~
STM32 深入浅出帮助,STM32,深入浅出,stm32,反馈意见
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
STM32 深入浅出
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='http://www.docin.com/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口赞助商链接
当前位置: >>
STM32-高级篇
1、SDIO(4bit + DMA、支持 SDHC)1.1 实验描述及工程文件清单 MicroSD 卡(SDIO 模式)测试实验,采用 4bit 数据线模式。没 有跑文件系统,只是单纯地读 block 并将测试信息通过串口 1 在电脑的超级终端上 打印出来。 硬件连接 PC12-SDIO-CLK:CLK PC10-SDIO-D2 :DATA2 PC11-SDIO-D3:CD/DATA3 PD2-SDIO-CMD :CMD PC8-SDIO-D0:DATA0 PC9-SDIO-D1:DATA1 startup/start_stm32f10x_hd.c CMSIS/core_cm3.c CMSIS/system_stm32f10x.c FWlib/stm32f10x_gpio.c FWlib/stm32f10x_rcc.c FWlib/stm32f10x_usart.c FWlib/ stm32f10x_sdio.c FWlib/ stm32f10x_dma.c FWlib/ misc.c 用户编写的文件 USER/main.c USER/stm32f10x_it.c USER/usart1.c USER/ sdio_sdcard.c实验描述用到的库文件 TheOne STM32 开发板 MicroSD 卡硬件原理图:1.2 SDIO 简介 TheOne STM32 开发板的 CPU ( STM32F103VET6 )具有一个 SDIO 接口。 SD/SDIO/MMC 主机接口可以支持 MMC 卡系统规范 4.2 版中的 3 个不同的数据 总线模式:1 位(默认)、4 位和 8 位。在 8 位模式下,该接口可以使数据传输速 率达到 48MHz,该接口兼容 SD 存储卡规范 2.0 版。SDIO 存储卡规范 2.0 版支 持两种数据总线模式:1 位(默认)和 4 位。 目前的芯片版本只能一次支持一个 SD/SDIO/MMC 4.2 版的卡,但可以同 时支持多个 MMC 4.1 版或之前版本的卡。除了 SD/SDIO/MMC,这个接口完全 与 CE-ATA 数字协议版本 1.1 兼容。 1.3 SD 协议 大多数人原来没有了解过 SD 协议,又看到 SDIO 的驱动有 2000 多行, 感觉无从下手。所以TheOne重新写了这个文档进行详细的解释,帮助大家更快 地 跨过这道槛。 附资料:《Simplified_Physical_Layer_Spec.pdf》,这个资料包含了 SDIO 协议中 SD 存储卡的部分。 下面TheOne结合 STM32 的 SDIO,分析 SD 协议,让大家对它先有个大概 了 解,更具体的说明在代码中展开。SDIO 接口图一.从 SDIO 的时钟说起。 SDIO_CK 时钟是通过 PC12 引脚连接到 SD 卡的,是 SDIO 接口与 SD 卡用 于同步的时钟。 SDIO 选配器挂载到 AHB 总线上,通过 HCLK 二分频输入到适配器得到 SDIO_CK 的时钟,这时 SDIO_CK = HCLK/(2+CLKDIV)。其中 CLKDIV 是 SDIO_CLK(寄存器)中的 CLKDIV 位。 另外,SDIO_CK 也可以由 SDIOCLK 通过设置 bypass 模式直接得到,这时 SDIO_CK = SDIOCLK=HCLK。 通过下面的库函数来配置时钟:1. SDIO_Init(&SDIO_InitStructure);对 SD 卡的操作一般是大吞吐量的数据传输,所以采用 DMA 来提高效率, SDIO 采用的是 DMA2 中的通道 4。在数据传输的时候 SDIO 可向 DMA 发出请 求。 二.讲解 SDIO 的命令、数据传输方式。 SDIO 的所有命令及命令响应,都是通过 SDIO-CMD 引脚来传输的。 命令只能由 host 即 STM32 的 SDIO 控制器发出。SDIO 协议把命令分成了 11 种,包括基本命令,读写命令还有 ACMD 系列命令等。其中,在发送 ACMD 命令前,要先向卡发送编号为 CMD55 的命令。 参照下面的命令格式图, 其中的 start bit,transmission bit ,crc7, endbit,都是由 STM32 中的 SDIO 硬件完成,我们在软件上配置的时候只需要 设置 command index 和命令参数 argument。Command index 就是命令索引 (编号),如 CMD0,CMD1…被编号成 0,1...。有的命令会包含参数,读命令 的地址参数等,这个参数被存放在 argument 段。SD 卡命令格式可以通过下面的函数来配置、发送命令:1. SDIO_SendCommand(&SDIO_CmdInitStructure); //发送命令SD 卡对 host 的各种命令的回复称为响应,除了 CMD0 命令外,SD 卡在接 收到命令都会返回一个响应。对于不同的命令,会有不同的响应格式,共 7 种, 分为长响应型(136bit)和短响应型(48bit)。以下图,响应 6(R6)为 例:SD 卡命令响应格式(R6) SDIO 通过 CMD 接收到响应后,硬件去除头尾的信息,把 command index 保存到 SDIO_RESPCMD 寄存器,把 argument field 内容保存存储到 SDIO_RESPx 寄存器中。这两个值可以分别通过下面的库函数得到。1. SDIO_GetCommandResponse(); //卡返回接收到的命令 2. SDIO_GetResponse(SDIO_RESP1); //卡返回的 argument field 内容数据写入,读取。请看下面的写数据时序图,在软件上,我们要处理的 只是读忙。另外,我们的实验中用的是 Micro SD 卡,有 4 条数据线,默认的时 候 SDIO 采用 1 条数据线的传输方式,更改为 4 条数据线模式要通过向卡发送 命令来更改。SD 卡的多块写入时序图三.卡的种类。 STM32 的 SDIO 支持 SD 存储卡,SD I/O 卡 ,MMC 卡。 其中 SDI/O 卡与 SD 存储卡是有区别的,SDI/O 卡实际上就是利用 SDIO 接口 的一些模块,插入 SD 的插槽中,扩展设备的功能,如:SDI/O wifi, SDI/O cmos 相机等。而 SD 存储卡就是我们平时常见的单纯用于存储数据的卡。 可使用 SDIO 接口类型的卡本实验中使用的 Micro SD 卡属于 SDSC(标准容量,最大两 G)卡。介绍卡的 种类是因为 SD 协议中的命令也支持这三种类型的卡,因此对 STM32 中的 SDIO 接口进行初始化后,上电后就要对接入的卡进行检测、分类,这个过程是 通过向卡发送一系列不同的命令,根据卡不同的响应来进行分类。 下面进入代码展开具体讲解。 1.4 代码分析 首先要添加用的库文件,在工程文件夹下 Fwlib 下我们需添加以下库文 件:FWlib/stm32f10x_gpio.c FWlib/stm32f10x_rcc.c FWlib/stm32f10x_usart.c FWlib/stm32f10x_sdio.c FWlib/stm32f10x_dma.c FWlib/misc.c还要在 stm32f10x_conf.h 中把相应的头文件添加进来: 1. 2. 3. 4. 5. 6.#include #include #include #include #include #include&stm32f10x_dma.h& &stm32f10x_gpio.h& &stm32f10x_rcc.h& &stm32f10x_sdio.h& &stm32f10x_usart.h& &misc.h&保持良好的习惯,从 main 函数开始分析:1. int main(void) 2. { 3. 4. /*进入到 main 函数前,启动文件 startup(startup_stm32f10x_xx.s)已经调用 了在 5. system_stm32f10x.c 中的 SystemInit(),配置好了系统时钟,在外部晶振 8M 的条件下, 6. 设置 HCLK = 72M */ 7. 8. /* Interrupt Config */ 9. NVIC_Configuration(); 10. 11. /* USART1 config */ 12. USART1_Config(); 13. 14. /*------------------------------ SD Init --------------------------------- */ 15. Status = SD_Init(); 16. 17. printf( &\r\n 这是一个 MicroSD 卡实验(没有跑文件系 统).........\r\n & ); 18. 19. 20. if(Status == SD_OK) //检测初始化是否成功 21. { 22. printf( & \r\n SD_Init 初始化成功 \r\n & ); 23. } 24. else 25. { 26. printf(&\r\n SD_Init 初始化失败 \r\n& ); 27. printf(&\r\n 返回的 Status 的值为: %d \r\n&,Status ); 28. } 29. 30. printf( & \r\n CardType is :%d &, SDCardInfo.CardType ); 31. printf( & \r\n CardCapacity is :%d &, SDCardInfo.CardCapacity ); 32. ; 33. 34. printf( & \r\n RCA is :%d &, SDCardInfo.RCA); printf( & \r\n ManufacturerID is :%d \r\n&, SDCardInfo.SD_cid.Man ufacturerID ); printf( & \r\n CardBlockSize is :%d &, SDCardInfo.CardBlockSize )35. 36. SD_EraseTest(); //擦除测试 37. 38. SD_SingleBlockTest(); //单块读写测试 39. 40. SD_MultiBlockTest(); //多块读写测试 41. 42. while (1) 43. {} 44. } main 函数的流程简单明了: 1. 用 NVIC_Configuration()初始化好 SDIO 的中断; 2. 用 USART1_Config()配置好用于返回调试信息的串口,SD_Init()开始 进行 SDIO 的初始化; 3. 最后分别用 SD_EraseTest()、SD_SingleBlockTest()、SD_MultiBlockTest() 进行擦除,单数据块读写,多数据块读写测试。 下面我们先进入 SDIO 驱动函数的大头――SD_Init()进行分析:1. /* 2. * 函数名:SD_Init 3. * 描述 :初始化 SD 卡,使卡处于就绪状态(准备传输数据) 4. * 输入 :无 5. * 输出 :-SD_Error SD 卡错误代码 6. * 成功时则为 SD_OK 7. * 调用 :外部调用 8. */ 9. SD_Error SD_Init(void) 10. { 11. /*重置 SD_Error 状态*/ 12. SD_Error errorstatus = SD_OK; 13. 14. /* SDIO 外设底层引脚初始化 */ 15. GPIO_Configuration(); 16. 17. /*对 SDIO 的所有寄存器进行复位*/ 18. SDIO_DeInit(); 19. 20. /*上电并进行卡识别流程,确认卡的操作电压 */ 21. errorstatus = SD_PowerON(); 22. 23. /*如果上电,识别不成功,返回“响应超时”错误 */ 24. if (errorstatus != SD_OK) 25. { 26. /*!& CMD Response TimeOut (wait for CMDSENT flag) */ 27. return(errorstatus); 28. } 29. 30. /*卡识别成功,进行卡初始化 */ 31. errorstatus = SD_InitializeCards(); 32. 33. if (errorstatus != SD_OK) //失败返回 34. { 35. /*!& CMD Response TimeOut (wait for CMDSENT flag) */ 36. return(errorstatus); 37. } 38. 39. /*!& Configure the SDIO peripheral 40. 上电识别,卡初始化都完成后,进入数据传输模式,提高读写速度 41. 速度若超过 24M 要进入 bypass 模式 42. !& on STM32F2xx devices, SDIOCLK is fixed to 48MHz 43. !& SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */ 44. 45. SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV; SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_R 升沿采集数据//上 46.SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_D //时钟 频率若超过 24M,要开启此模式 47. SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_D //若开启此功能,在总线空闲时关闭 sd_clk 时钟 48. SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b; //1 位模式 49. SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_ D //硬件流,若开启,在 FIFO 不能进行发送和接收数据时,数据传输暂停 50. SDIO_Init(&SDIO_InitStructure); 51. 52. if (errorstatus == SD_OK) 53. { 54. /*----------------- Read CSD/CID MSD registers -----------------*/ 55. errorstatus = SD_GetCardInfo(&SDCardInfo); //用来读取 csd/cid 寄存 器 56. } 57. 58. if (errorstatus == SD_OK) 59. { 60. /*----------------- Select Card -------------------------------*/ 61. errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA && 16)); //通过 cmd7 ,rca 选择要操作的卡 62. } 63. 64. if (errorstatus == SD_OK) 65. { 66. errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b); //开启 4bits 模式 67. } 68. 69. return(errorstatus); 70. }先从整体上了解这个 SD_Init()函数: 1.用 GPIO_Configuration()进行 SDIO 的端口底层配置 2.分别调用了 SD_PowerON()和 SD_InitializeCards()函数,这两个函数共同实 现了上面提到的卡检测、识别流程。 3.调用 SDIO_Init(&SDIO_InitStructure)库函数配置 SDIO 的时钟,数据线宽 度,硬件流(在读写数据的时候,开启硬件流是和很必要的,可以减少出错) 4. 调用 SD_GetCardInfo(&SDCardInfo)获取 sd 卡的 CSD 寄存器中的内容,在 main 函数里输出到串口的数据就是这个时候从卡读取得到的。 5. 调用 SD_SelectDeselect()选定后面即将要操作的卡。 6.调用 SD_EnableWideBusOperation(SDIO_BusWide_4b) 开启 4bit 数据线模式如果 SD_Init() 函数能够执行完整个流程,并且返回值是SD_OK 的话则说明初始化成功,就可以开始进行擦除、读写的操作了。 下面进入 SD_PowerON() 函数,分析完这个函数大家就能了解 SDIO 如何接收、 发送命令了。 1. /* 2. * 函数名:SD_PowerON 3. * 描述 :确保 SD 卡的工作电压和配置控制时钟 4. * 输入 :无 5. * 输出 :-SD_Error SD 卡错误代码 6. * 成功时则为 SD_OK 7. * 调用 :在 SD_Init() 调用 8. */ 9. SD_Error SD_PowerON(void) 10. { 11. SD_Error errorstatus = SD_OK; 12. uint32_t response = 0, count = 0, validvoltage = 0; 13. uint32_t SDType = SD_STD_CAPACITY; 14. 15. /*!& Power ON Sequence ----------------------------------------------------*/ 16. /*!& Configure the SDIO peripheral */ 17. /*!& SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */ 18. /*!& on STM32F2xx devices, SDIOCLK is fixed to 48MHz */ 19. /*!& SDIO_CK for initialization should not exceed 400 KHz */ 20. /*初始化时的时钟不能大于 400KHz*/ 21. SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV; /* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */ 22. SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_R 23. SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_D //不使 用 bypass 模式,直接用 HCLK 进行分频得到 SDIO_CK 24. SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_D / / 空闲时不关闭时钟电源 25. SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b; / /1 位数据线 26. SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_ D//硬件流 27. SDIO_Init(&SDIO_InitStructure); 28. 29. /*!& Set Power State to ON */ 30. SDIO_SetPowerState(SDIO_PowerState_ON); 31. 32. /*!& Enable SDIO Clock */ 33. SDIO_ClockCmd(ENABLE); 34. 35. /*下面发送一系列命令,开始卡识别流程*/ 36. /*!& CMD0: GO_IDLE_STATE --------------------------------------------------*/ 37. /*!& No CMD response required */ 38. SDIO_CmdInitStructure.SDIO_Argument = 0x0; 39. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; //cmd0 40. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No; //无响应 41. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 42. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E //则 CPSM 在开始发送 命令之前等待数据传输结束。 43. SDIO_SendCommand(&SDIO_CmdInitStructure); //写命令进命令寄存器 44. 45. errorstatus = CmdError();//检测是否正确接收到 cmd0 46. 47. if (errorstatus != SD_OK) //命令发送出错,返回 48. { 49. /*!& CMD Response TimeOut (wait for CMDSENT flag) */ 50. return(errorstatus); 51. } 52. 53. /*!& CMD8: SEND_IF_COND ---------------------------------------------------*/ 54. /*!& Send CMD8 to verify SD card interface operating condition */ 55. /*!& Argument: - [31:12]: Reserved (shall be set to '0') 56. - [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V) 57. 58. 59.- [7:0]: Check Pattern (recommended 0xAA) */ /*!& CMD Response: R7 */ SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN; //接收到命令 sd 会返回这个参数 60. SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND; //cmd8 61. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S //r7 62. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; //关闭等待中 断 63. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 64. SDIO_SendCommand(&SDIO_CmdInitStructure); 65. 66. /*检查是否接收到命令*/ 67. errorstatus = CmdResp7Error(); 68. 69. if (errorstatus == SD_OK) //有响应则 card 遵循 sd 协议 2.0 版本 70. { 71. CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!& SD Card 2.0 ,先把它定 义会 sdsc 类型的卡*/ 72. SDType = SD_HIGH_CAPACITY; //这个变量用作 acmd41 的参数,用来询问是 sdsc 卡 还是 sdhc 卡 73. } 74. else //无响应,说明是 1.x 的或 mmc 的卡 75. { 76. /*!& CMD55 */ 77. SDIO_CmdInitStructure.SDIO_Argument = 0x00; 78. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD; 79. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S 80. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 81. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 82. SDIO_SendCommand(&SDIO_CmdInitStructure); 83. errorstatus = CmdResp1Error(SD_CMD_APP_CMD); 84. } 85. /*!& CMD55 */ //为什么在 else 里和 else 外面都要发送 CMD55? 86. //发送 cmd55,用于检测是 sd 卡还是 mmc 卡,或是不支持的卡 87. SDIO_CmdInitStructure.SDIO_Argument = 0x00; 88. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD; 89. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S //r1 90. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 91. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 92. SDIO_SendCommand(&SDIO_CmdInitStructure); 93. errorstatus = CmdResp1Error(SD_CMD_APP_CMD); //是否响应,没响应的是 mmc 或 不支持的卡 94. 95. /*!& If errorstatus is Command TimeOut, it is a MMC card */ 96. /*!& If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage rang e mismatch) 97. or SD card 1.x */ 98. if (errorstatus == SD_OK) //响应了 cmd55,是 sd 卡,可能为 1.x,可能为 2.0 99. { 100. /*下面开始循环地发送 sdio 支持的电压范围,循环一定次数*/ 101. 102. /*!& SD CARD */ 103. /*!& Send ACMD41 SD_APP_OP_COND with Argument 0x */ 104. while ((!validvoltage) && (count & SD_MAX_VOLT_TRIAL)) 105. { 106. //因为下面要用到 ACMD41,是 ACMD 命令,在发送 ACMD 命令前都要先向卡发送 CMD55 107. /*!& SEND CMD55 APP_CMD with RCA as 0 */ 108. SDIO_CmdInitStructure.SDIO_Argument = 0x00; 109. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD; //CMD55 110. 111. 112. 113. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E SDIO_SendCommand(&SDIO_CmdInitStructure); 114. 115. errorstatus = CmdResp1Error(SD_CMD_APP_CMD); //检测响应 116. 117. if (errorstatus != SD_OK) 118. { 119. return(errorstatus);//没响应 CMD55,返回 120. } 121. //acmd41,命令参数由支持的电压范围及 HCS 位组成,HCS 位置一来区分卡是 SDSc 还是 sdhc 122. SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDT //参数为主机可供电压范围及 hcs 位 123. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND; 124. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S //r3 125. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 126. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 127. SDIO_SendCommand(&SDIO_CmdInitStructure); 128. 129. errorstatus = CmdResp3Error(); //检测是否正确接收到数据 130. if (errorstatus != SD_OK) 131. { 132. return(errorstatus); //没正确接收到 acmd41,出错,返回 133. } 134. /*若卡需求电压在 SDIO 的供电电压范围内,会自动上电并标志 pwr_up 位*/ 135. response = SDIO_GetResponse(SDIO_RESP1); //读取卡寄存器,卡状 态 136. validvoltage = (((response && 31) == 1) ? 1 : 0); //读取卡的 ocr 寄存器的 pwr_up 位,看是否已工作在正常电压 137. count++; //计算循环次数 138. } 139. if (count &= SD_MAX_VOLT_TRIAL) //循环检测超过一定次数还没上电 140. { 141. errorstatus = SD_INVALID_VOLTRANGE; //SDIO 不支持 card 的供电电 压 142. return(errorstatus); 143. } 144. /*检查卡返回信息中的 HCS 位*/ 145. if (response &= SD_HIGH_CAPACITY) //判断 ocr 中的 ccs 位 ,如果是 sdsc 卡则不执行下面的语句 146. { 147. CardType = SDIO_HIGH_CAPACITY_SD_CARD; //把卡类型从初始化的 sdsc 型 改为 sdhc 型 148. } 149. 150. }/*!& else MMC Card */ 151. 152. return(errorstatus); 153. }这个函数的流程就是卡的上电、识别操作,如下图:卡的上电,识别流程: 截图来自《Simplified_Physical_Layer_Spec.pdf》 page27 代码中所有的判断语句都是根据这个图的各个识别走向展开的,最终把卡 分为 1.0 版的 SD 存储卡,2.0 版的 SDSC 卡和 2.0 版的 SDHC 卡。 在这个代码流程中有两点要注意一下: 1.初始化的时钟。SDIO_CK 的时钟分为两个阶段,在初始化阶段 SDIO_CK 的频率要小于 400KHz,初始化完成后可把 SDIO_CK 调整成高速模式,高速模 式时超过 24M 要开启 bypass 模式,对于 SD 存储卡即使开启 bypass,最高频 率不能超过 25MHz。 2.CMD8 命令。 CMD8 命令格式。 CMD8 命令中的 VHS 是用来确认主机 SDIO 是否支持卡的工作电压的。Check pattern 部分可以是任何数值,若 SDIO 支持卡的工作电压,卡会把接收到的 check pattern 数值原样返回给主机。 CMD8 命令的响应格式 R7:在驱动程序中调用了 CmdResp7Error()来检验卡接收命令后的响应。 3.ACMD41 命令。 这个命令也是用来进一步检查 SDIO 是否支持卡的工作电压的,协议要它在 调用它之前必须先调用 CMD8,另外还可以通过它命令参数中的 HCS 位来区分 卡是 SDHC 卡还是 SDSC 卡。 确认工作电压时循环地发送 ACMD41,发送后检查在 SD 卡上的 OCR 寄存器 中的 pwr_up 位,若 pwr_up 位置为 1,表明 SDIO 支持卡的工作电压,卡开始 正常工作。 同时把 ACMD41 中的命令参数 HCS 位置 1,卡正常工作的时候检测 OCR 寄存 器中的 CCS 位,若 CCS 位为 1 则说明该卡为 SDHC 卡,为零则为 SDSC 卡。 因为 ACMD41 命令属于 ACMD 命令,在发送 ACMD 命令前都要先发送 CMD55. ACMD41 命令格式 ACMD41 命令的响应(R3),返回的是 OCR 寄存器的值OCR 寄存器的内容SD 卡上电确认成功后,进入 SD_InitializeCards() 函数: 1. /* 2. * 函数名:SD_InitializeCards 3. * 描述 :初始化所有的卡或者单个卡进入就绪状态 4. * 输入 :无 5. * 输出 :-SD_Error SD 卡错误代码 6. * 成功时则为 SD_OK 7. * 调用 :在 SD_Init() 调用,在调用 power_on()上电卡识别完毕后,调用此函数进 行卡初始化 8. */ 9. SD_Error SD_InitializeCards(void) 10. { 11. SD_Error errorstatus = SD_OK; 12. uint16_t rca = 0x01; 13. 14. if (SDIO_GetPowerState() == SDIO_PowerState_OFF) 15. { 16. errorstatus = SD_REQUEST_NOT_APPLICABLE; 17. return(errorstatus); 18. } 19. 20. if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)//判断卡的类型 21. { 22. /*!& Send CMD2 ALL_SEND_CID */ 23. SDIO_CmdInitStructure.SDIO_Argument = 0x0; 24. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID; 25. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_L 26. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 27. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 28. SDIO_SendCommand(&SDIO_CmdInitStructure); 29. 30. errorstatus = CmdResp2Error(); 31. 32. if (SD_OK != errorstatus) 33. { 34. return(errorstatus); 35. } 36. 37. CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1); 38. CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2); 39. CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3); 40. CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4); 41. } 42. 43. /*下面开始 SD 卡初始化流程*/ 44. if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPAC ITY_SD_CARD_V2_0 == CardType) || (SDIO_SECURE_DIGITAL_IO_COMBO_CARD = = CardType) 45. || (SDIO_HIGH_CAPACITY_SD_CARD == CardType)) //使用的是 2.0 的 卡 46. { 47. /*!& Send CMD3 SET_REL_ADDR with argument 0 */ 48. /*!& SD Card publishes its RCA. */ 49. SDIO_CmdInitStructure.SDIO_Argument = 0x00; 50. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR; //cmd3 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S //r6 SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca); //把接收到 的卡相对地址存起来。 if (SD_OK != errorstatus) { return(errorstatus); 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. }} } if (SDIO_SECURE_DIGITAL_IO_CARD != CardType) { RCA = /*!& Send CMD9 SEND_CSD with argument as card's RCA */ SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca && 16); SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_L SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp2Error(); if (SD_OK != errorstatus) { return(errorstatus); } CSD_Tab[0] CSD_Tab[1] CSD_Tab[2] CSD_Tab[3] } errorstatus = SD_OK; /*!& All cards get intialized */ return(errorstatus); = = = = SDIO_GetResponse(SDIO_RESP1); SDIO_GetResponse(SDIO_RESP2); SDIO_GetResponse(SDIO_RESP3); SDIO_GetResponse(SDIO_RESP4);这个函数向卡发送了 CMD2 和 CMD3 命令 1.CMD2 CMD2 命令是要求卡返回它的 CID 寄存器的内容。 命令的响应格式(R2)。因为命令格式是 136 位的,属于长响应。软件接收的信息有 128 位。在长响应 的时候通过 SDIO_GetResponse(SDIO_RESP4) ; 中的不同参数来获取 CID 中的不同数 据段的数据。2.CMD3CMD3 命令是要求卡向主机发送卡的相对地址。在接有多个卡的时候,主机要 求接口上的卡重新发一个相对地址,这个地址跟卡的实际 ID 不一样。比如接 口上接了 5 个卡,这 5 个卡的相对地址就分别为 1,2,3,4,5.以后主机 SDIO 对这几个卡寻址就直接使用相对地址。这个地址的作用就是为了寻址更 加简单。接下来我们回到 SD_Init()函数。分析到这里大家应该对 SDIO 的命令发送和响 应比较清楚了。在 SD_InitializeCards()之后的 SD_GetCardInfo(&SDCardInfo)、SD_SelectDeselect()和 SD_EnableWideBusOperation(SDIO_BusWide_4b) 的具体实现就不再详细分析了,实 际就是发送相应的命令,对卡进行相应的操作。 接下来分析 main 函数中的 SD_MultiBlockTest() 多块数据读写函数, 让 大家了解 SDIO 是怎样传输数据的。1. /* 2. * 函数名:SD_MultiBlockTest 3. * 描述 : 多数据块读写测试 4. * 输入 :无 5. * 输出 :无 6. */ 7. void SD_MultiBlockTest(void) 8. { 9. /*--------------- Multiple Block Read/Write --------------------*/ 10. /* Fill the buffer to send */ 11. Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0); 12. 13. if (Status == SD_OK) 14. 15. 16.{/* Write multiple block of many bytes on address 0 */ Status = SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, BLOCK_SIZ E, NUMBER_OF_BLOCKS); 17. /* Check if the Transfer is finished */ 18. Status = SD_WaitWriteOperation(); 19. while(SD_GetStatus() != SD_TRANSFER_OK); 20. } 21. 22. if (Status == SD_OK) 23. { 24. /* Read block of many bytes from address 0 */ 25. Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE , NUMBER_OF_BLOCKS); 26. /* Check if the Transfer is finished */ 27. Status = SD_WaitReadOperation(); 28. while(SD_GetStatus() != SD_TRANSFER_OK); 29. } 30. 31. /* Check the correctness of written data */ 32. if (Status == SD_OK) 33. { 34. TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBloc k_Rx, MULTI_BUFFER_SIZE); 35. } 36. 37. if(TransferStatus2 == PASSED) 38. printf(&\r\n 多块读写测试成功! & ); 39. 40. else 41. printf(&\r\n 多块读写测试失败! & ); 42. 43. }把这个函数拿出来分析最重要的一点就是让大家注意在调用了SD_WriteMultiBlocks()这一类读写操作的函数后,一定要调用 SD_WaitWriteOperation()[在读数据时调用 SD_WaitReadOperation ]和 SD_GetStatus()来确保数据传输已经结束再进行其它操作。其中的 SD_WaitWriteOperation()是用来 等待 DMA 把缓冲的数据传输到 SDIO 的 FIFO 的;而 SD_GetStatus()是用来等待 卡与 SDIO 之间传输数据完毕的。 最后进入 SD_WriteMultiBlocks()函数分析:1. /* 2. * 函数名:SD_WriteMultiBlocks 3. * 描述 :从输入的起始地址开始,向卡写入多个数据块, 4. 只能在 DMA 模式下使用这个函数 5. 注意:调用这个函数后一定要调用 6. SD_WaitWriteOperation()来等待 DMA 传输结束 7. 和 SD_GetStatus() 检测卡与 SDIO 的 FIFO 间是否已经完成传输 8. * 输入 : 9. * @param WriteAddr: Address from where data are to be read. 10. * @param writebuff: pointer to the buffer that contain the data to be transferred. 11. * @param BlockSize: the SD card Data block size. The Block size should be 512. 12. * @param NumberOfBlocks: number of blocks to be written. 13. * 输出 :SD 错误类型 14. */ 15. SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, u int16_t BlockSize, uint32_t NumberOfBlocks) 16. { 17. SD_Error errorstatus = SD_OK; 18. IO uint32_t count = 0; 19. 20. TransferError = SD_OK; 21. TransferEnd = 0; 22. StopCondition = 1; 23. 24. SDIO-&DCTRL = 0x0; 25. 26. if (CardType == SDIO_HIGH_CAPACITY_SD_CARD) 27. { 28. BlockSize = 512; 29. WriteAddr /= 512; 30. } 31. 32. /*******************add,没有这一段容易卡死在 DMA 检测中 *************************************/ 33. /*!& Set Block Size for Card,cmd16,若是 sdsc 卡,可以用来设置块大小,若 是 sdhc 卡,块大小为 512 字节,不受 cmd16 影响 */ 34. SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockS 35. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN; 36. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S //r1 37. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 38. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 39. SDIO_SendCommand(&SDIO_CmdInitStructure); 40. 41. errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN); 42. 43. if (SD_OK != errorstatus) 44. { 45. return(errorstatus); 46. } 47. /******************************************************************** *************/ 48. 49. /*!& To improve performance */ 50. SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA && 16); 51. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD; // cmd55 52. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S 53. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 54. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 55. SDIO_SendCommand(&SDIO_CmdInitStructure); 56. 57. 58. errorstatus = CmdResp1Error(SD_CMD_APP_CMD); 59. 60. if (errorstatus != SD_OK) 61. { 62. return(errorstatus); 63. } 64. /*!& To improve performance */// pre-erased,在多块写入时可发送此命令进 行预擦除 65. SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)NumberOfB // 参数为将要写入的块数目 66. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCK_COUNT; //cmd 23 67. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S 68. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; 69. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E 70. SDIO_SendCommand(&SDIO_CmdInitStructure); 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99.errorstatus = CmdResp1Error(SD_CMD_SET_BLOCK_COUNT); if (errorstatus != SD_OK) { return(errorstatus); } /*!& Send CMD25 WRITE_MULT_BLOCK with argument data address */ SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)WriteA SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_S SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_E SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK); if (SD_OK != errorstatus) { return(errorstatus); } SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT; SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockS SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 && 4; SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToC SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_B100. SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_E 101. SDIO_DataConfig(&SDIO_DataInitStructure); 102. 103. SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE); 104. SDIO_DMACmd(ENABLE); 105. SD_DMA_TxConfig((uint32_t *)writebuff, (NumberOfBlocks * Bloc kSize)); 106. 107. return(errorstatus); 108. }写操作在发送正式的多块写入命令 CMD25 前调用了 CMD23 进行预写,这样 有利于提高写入的速度。在代码的最后调用了 SDIO_ITConfig(),SDIO 的数据 传输结束中断就是这个时候开启的,数据传输结束时,就进入到 stm32f10x_it.c 文件中的中断服务函数 SDIO_IRQHandler()中处理了,中断服务函 数主要就是负责清中断。 最后讲一下官方原版的驱动中的一个 bug。 在官方原版的 SDIO 驱动的 SD_ReadBlock()、SD_ReadMultiBlocks()、 SD_WriteBlock()和 SD_WriteMultiBlocks()这几个函数中,发送读写命令前,漏 掉了发送一个 CMD16 命令,这个命令用于设置读写 SD 卡的块大小。缺少这个 命令很容易导致程序运行时卡死在循环检测 DMA 传输结束的代码中,网上很多 人直接移植 ST 官方例程时,用 3.5 版库函数和这个 4.5 版的 SDIO 驱动移植失 败,就是缺少了这段用 CMD16 设置块大小的代码。 到这里,终于讲解完毕 啦!这个讲解如果能让你从对 SDIO 一无所知到大概 了解的话,我的目标就达到啦,想要更深入了解还是要好好地配合这个例程中 我在代码中的注释和附带资料 SD2.0 协议 《Simplified_Physical_Layer_Spec.pdf》好好研究一番! ^_^注意:这个例程是没有跑文件系统的,而是直接就去读卡的 block,这样的话 就会破坏卡的分区,在实验完成之后,你再把卡插到电脑上时,电脑 会提示你要重新初始化卡,这是正常想象,并不是本实验把你的卡弄 坏了,如果卡原来有资料的请先把数据备份了再进行测试。但跑文件 系统时就不会出现这种问题,有关文件系统的操作将在下一讲的教程 中讲解。 1.5 实验现象 将TheOne STM32 开发板供电(DC5V),插上 JLINK,插上串口线(两头都是 母 的交叉线),插上 MicroSD 卡( 我用的是 1G ,经测试,本驱动也适用于 2G 以 上的卡(sdhc 卡)),打开超级终端,配置超级终端为 -N-1,将 编译 好的程序下载到开发板,即可看到超级终端打印出如下信息: 2、FatFs (Rev-R0.09)2.1 实验描述及工程文件清单 MicroSD 卡文件系统 FATFS R0.07C 测试实验。在 MicroSD 卡里面创建一个 DEMO.TXT 文本文件,在文件里面写入字符 串“感谢您选用 TheOne STM32 开发板 !^_^”,然后通过 串 口将这些内容打印在电脑的超级终端上。这个更新版本 的代 码增加了简体中文和长文件名的支持,采用 SDIO 的 4bit+DMA 模式。并图解了文件系统移植的全部过程。实验描述硬件连接PC12-SDIO-CLK:CLK PC10-SDIO-D2 :DATA2 PC11-SDIO-D3:CD/DATA3 PD2-SDIO-CMD :CMD PC8-SDIO-D0:DATA0 PC9-SDIO-D1:DATA1 startup/start_stm32f10x_hd.c CMSIS/core_cm3.c CMSIS/system_stm32f10x.c FWlib/stm32f10x_gpio.c FWlib/stm32f10x_rcc.c FWlib/stm32f10x_usart.c FWlib/ stm32f10x_sdio.c FWlib/ stm32f10x_dma.c FWlib/ misc.c用到的库文件用户编写的文件USER/main.c USER/stm32f10x_it.c USER/usart1.c USER/ sdio_sdcard.c 文件系统文件 ff9/diskio.c ff9/ff.c ff9/cc936.cTheOne STM32 开发板 MicroSD 卡硬件原理图:2.2 实验简介 本实验是在上一讲《SDIO(4bit + DMA)》的基础上讲解的,只有上一讲 的实验成功了,文件系统才能跑起来。有关卡的底层的初始化,这里不再详 述, 这里着重讲解文件系统的移植和文件系统的应用。这个文档是更新版本, 采用 的文件系统的版本是目前最新的 R0.09。 文件系统的源码可以从 fatfs 官网 下载 http://elm-chan.org/fsw/ff/00index_e.html 2.3 FatFS 文件系统简介 FAFFS 是面向小型嵌入式系统的一种通用的 FAT 文件系统。FATFS 完全是 由 AISI C 语言编写并且完全独立于底层的 I/O 介质。因此它可以很容易地不加 修改地移植到其他的处理器当中,如 8051、PIC、AVR、SH、Z80、H8、ARM 等。FATFS 支持 FAT12、FAT16、FAT32 等格式,所以我们利用前面写好的 SDIO 驱动,把 FATFS 文件系统代码移植到工程之中,就可以利用文件系统的 各种函数,对已格式化的 SD 卡进行读写文件了。 本实验是将 FATFS 移植到TheOne STM32 开发板中,CPU 为 STM32F103VET6,是采用 ARM 公司最新内核 ARMV7 的一款单片机。 2.4 移植前的工作 2.4.1 分析 FATFS 的目录结构 在移植 FATFS 文件系统之前,我们先要到 FAT 的官网获取源码,版本为 R0.07C。解压之后可看到里面有 doc 和 src 这两个文件夹。doc 文件夹里面是 一些使用文档, src 里面是文件系统的源码。打开 doc 文件夹,可看到如下文件目录: 其中 en 和 ja 这两个文件夹里面是编译好的 html 文档,讲的是 FATFS 里 面各个函数的使用方法,这些函数就如 LINUX 下的系统调用,是封装得非常好 的函数,利用这些函数我们就可以操作我们的 MicroSD 卡了。有关具体的函数 我们在用到的时候再讲解。这两个文件夹的唯一区别就是 en 文件夹下的文档 是英文的,ja 文件夹下的是日文的。偏偏就是没中文的,真狗血呀。 00index_e.html 是一些关于 FATFS 的英文简介,updates.txt 是 FATFS 的更新 信息,至于其他几个文件可以不看。 打开 src 文件夹,可看到如下目录: option 文件夹下是一些可选的外部 c 文件,包含了多语言支持需要用到的 文件和转换函数。 00readme.txt 说明了当前目录下 diskio.c 、diskio.h、ff.c、ff.h、integer.h 的功用、涉及了 FATFS 的版权问题( 是自由软件 ),还讲到了 FATFS 的版本更 新信息。integer.h:是一些数值类型定义 diskio.c : 底层磁盘的操作函数,这些函数需要用户自己实现 ff.c : 独立于底层介质操作文件的函数,完全由 ANSI C 编写cc936.c :简体中文支持所需要添加的文件,包含了简体中文的 GBK 和转 换函数。 只要添加进来就行,这个文件不需要修改。 ffconf.h:这个头文件包含了对文件系统的各种配置,如需要支持简体中文 要把_CODE_PAGE 的宏改成 936 并把上面的 cc936.c 文件加入到工程之中 建议阅读这些源码的顺序为:integer.h -& diskio.c -& ff.c 。 关于具体源 码的分析不是我能力所及呀,大家就自己研究吧。我的主要工 作是带领大家把这个文件系统移植到我们的开发板上,让这个文件系统先跑起 来,这样才是硬道理呀。文件系统工作起来了的话,源码的分析那自然是大家 的活啦。2.5 开始移植 首先我们要获取一个完全没有修改过的文件系统源码,然后 在 10-MicroSD 卡这个文件夹下的实验代码下移植,这个实验代码实现的是卡的底层的块操 作。注意,我们在移植这个文件系统的过程中会尽量保持文件系统源码的纯 净,尽量做到在修改最少量的源码的情况下移植成功。 首先将 integer.h、diskio.h、diskio.c、ff.h、ff.c 添加到工程目录下的 USER 文件夹下,如下截图: 然后并回到 MDK 界面下将 diskio.c 、ff.c 这两个文件添加到 USER 目录下,如 下截图:因为我们要用到这两个 c 文件,所以我们在 main.c 中将这两个 c 文件对应 的头文件 diskio.h 、ff.h 包含进来,如下截图: 好嘞,下面我们开始编译,这时会出现如下错误:意思是说 FALSE 跟 TRUE 这两个变量已经定义过了,为什么会出现这个错 误呢?因为在 integer.h 和我们的 M3 库头文件 stm32f10x.h 中都定义了这两个 变量,所以就产生了重复定义: integer.hstm32f10x.h怎么解决呢,很简单,只要搞掉一个即可,但要去掉哪个呢?我们考虑到 外面的 M3 库很多文件都包含了 stm32f10x.h 这个头文件,假如是修改 stm32f10x.h 的话工作量会非常大,鉴于此就只能委屈 integer.h 了,修改如 下,将它注释掉:好嘞,修改之后,我们再编译下,接着又出现如下错误:意思是说在 diskio.h、ff.c 中 BOOL 、FALSE、TRUE 没定义。刚刚才把人 家注释掉了,不报错才怪。 解决方法如下: 1、将 integer.h 中有关 BOOL 的那句注释掉,注释掉也没太大关系,因为 注释掉的不会怎么用到:2、在 ff.c 文件的开头重新定义一个布尔变量,取名为 bool,与 stm32f10x.h 中的名字一样:同时在 ff.c 的第 585 行做如下修改:现在我们再编译下,发现既没警告也没错误: 到这里我们算是把文件系统移植成功了,接下来的任务就是调用文件系统 的函数来操作我们的卡了。其实这里的移植是非常非常简单的,要是你学过 LINUX 的话,那里面的 UBOOT 移植,系统移植,那才叫人头疼,就光是目录 里面的文件夹都几千个,更别说是找到要修改的源代码了,刚接触的话绝对叫 你吐血,就连下载个交叉编译器都涉及到移植。 2.6 实验代码分析 FATFS 是独立于底层介质的应用函数库,对底层介质的操作都要交给用户 去实现,其仅仅是提供了一个函数接口而已,函数为空,要用户添加代码。 这几个函数的原型如下,在 diskio.c 中定义:1. 2. 3. 4. ) /* Inidialize a Drive */ DSTATUS disk_initialize ( BYTE drv /* Physical drive nmuber (0..) */1. 2. 3. 4./* Return Disk Status DSTATUS disk_status ( BYTE drv )*//* Physical drive nmuber (0..) */1. 2./* Read Sector(s) */ DRESULT disk_read ( 3. 4. 5. 6. 7. )BYTE drv,/* Physical drive nmuber (0..) */BYTE *buff, /* Data buffer to store read data */ DWORD sector, BYTE count /* Sector address (LBA) *//* Number of sectors to read (1..255) */1. /* Write Sector(s) */ 2. #if _READONLY == 0 3. DRESULT disk_write ( 4. 5. 6. 7. 8. ) BYTE drv, const BYTE *buff, DWORD sector, BYTE count /* Physical drive nmuber (0..) */ /* Data to be written */ /* Sector address (LBA) */ /* Number of sectors to write (1..255) */1. 2. 3. 4. 5. 6./* Miscellaneous Functions DRESULT disk_ioctl ( BYTE drv, BYTE ctrl, void *buff ) /* Physical drive nmuber (0..) */ /* Control code */ /* Buffer to send/receive control data */*/这些函数都是操作底层介质的函数,都需要用户自己实现,然后 FATFS 的 应用函数就可以调用这些函数来操作我们的卡了。关于这些底层介质函数是如 何实现的,请参考源码,这里就不贴出来了。 在 diskio.c 的最后我们还得提供了获取时间的函数,因为 ff.c 中调用了这个 函数,而 FATFS 库又没有给出这个函数的原型,所以需要用户实现,不然会编 译出错,函数体为空即可(也可以为它加载 STM32 的 RTC 驱动):野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 34 页- 实现好底层介质的操作函数之后,我们就可以回到应用层了,下面我们从 main 函数开始看起。有关系统初始化和串口初始化这部分请参考前面的教程, 这里不再详述。 首先我们调用函数 disk_initialize( 0 ); 将我们的底层硬件初始化 好,这一步非常重要,如果不成功的话,接下来什么都干不了。f_open( &fsrc , &0:/Demo.TXT& , FA_CREATE_NEW | FA_WRITE); 将在刚刚开辟的工作区的盘符 0 下打开一个名为 Demo.TXT 的文件,以只写的方 式打开,如果文件不存在的话则创建这个文件。并将 Demo.TXT 这个文件关联 到 fsrc 这个结构指针,以后我们操作文件就是通过这个结构指针来完成的。f_write(&fsrc, textFileBuffer, sizeof(textFileBuffer), &br);将缓冲区的数据写到刚刚打开的 Demo.TXT 文件中。写完之后调用f_close(&fsrc);。关闭文件, f_open(&fsrc, &0:/Demo.TXT&, FA_OPEN_EXISTING | FA_READ);以只读的方式打开刚刚的文件。f_read( &fsrc, buffer, sizeof(buffer), &br ); 将文件的内容读到缓冲区,然后调用 printf(&\r\n %s &, buffer);将数据打印到电脑的超级终 端。 最后调用 f_close(&fsrc);关闭文件。当被打开的文件操作完成之后都要 调用 f_close();将它关闭,就像一块动态分配的内存在用完之后都要调用free()来将它释放。这里涉及到了 FATFS 文件系统库函数的操作,如果你学过 LINUX 系统调用 的话,操作这些函数将是非常简单,没有学过的话也没太大的关系,因为 FATFS 源码目录 doc 这个文件夹中提供了每个应用函数的用法,如 f_mount():野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 35 页- 2.7 实验现象 将TheOne STM32 开发板供电(DC5V),插上 JLINK,插上串口线(两头都是 母 的交叉线),插上 MicroSD 卡( TheOne用的是 1G,4G 的也已经测试通过 ), 打开 超级终端,配置超级终端为 -N-1,将编译好的程序下载到开发 板,即 可看到超级终端打印出如下信息:野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 36 页-
4、液晶触摸画板4.1 实验简介 本实验向大家介绍如何使用 STM32 的 FSMC 接口驱动 LCD 屏,及使用触 摸屏控制器检测触点坐标。 4.2 LCD 控制器简介 LCD,即液晶显示器,因为其功耗低、体积小,承载的信息量大,因而被 广泛用于信息输出、与用户进行交互,目前仍是各种电子显示设备的主流。 因为 STM32 内部没有集成专用的液晶屏和触摸屏的控制接口,所以在显示 面板中应自带含有这些驱动芯片的驱动电路(液晶屏和触摸屏的驱动电路是独立 的),STM32 芯片通过驱动芯片来控制液晶屏和触摸屏。以TheOne 3.2 寸液晶屏 (240*320)为例,它使用 LIL9320 芯片控制液晶屏,通过 ads7843芯片控制触 摸 屏。4.2.1 LIL9320 控制器结构 液晶屏的控制芯片内部结构非常复杂,见 错误!未找到引用源。。最主要 的是位于中间 GRAM(Graphics RAM),可以理解为 显存 。GRAM 中每个存储单 元都对应着液晶面板的一个像素点。它右侧的各种模块共同作用把 GRAM 存储 单元的数据转化成液晶面板的控制信号,使像素点呈现特定的颜色,而像素点 组合起来则成为一幅完整的图像。 框图的左上角为 LIL9320 的主要控制信号线和配置引脚,根据其不同状态 设置可以使芯片工作在不同的模式,如每个像素点的位数是 6、16 还是 18 位; 使用 SPI 接口还是 8080 接口与 MCU 进行通讯;使用 8080 接口的哪种模 式。 MUC 通过 SPI 或 8080 接口与 LIL9320 进行通讯,从而访问它的控制寄存 器 (CR)、地址计数器(AC)、及 GRAM。 在 GRAM 的左侧还有一个 LED 控制器(LED Controller)。LCD 为非发光性的 显示装置,它需要借助背光源才能达到显示功能,LED 控制器就是用来控制液 晶屏中的 LED 背光源。图 0-1 LIL9320 控制器内部框图4.2.2 像素点的数据格式 图像数据的像素点由红(R)、绿(G)、蓝(B)三原 色组成,三原色根据其深浅 程度被分为 0~255 个级别,它们按不同比例的混合可以得出各种色彩。如 R: 255,G255,B255 混合后为白色。根据描述像素点数据的长度,主要分为 8、 16、24 及 32 位。如以 8 位来描述的像素点可表示 28=256 色,16 位描述的为 216=65536 色,称为真彩色,也称为 64K 色。实际上受人眼对颜色的识别能力 的限制, 16 位色与 12 位色已经难以分辨了。 LIL9320 最高能够控制 18 位的 LCD,但为了数据传输简便,我们采用它的 16 位控制模式,以 16 位描述的像素点。按照标准格式,16 位的像素点的三原 色描述的位数为 R:G:B =5:6:5,描述绿色的位数较多是因为人眼对绿色 更为敏感。16 位的像素点格式见图 0-2。图 0-2 16 位像素点格式图中的是默认 18 条数据线时,像素点三原色的分配状况,D1~D5 为蓝色, D6~D11 为绿色,D13~D17 为红色。这样分配有 D0 和 D12 位是无效的。若使 用 16 根数据线传送像素点的数据,则 D0~D4 为蓝色,D5~D10 为绿色, D11~D15 为红色,使得刚好使用完整的 16 位。 RGB 比例为 5:6:5 是一个十分通用的颜色标准,在 GRAM 相应的地址中填 入该颜色的编码,即可控制 LCD 输出该颜色的像素点。如黑色的编码为 0x0000,白色的编码为 0xffff,红色为 0xf800。 4.2.3 LIL9320 的通讯时序 目前,大多数的液晶控制器都使用 8080 或 6800 接口与 MCU 进行通讯, 它们的时序十分相似,TheOne以 LIL9320 使用的 8080 通讯时序进行分析,实际 上 LIL9320 也可以使用 SPI 接口来控制。 LIL9320 的 8080 接口有 5 条基本的控制信号线: 1. 用于片选的 CSX 信号线; 2. 用于写使能的 WRX 信号线; 3. 用于读使能的 RDX 信号线; 4. 用于区分数据和命令的 D/CX 信号线; 5. 用于复位的 RESX 信号线。 其中带 X 的表示低电平有效。除了控制信号,还有数据信号线,它的数目 不定,可根据 LIL9320 框图中的 IM[3:0]来设定,这部分一般由制作液晶屏的厂 家完成。为便于传输像素点数据,TheOne使用的液晶屏设定为 16 条数据线 D[15:0]。使用 8080 接口的写命令时序图见错误!未找到引用源。。图 0-3 使用 18 条数据线的 8080 接口写命令时序由图可知,写命令时序由 CSX 信号线拉低开始, D/CX 信号线也置低电平 表示写入的是 命令地址 (可理解为命令编码,如软件复位命令:0x01),以 WRX 信号线为低,RDX 信号为高表示数据传输方向为写入,同时,在数据线[17:0] 输出命令地址,在第地二个传输阶段传送的为命令的参数,所以 D/CX 要置高电平,表示写入的是命令数据。当我们需要向 GRAM 写入数据的时候,把 CSX 信号线拉低后,把 D/CX 信号线置为高电平 ,这时由 D[17:0]传输的数据则会被 LIL9320 保存至它的 GRAM中。 4.3 用 STM32 驱动 LCD LIL9320 的 8080 通讯接口时序可以由 STM32 使用普通 I/O 接口进行模 拟,但这样效率较低,它提供了一种特别的控制方法――使用 FSMC 接口。 FSMC 简介 FSMC(flexible static memory controller),译为静态存储控制器。可用于 STM32 芯片控制 NOR FLASH、PSRAM、和 NAND FLASH 存储芯片。其结构见 图 0-4。图 0-4 FSMC 结构图我们是使用 FSMC 的 NOR\PSRAM 模式控制 LCD,所以我们重点分析框图 中 NOR FLASH 控制信号线部分。控制 NOR FLASH 主要使用到如下信号线: 图 0-5 FSMC 控制 NOR FLASH 的信号线根据 STM32 对寻址空间的地址映射,见前面的错误!未找到引用源。,地址 0x ~0x9FFF FFFF 是映射到外部存储器的 ,而其中的 0x6000FFF FFFF 则是分配给 NOR FLASH、PSRAM 这类可直接寻址的器 件。当 FSMC 外设被配置为正常工作,并且外部接了 NOR FLASH,这时若向 0x 地址写入数据 0xffff,FSMC 会自动在各信号线上产生相应的电平 信号,写入数据。该过程的时序图见图 0-6。与 8080 类 似的信号线 D/CXCSXRDXWRXD[15:0]图 0-6 FSMC 写 NOR 时序图它会控制片选信号 NE[X] 选择相应的某块 NOR 芯片,然后使用地址线A[25:0]输出 0x,在 NEW 写使能信号线上发出写使能信号,而要写 入的数据信号 0xffff 则从数据线 D[15:0]输出,然后数据就被保存到 NOR FLASH 中了。 用 FSMC 模拟 8080 时序 在图 0-6 的时序图中 NADV 信号是在地址、信号线复用时作为锁存信号 的,在此,我们略它。然后读者会发现,这个 FSMC 写 NOR 时序是跟 8080 接 口的时序(见图 0-3)是十分相似的,对它们的信号线对比如下: 8080 信号线 功能 FSMC-NOR 信 号线 CSX WRX RDX D[15:0] D\CX 片选信号 写使能 读使能 数据信号 数据/命令选 择 前四种信号线都是完全一样的,仅在 8080 的数据\命令选择线与 FSMC 的 地址信号线有区别。为了模拟出 8080 时序,我们把 FSMC 的 A0 地址线(也可以 使用其它地址线)连接 8080 的 D\CX,即 A0 为高电平 时,数据线 D[15:0]的信 号会被理解 LIL9320 为数值,若 A0 为 低电平时,传输的信号则会被理解为命 NEx NWR NOE D[15:0] A[25:0] 片选 写使能 读使能 数据信号 地址信号 功能令。也就是说,当向地址为 0x6xxx xxx1、0x6xxx xxx3、0x6xxx xxx5?这些奇数地址 写入数据时,地址线 A0(D/CX)会为高电平,这个数据被理解为数值;若向 0x6xxx xxx0 、0x6xxx xxx2、0x6xxx xxx4?这些偶数地址 写入数据时,地 址线 A0(D/CX)会为低电平,这个数据会被理解为命令。 有了这个基础,只要我们在代码中利用指针变量,向不同的地址单元写入 数据,就能够由 FSMC 模拟出的 8080 接口向 LIL9320 写入控制命令或 GRAM 的 数据了。 4.3.1 触摸屏感应原理 触摸屏常与液晶屏配套使用,组合成为一个可交互的输入输出系统。除了 熟悉的电阻、电容屏外,触摸屏的种类还有超声波屏、红外屏。由于电阻屏的 控制系统简单、成本低,且能适应各种上恶劣环境,被广泛采用。 电阻触摸屏的基本原理为分压,它由一层或两层阻性材料组成,在检测坐 标时,在阻性材料的一端接参考电压 Vref,另一端接地,形成一个沿坐标方向 的均匀电场 。当触摸屏受到挤压时,阻性材料与下层电极接触,阻性材料被分 为两部分,因而在触摸点的电压,反映了触摸点与阻性材料的 Vref 端的距离,而且为线性关系 ,而该触点的电压可由 ADC 测得。更改电场方向,以同样的方法,可测得另一方向的坐标。 4.3.2 ADS7843 触摸屏控制器 ADS7843 是专用在四线电阻屏的触摸屏控制器,MCU 可通过 SPI 接口向它写 入控制字,由它测得 X、Y 方向的触点电压返回给 MCU。见图 0-7。图 0-7 ADS7843 与电阻屏的连接图图中,电阻屏两层阻性材料的两端分别接入到 ADS7843 的 X+、X-和 Y+、 Y-。当要测量 X 坐标时,MCU 通过 SPI 接口写命令到 ADS7843,使它通 过内部 的模拟开关使 X+、X-接通电源,于是在电阻屏的 X 方向上产生一个匀强 电场; 把 Y+、Y-连接到 ADS7843 的 ADC。当电阻屏被触摸时,上、下两层的 阻性材料接触,在 PENIRQ 引脚产生一个 中断信号,通知 MCU。该触点的电压 由 Y+或 Y-(此时的 Y+Y-电阻很小,可忽略)引入到 ADC 进行测量,MCU 读取该 电压,进行软件转换,就可以测得触点 X 方向的坐标。同理可以测得 Y 方向的 坐标。 4.4 实验讲解 4.4.1 实验描述及工程文件清单 实验描述 TheOne STM32 开发板驱动配套的 3.2 寸液晶、触摸屏,使 用 FSMC 接口控制该屏幕自带的液晶控制器 LIL9320,使用 SPI 接口与触摸屏控制器 ADS7843 通讯。驱动成功后可在屏幕上 使用基本的触摸绘图功能。 硬件连接TFT 数据线PD14-FSMC-D0 PD15-FSMC-D1 PD0-FSMC-D2 PD1-FSMC-D3 PE7-FSMC-D4 PE8-FSMC-D5 PE9-FSMC-D6 PE10-FSMC-D7 PE11-FSMC-D8 PE12-FSMC-D9 PE13-FSMC-D10 PE14-FSMC-D11 PE15-FSMC-D12 PD8-FSMC-D13 PD9-FSMC-D14 PD10-FSMC-D15 ----LCD-DB0 ----LCD-DB1 ----LCD-DB2 ----LCD-DB3 ----LCD-DB4 ----LCD-DB5 ----LCD-DB6 ----LCD-DB7 ----LCD-DB8 ----LCD-DB9 ----LCD-DB10 ----LCD-DB11 ----LCD-DB12 ----LCD-DB13 ----LCD-DB14 ----LCD-DB15 TFT 控制信号线PD4-FSMC-NOE PD5-FSMC-NEW PD7-FSMC-NE1 PD11-FSMC-A16 PE1-FSMC-NBL1 PD13-FSMC-A18 ----LCD-RD ----LCD-WR ----LCD-CS ----LCD-DC ----LCD-RESET ----LCD-BLACK-LIGHT触摸屏 ADS7843 控制线PB13-SPI1-SCK PB15-SPI1-MOSI PB14-SPI1-MISO PB12 PB0 用到的库文件 startup/start_stm32f10x_hd.c CMSIS/core_cm3.c CMSIS/system_stm32f10x.c ----ADS7843-SPI -SCK ----ADS7843-SPI - MOSI ----ADS7843-SPI C MISO ----ADS7843-SPI-CS ----ADS7843- INT_IRQFWlib/misc.c FWlib/stm32f10x_spi.c FWlib/stm32f10x_rcc.c FWlib/stm32f10x_exti.c FWlib/stm32f10x_gpio.c FWlib/stm32f10x_fsmc.c用户编写的文件 USER/main.c USER/stm32f10x_it.cUSER/lcd.c USER/SysTick.c USER/lcd_botton.c USER/Touch.c TheOne STM32 开发板 3.2 寸 LCD 硬件连接图4.4.2 配置工程环境 本 LCD 触摸屏画板实验中我们用到了 GPIO、RCC、SPI、EXTI、FSMC 外 设, 所以我们先要把以下库文件添加到工程: stm32f10x_gpio.c 、stm32f10x_rcc.c、stm32f10x_spi.c、stm32f10x_exti.c、stm32f10x_fsmc.c。由于在 ADS7843 的触摸检测中使用了中断,所以还要把 misc.c 文件添加进工 程。 本工程使用了旧的用户文件 SysTick.c,用作定时,把它添加到新工程之 中,并新建 lcd_botton.c、lcd.c、Touch.c 及相应的头文件。其中 lcd_botton.c 文件定义了最底层的 LCD 控制函数,LCD 上层的函数如画点、显示字符等位于lcd.c 文件中。最后在 stm32f10x_conf.h 中把使用到的 ST 库的头文件注释去掉。1. /** 2. ********************************************************** 3. * @file Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h 4. * @author MCD Application Team 5. * @version V3.5.0 6. * @date 08-April-2011 7. * @brief Library configuration file. 8. ******************************************************/ 9. 10. #include &stm32f10x_exti.h& 11. #include &stm32f10x_fsmc.h& 12. #include &stm32f10x_gpio.h& 13. #include &stm32f10x_rcc.h& 14. #include &stm32f10x_spi.h& 15. #include &misc.h&4.4.3 main 文件 从本工程的 main 文件分析代码的执行流程:1. /* 2. * 函数名:main 3. * 描述 :主函数 4. * 输入 :无 5. * 输出 :无 6. */ 7. int main(void) 8. { 9. SysTick_Init(); /*systick 初始化*/ 10. LCD_Init(); /*LCD 初始化*/ 11. Touch_init(); /*触摸初始化*/ 12. 13. while(Touchl_Calibrate() !=0); /*等待触摸屏校准完毕*/ 14. Init_Palette(); /*画板初始化*/ 15. 16. while (1) 17. { 18. if(touch_flag == 1) /*如果触笔按下了*/ 19. { 20. /*获取点的坐标*/ 21. if(Get_touch_point(&display, Read_2046_2(), &touch_para ) !=DISABLE) 22. { 23. /*画点*/ 24. Palette_draw_point(display.x,display.y); /*画点*/ 25. } 26. } 27. } 28. }其执行流程如下: 1. 调用 SysTick_Init()、LCD_Init()、Touch_init() 初始化了 STM32 的 Systick、FSMC、SPI 外设,并用 FSMC 和 SPI 接口初始化了 LIL9320 和 ADS7843 控制器。 2. 调用 Touch1_Calibrate()函数进行触摸屏校准,使得触摸屏与液晶屏的 坐标匹配。 3. 调用 Init_Palette()函数初始化触摸画板的应用程序,使得在 LCD 上显示 画板界面,并能够正常响应触摸屏的信号。 4. 第 16~27 行的 while 循环,通过不断检测触笔按下标志 touch_flag,判 断触摸屏是否被触笔按下。触摸屏控制器 ADS7843 检测到触笔信号野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 66 页- 时,由它的 PENIRQ 引脚触发 STM32 的中断,在中断服务函数中对touch_flag 标志置 1。5. 第 21 行,若检测到触笔按下,调用 Get_touch_point()函数读取ADS7843 的寄存器,获得与触点的 X、Y 坐标相关的电压信号,转化成LCD 的 X、Y 坐标。 6. 获取了触点坐标后,使 LCD 液晶屏在该坐标点显示为对应的颜色。 7. 循环触点捕捉、画点过程,就实现了触摸画板的功能。 4.4.5 初始化 FSMC 模式 4.4.5.1 初始化液晶屏流程 在 main 函数中调用的 LCD_Init() 函数,它对液晶控制器 LIL9320 用到的 GPIO、FSMC 接口进行了初始化,并且向该控制器写入了命令参数,配置好了 LCD 液晶屏的基本功能。其函数定义位于 lcd_botton.c 文件,如下:1. /***************************************** 2. * 函数名:LCD_Init 3. * 描述 :LCD 控制 I/O 初始化 4. * LCD FSMC 初始化 5. * LCD 控制器 HX8347 初始化 6. * 输入 : 无 7. * 输出 :无 8. * 举例 :无 9. * 注意 :无 10. ******************************************/ 11. void LCD_Init(void) 12. { 13. 14. 15. LCD_GPIO_Config(); //初始化使用到的 GPIO 16. LCD_FSMC_Config(); //初始化 FSMC 模式 17. LCD_Rst(); //复位 LCD 液晶屏 18. Lcd_init_conf(); //写入命令参数,对液晶屏进行基本的初始化配置 19. Lcd_data_start(); //发送写 GRAM 命令 20. for(i=0; i&(320*240); i++) 21. { 22. LCD_WR_Data(GBLUE); //发送颜色数据,初始化屏幕为 GBLUE 颜色 23. 24. } 25. }LCD_Init() 函数执行后,最直观的结果是使 LCD 整个屏幕显示编码为0X07FF 的 GBLUE 颜色。野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 67 页- 函数中调用的 LCD_GPIO_Config()主要工作是把液晶屏(不包括触摸屏)中 使用到的 GPIO 引脚和使能外设时钟,除了 背光、复位 用的 PD13 和 PD1 设置 为 通用推挽输出 外,其它的与 FSMC 接口相关的地址信号、数据信号、控制信号的端口均设置为复用推挽输出。4.4.5.2 初始化 FSMC 模式 接下来 LCD_Init() 函数调用 LCD_FSMC_Config() 设置 FSMC 的模式,我们 的目的是使用它的 NOR FLASH 模式模拟出 8080 接口,在 LCD 接口中我们使用 的是 FSMC 地址线 A16 作为 8080 的 D/CX 命令选择信号的。LCD_FSMC_Config()具体代码如下:1. /******************************************* 2. * 函数名:LCD_FSMC_Config 3. * 描述 :LCD FSMC 模式配置 4. * 输入 : 无 5. * 输出 :无 6. * 举例 :无 7. * 注意 :无 8. *********************************************/ 9. void LCD_FSMC_Config(void) 10. { 11. FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitS 12. FSMC_NORSRAMTimingInitTypeD 13. 14. 15. p.FSMC_AddressSetupTime = 0x02; //地址建立时间 16. p.FSMC_AddressHoldTime = 0x00; //地址保持时间 17. p.FSMC_DataSetupTime = 0x05; //数据建立时间 18. p.FSMC_BusTurnAroundDuration = 0x00; //总线恢复时间 19. p.FSMC_CLKDivision = 0x00; //时钟分频 20. p.FSMC_DataLatency = 0x00; //数据保持时间 21. p.FSMC_AccessMode = FSMC_AccessMode_B; //在地址数\据线不复用的情况 下,ABCD 模式的区别不大 22. //本成员配置只有使用扩展模式 才有效 23. 24. 25. FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1; //NOR FLASH 的 BANK1 26. FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMu x_D //数据线与地址线不复用 27. FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR; //存储器类型 NOR FLASH 28. FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWi dth_16b; //数据宽度为 16 位 29. FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessM ode_D //使用异步写模式,禁止突发模式 30. FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSigna lPolarity_L //本成员的配置只在突发模式下有效,等待信号极性为低野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 68 页- 31.FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_D //禁止非对齐突发模式 32. FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalA ctive_BeforeWaitS //本成员配置仅在突发模式下有效。NWAIT 信号在什么时期 产生 33. FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_D //本成员的配置只在突发模式下有效,禁用 NWAIT 信号 34. FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_D //禁止突发写操作 35. FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperatio n_E //写使能 36. FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_D //禁止扩展模式,扩展模式可以使用独立的读、写模式 37. FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; //配置读写时序 38. FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; //配置写时序 39. 40. 41. FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); 42. 43. /* 使能 FSMC Bank1_SRAM Bank */ 44. FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE); 45. }本函数主要使用了两种类型的结构体对 FSMC 进行配置,第一种 为 FSMC_NORSRAMInitTypeDef 类型的结构体主要用于 NOR FLASH 的模式配 置,包括存储器类型、数据宽度等。另一种的类型为FSMC_NORSRAMTimingInitTypeDef,这是用于配置 FSMC 的 NOR FLASH 模式下读写时序中的地址建立时间、地址保持时间等,代码中用它定义了结构体 p, 这个第二种类型的结构体在前一种结构体中被指针调用。 下面先来分析 FSMC_NORSRAMInitTypeDef 的结构体成员: 1. FSMC_Bank 用于 选择外接存储器的区域 (或地址),见图 0-8 FSMC 存储块, STM32 的存储器映射中,把 0xx9fff ffff 的地址都映射到 被 FSMC 控制的外存储器中,其中属于 NOR FLASH 的为 0xx6fff ffff。而属于 NOR FLASH 的这部分地址空间又被分为 4 份,每份大小为 64MB,编号为 BANK1 ~BANK4。 分 BANK 是由 FSMC 寻址范围决定的,该接口的地址线最多为 26 条,即最大寻址空间为 226 =64MB。为了扩展寻址空间,可把地址与数据线与多片 NOR FLASH 并联,由不同的片选信号 NE[3:0]区分不同的 BANK。野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 69 页- 在本实验中,我们使用的是 FSMC 的信号线 NE1 作为控制 8080 的 CSX 片选信号,所以我们把本成配置为 FSMC_Bank1_NORSRAM1 (NE1 片选 BANK1)。图 0-8 FSMC 存储块2. FSMC_DataAddressMux本成员用于配置FSMC 的数据线与地址线是否复用 。FSMC 支持数据与地址线复用或非复用两种模式。在 非复用模式 下 16 位数据线及 26 位地址线分开始用; 复用模式 则低 16 位数据/地址线复用。在复用模式 下,推荐使用地址锁存器以区分数据与地址。当 NADV 信号线为低时, 复用信号线 ADx(x=0…15)上出现地址信号 Ax,当 NADV 变高时,ADx 上出现数据信号 Dx。 本实验中用 FSMC 模拟 8080 接口,地址线 A16 提供 8080 的D/CX 信号,实际上就只使用了这一条地址线,I\O 资源并不紧张,所以把本成员配置为 FSMC_DataAddressMux_Disable (非复用模式)。 3. FSMC_MemoryType野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 70 页- 本成员用于配置 FSMC 外接的存储器的类型 ,可被配置为 NOR FLASH 模式、PSARM 模式及 SRAM 模式。在本实验的应用中,由于 NOR FLASH 模式的时序与 8080 更接近,所以本 结构体被配置为 FSMC_MemoryType_NOR (NOR FLASH 模式)。 4. FSMC_MemoryDataWidth 本成员用于设置 FSMC 接口的 数据宽度 ,可被设置为 8Bit 或 16bit。对于 16 位宽度的外部存储器。在 STM32 地址映射到 FSMC 接口的结构中,HADDR 信号线是需要转换到外部存储器的内部 AHB 地址线,是字节地址。 若存储器 的数据线宽为 8Bit,FSMC 的 26 条地址信号线 FSMC_A[25:0]直 接可以引入到与 AHB 相连的 HADDR[25:0], 26 条字节地址信号线最大寻址空 间为 64MB。见图 0-9。图 0-9 外部存储器地址若存储器的数据线宽 16Bit,则存储器的地址信号线是 半字地址(16Bit) 。 为了使 HADDR 的 字节地址 信号线与存储器匹配,FSMC 的 25 条地址信号线 FSMC_A[24:0]与 HADDR[25:1]相连,由于变成了 半字地址(16Bit), 仅需要 25 条 半字字地址 信号线就达到最大寻址空间 64MB。正因地址线的不对称相连,16bit 数据线宽下,实际的访问地址为右移一位之后的地址。 本实验中 8080 接口采用 16bit 模式,所以我们把本成员配置为FSMC_MemoryDataWidth_16b,由于地址线不对称相连,这会影响到我们用 地 址信号线 A16 控制的 8080 接口的 D/CX 信号。5. FSMC_BurstAccessMode 本成员用于配置 访问模式 。FSMC 对存储器的访问分为异步模式和突发 模式(同步模式)。在异步模式下,每次传送数据都需要产生一个确定的地 址,而突发模式可以在开始始提供一个地址之后,把数据成组地连续写 入。野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 71 页- 本实验中使用 FSMC 模拟 8080 端口,更适合使用异步模式,因而向本成 员赋值为 FSMC_WriteBurst_Disable。 6. 突发模式参数配置 代码中的 30~34 行,都是关于使用突发模式时的一些参数配置,这些成 员为: FSMC_WaitSignalPolarity(配置等待信号极性)、FSMC_WrapMode(配置是否使用非对齐方式)、FSMC_WaitSignalActive(配 置等待信号什么时期产生)、FSMC_WaitSignal(配置是否使用等待信 号)、FSMC_WriteBurst(配置是否允许突发写操作),这些成员均需要在突发模式开启后配置才有效。 这些成员在开启突发模式时才有效,本实验使用的是异步模式,所以这 些成员的参数没有意义。 7. FSMC_WriteOperation 本成员用于配置 写操作使能 ,如果禁止了写操作,FSMC 不会产生写时 序,但仍可从存储器中读出数据。 本实验需要写时序,所以向本成员赋值为FSMC_WriteOperation_Enable(写使能)8. FSMC_ExtendedMode 本成员用于配置是否使用扩展模式,在扩展模式下,读时序和写时序可以 使用独立时序模式。如读时序使用模式 A,写时序使用模式 B,这些 A、B、 C、 D 模式实际上差别不大,主要是在使用数据/地址线复用的情况下,NADV 信 号产生的时序不一样,具体的时序图可查阅《STM32 参考手册》。 本实验中数据/地址线不复用,所以读写时序中不同的 NADV 信号并没影 响,禁止使用扩展模式 SMC_ExtendedMode_Disable。 9. FSMC_ReadWriteTimingStruct 及 FSMC_WriteTimingStruct 这两个参数分别用来设置 FSMC 的读时序及写时序的时间参数。若使用了 扩展模式,则前者配置的是读时序,后者为写时序;若禁止了扩展模式,则读 写时序都使用 FSMC_ReadWriteTimingStruct 结构体中的参数。野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 72 页- 在配置这两个参数时,使用的是类型 FSMC_NORSRAMTimingInitTypeDef 时序初始化结构体,对这种类型结构体的成员进行赋值。它的成员分别有:FSMC_AddressSetupTime(地址建立时间)、FSMC_AddressHoldTime(地址保持 时间)、FSMC_DataSetupTime(数据建立时间)、FSMC_DataLatency(数据保持 时间) 、FSMC_BusTurnAroundDuration(总线恢复时间)、FSMC_CLKDivision(时 钟分频)、FSMC_AccessMode(访问模式)。 对以上各个时间成员赋的数值 X 表示X 个时钟周期,它的时钟是由 HCLK 经过成员时钟分频得来的,该分频值在成 员 FSMC_CLKDivision(时钟分频)中设置。其中 FSMC_AccessMode(访问模式)成 员的设置只在开启了扩展模式才有效,而且开启了扩展模式后,读时序和写时 序的设置可以是独立的。 本实验中的时序设置是根据 LIL9320 的 datasheet 设置的,调试的时候可 以先把这些值设置得大一些,然后慢慢靠近 datasheet 要求的最小值,这样会 取得比较好的效果。时序的参数设置对 LCD 的显示效果有一定的影响。 配置完初始化结构体后,要调用库函数 FSMC_NORSRAMInit() 把这些配置 参数写到控制寄存器,还要调用 FSMC_NORSRAMCmd() 使能 BANK1。如果是 使用 FSMC 配置其它存储器如 NAND FLASH,要使用其它的库函数及初始化结 构体。 4.4.6 FSMC 模拟 8080 读写参数、命令 回到 LCD_Init() 函数的执行流程,初始化完成 FSMC 接口后,就可以使用 它控制 LIL9320 了。在 LCD_Init() 中调用了 Lcd_init_conf()函数向 LIL9320 写入 了一系列的控制参数:1. /********************************** 2. * 函数名:Lcd_init_conf 3. * 描述 :LIL9320 LCD 寄存器初始配置 4. * 输入 : 无 5. * 输出 :无 6. * 举例 :无 7. * 注意 :无 8. *************************************/ 9. void Lcd_init_conf(void) 10. { 11. DEBUG_DELAY(); 12. LCD_LIL9320_CMD(0xCF); 13. LCD_LIL9320_Parameter(0x00);野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 73 页- 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29.LCD_LIL9320_Parameter(0x81); LCD_LIL9320_Parameter(0x30); DEBUG_DELAY(); LCD_LIL9320_CMD(0xED); LCD_LIL9320_Parameter(0x64); LCD_LIL9320_Parameter(0x03); LCD_LIL9320_Parameter(0x12); LCD_LIL9320_Parameter(0x81); DEBUG_DELAY(); LCD_LIL9320_CMD(0xE8); LCD_LIL9320_Parameter(0x85); LCD_LIL9320_Parameter(0x10); LCD_LIL9320_Parameter(0x78); // ??????此处省略几十行????本函数十分长,由于篇幅问题,以上只是该函数其中的一部分,省略部分 的代码也是这样的模板,只是写入的命令和参数不一样而已,这些命令和参数 设置了像素点颜色格式、屏幕扫描方式、横屏\竖屏等初始化配置,这些命令的 意义从 LIL9320 的 datasheet 命令列表中可以查到。该函数通过调用LCD_LIL9320_CMD() 写入命令 ,用 LCD_LIL9320_Parameter()写入参数。它们 实质是两个宏:1. /*******************************************************/ 2. #define LCD_LIL9320_CMD(index) LCD_WR_REG(index) 3. #define LCD_LIL9320_Parameter(val) LCD_WR_Data(val) 4. 5. /****为了移植方便,上面的宏只是封装,以下才是最底层的宏**********/ 6. /* 选择 BANK1-BORSRAM1 连接 TFT,地址范围为 0XX63FFFFFF 7. * FSMC_A16 接 LCD 的 DC(寄存器/数据选择)脚 8. * 16 bit =& FSMC[24:0]对应 HADDR[25:1] 9. * 寄存器基地址 = 0X. * RAM 基地 址 = 0X = 0X^16*2 = 0X + 0X20000 = 0X. * 当选择不同的地址线时,地址要重新计算。 12. */ 13. 14. #define Bank1_LCD_D ((u32)0x) //Disp Data ADDR 15. #define Bank1_LCD_C ((u32)0x) //Disp Reg ADDR 16. 17. /*选定 LCD 指定寄存器(命令编码)*/ 18. #define LCD_WR_REG(index) ((*( IO u16 *) (Bank1_LCD_C)) = ((u16)inde x)) 19. 20. /*往 LCD 写入数据*/ 21. #define LCD_WR_Data(val) ((*( IO u16 *) (Bank1_LCD_D)) = ((u16)(v al)))这部分是 FSMC 模拟 8080 接口的精髓。野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 74 页- 读写参数、命令 先来看第 21 行的宏, LCD_WR_Data(val) ,这是一个带参宏,用于向 LCD 控制器写入参数,参数为 val。它的宏展开为:1. ((*( IO u16 *) (Bank1_LCD_D)) = ((u16)(val)))宏展开中的 (Bank1_LCD_D) 是一个在第 14 行定义的宏,它的值为 0x,实质是一个地址,这个地址的计算在后面介绍。(IO u16 *) (Bank1_LCD_D) 表示把 (Bank1_LCD_D) 强制转换成一个 16 IO u16 *) (Bank1_LCD_D)) 表示再对这个地址作‖ * ‖指针运位的地址。((*(算,取该指针对象的内容,并把它的内容赋值为 = ((u16)(val)))。所以整个宏 的操作就是:把参数 val 写入到地址为 0x 的地址空间。 由于这个地址被 STM32 映射到外存储器,所以会由 FSMC 外设以访问 NOR FLASH 的形式、时序,在地址线上发出 0x 地址信号,在数据线 上发出 val 数据信号,写入参数到外存储器中。而 FSMC 接口又被我们模拟成 了 8080 接口,最终 val 被 8080 接口理解为参数,传输到 LIL9320 控制器中。 计算地址 见图 0-10。计算地址前,再明确一下在本实验中,使用的是 FSMC_NE1 作 为 8080_CS 片选信号,以 FSMC_A16 作为 8080_D/CX 数据/命令信号(图中为 RS,意义相同)。图 0-10 FSMC 与 8080 端口连接简图野火淘宝店:http://firestm32.taobao.com (点我) 有疑问就到:www.amobbs.com/forum-1008-1.html (点我)-第 75 页- 按这种连接时,FSMC_NE1 为 低电平 、FSMC_A16 为高电平,表示通过 D[15:0]发送\接收的数据被 8080 接口解释为 参数(数值) ,当我们访问 0x60020000 这个地址的时候,正好符合这个条件。该地址的计算过程如下: 由于选择的是使用 FSMC_NE1 片选信号线,片选的为 BANK1,所以基地址 为 0x。要把地址线 FSMC_A16 置为高电平,可以采用下列算式:1. 0x |= 1&&16; //结果 = 0x

我要回帖

更多关于 stm32 u8 u16什么意思 的文章

 

随机推荐