- Printf的重定向
因为printf是c++中的库函数,要使用printf输出到串口,需要重定向,将printf定向到HAL_UART_Transmit。
新建一个retarget.c文件。
#include "stdio.h" #include "stm32f1xx_hal.h" #include "usart.h" #pragma import(__use_no_semihosting_swi) #pragma import(__use_no_semihosting) void _sys_exit(int x) { x = x; } struct __FILE { int handle; /* Whatever you require here. If the only file you are using is */ /* standard output using printf() for debugging, no file handling */ /* is required. */ }; /* FILE is typedef’ d in stdio.h. */ FILE __stdout; void _ttywrch(int ch){};
int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff); return ch; } |
网上找的代码或多或少运行不起来,搞了好久才调通,以上是调通后的代码,几乎只要是调试keil的工程都需要用到。可以保存下来重复使用。
- delay微秒
delay是一个延时函数。Stm32的HAL库提供了一个HAL_Delay的函数,但是只能延时毫秒。延时微秒,需要根据stm32的系统时钟计算,网上一大堆,但貌似或多或少有点问题,不能拿来即用。
新建一个delay.c和delay.h的文件。
#include "delay.h" //仿原子延时,不进入systic中断 void delay_us(uint32_t nus) { uint32_t temp; SysTick->LOAD = 9*nus; SysTick->VAL=0X00;//清空计数器 SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源 do { temp=SysTick->CTRL;//读取当前倒计数值 }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达 SysTick->CTRL=0x00; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } void delay_ms(uint16_t nms) { uint32_t temp; SysTick->LOAD = 9000*nms; SysTick->VAL=0X00;//清空计数器 SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源 do { temp=SysTick->CTRL;//读取当前倒计数值 }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达 SysTick->CTRL=0x00; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } |
#include "stm32f1xx_hal.h" 或者直接#include “main.h”,规范一点 void delay_us(uint32_t nus); |
之后,在main.c中引用delay.h就可以调用delay_us函数。
- 遇到的问题
1.编译报错
w25qxx.c(69): error: #20: identifier "SPI_I2S_FLAG_TXE" is undefined
解:SPI_I2S_FLAG_TXE这个寄存器没有定义。网上使用的都是这个寄存器,但是在spi的库函数里找不到这个。看了半天,找到一个SPI_FLAG_TXE,改成这个就可以了。可能是时间长了,库函数里的变量名改了。
2. .\spi.axf: Error: L6218E: Undefined symbol SPI_I2S_GetFlagStatus (referred from w25qxx.o).
解:原理如上个问题,修改为
HAL_SPI_GetState(&hspi2) == HAL_SPI_STATE_BUSY_TX
3. STM32关于我遇到的HardFault_Handler的处理_warning:hardfault handler-CSDN博客
调了一个下午,发现是内存泄漏,数组访问越界的问题,也是醉了。
4. 软件SPI和硬件SPI的区别
软件SPI:用IO模拟SPI时序,这个模拟过程全部是CPU在负责执行,为了其稳定的存取数据,你可能会插入软件延时,这个时间在读取数据量不大的情况下并不明显,但是基本上你在读取过程中,其他非中断非异常程序是无法得到执行。
硬件SPI:首先这个数据存道储的过程是不需要CPU参与得,程序中配置好SPI的访问时序,开启中断,CPU就可以在中断函数中搬移数据,省下了软件模拟IO得存取时间。
仔细研究就会发现,CPU在进行SPI中断服务程序还版是需要耽误时间得,这个过程在大数据量传输中还是很耗时,ARM中Cortex-M3内核得处理器在硬件SPI上加入了DMA,这个DMA直接从SPI的数据寄存器,软件配置好DMA之后,基本上整个传输都不要权CPU参与,软件设计得好的话,整个数据传输都不要CPU参与。
W25Qxx------------(硬件SPI)_硬件spi和软件spi区别-CSDN博客
5.W25QXX上的端口容易接反。
D0--------MISO---------PA6--------PB15
D1--------MOSI---------PA7--------PB14
6.SPI1和SPI2的区别
SPI2需要复用引脚,重映射。Cubemx里面生成代码会配置好,如果用标准库去写,需要自己配置重映射。
7.硬SPI和软SPI的区别
硬SPI可以不用配置CS/NSS管脚,软SPI就是自己重新配置CS管脚,使其达到控制片选的功能,硬SPI利用终断或DMA传输数据,还没试过。
8.SPI中参数配置
hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; |
SPI_POLARITY_LOW--------对应上文的CLK极性0.
SPI_PHASE_1EDGE---------对应上文的CLK相位0
所以SPI工作在模式1(总共4个模式,上升沿采集数据,下降沿发送数据)
SPI_NSS_SOFT----------软SPI
SPI_BAUDRATEPRESCALER_128-----------128分频,在cubemx配置里面会显示对应多少赫兹
- 调试成功,纪念一下
完成
STM32CubeMX学习笔记(10)——SPI接口使用(读写SPI Flash W25Q64)_stm32cubemx配置spi-CSDN博客
- 继续封装SPI收发数据成W25Q64收发数据
网上代码一般比较标准,按照教程来操作即可,无需太多改动。已经标准化的东西,拿来用用,理解一下原理就好,加深印象。
7、单片机与W25Q128(FLASH)的通讯(SPI)实验(STM32F407)_f407 w25q-CSDN博客
6.GPIO的8种模式理解
开漏输出(open drain)与推挽输出(push pull)学习详解及某个踩到的坑分享-CSDN博客
STM32CUBEMX配置教程(三)通用GPIO配置_stm32cubemx怎么设置gpio精准输出-CSDN博客
STM32的GPIO端口配置八种模式的理解_stm32的gpio的配置模式有哪几种-CSDN博客