终于利用stm32cube生成了usb虚拟串口程序并初步调试通过,分享代码和配置先!!

软件教程Wise Up 回复了问题 • 79 人关注 • 32 个回复 • 46535 次浏览 • 2 天前 • 来自相关话题

(直播)分析老外的关于stm32的usb虚拟串口程序,体会配置方法和实现流程

经验分享Wise Up 回复了问题 • 12 人关注 • 14 个回复 • 21638 次浏览 • 2 天前 • 来自相关话题

STM32CubeMX5.01+F1 1.7.0初始化默认端口为高电平不成功,这个是Bug吗

回复

问题困惑BG4RFF 发起了问题 • 1 人关注 • 0 个回复 • 32 次浏览 • 2 天前 • 来自相关话题

stm32cube 配置fsmc LCD不成功

回复

问题困惑a6gu 发起了问题 • 1 人关注 • 0 个回复 • 31 次浏览 • 2 天前 • 来自相关话题

cube 5.0 创建新工程,一直转圈

问题困惑a6gu 回复了问题 • 5 人关注 • 3 个回复 • 437 次浏览 • 2 天前 • 来自相关话题

关于CUBE生成的I2C的HAL_MEM_Read()函数的疑问,做过的大佬麻烦停留一分钟伸出援助之手,小弟感激不尽!

回复

问题困惑大博哥 发起了问题 • 1 人关注 • 0 个回复 • 44 次浏览 • 3 天前 • 来自相关话题

鉴于好多人问,分享个24C64的HAL库文件和使用操作(利用硬件库iic)

软件教程大博哥 回复了问题 • 11 人关注 • 10 个回复 • 5867 次浏览 • 3 天前 • 来自相关话题

知道为什么大部分人喜欢用IO口模拟IIC吗?知道怎么去模拟吗?这里有你要的答案

经验分享大博哥 回复了问题 • 8 人关注 • 5 个回复 • 3765 次浏览 • 3 天前 • 来自相关话题

发现CubeMX有一个BUG

经验分享admin 回复了问题 • 2 人关注 • 1 个回复 • 62 次浏览 • 3 天前 • 来自相关话题

STM32—HAL库的回调函数放在main中么

问题困惑admin 回复了问题 • 2 人关注 • 1 个回复 • 41 次浏览 • 3 天前 • 来自相关话题

I2C函数中HAL_I2C_Mem_Write和HAL_I2C_Master_Transmit有啥区别呀

问题困惑大博哥 回复了问题 • 8 人关注 • 6 个回复 • 10242 次浏览 • 4 天前 • 来自相关话题

求助串口问题

回复

新手交流张小博 回复了问题 • 1 人关注 • 1 个回复 • 44 次浏览 • 5 天前 • 来自相关话题

关于使用串口接收中断会出现接收溢出错误的困惑

问题困惑豳草 回复了问题 • 4 人关注 • 2 个回复 • 895 次浏览 • 6 天前 • 来自相关话题

STM32F3系列运用HAL库实现SPI读写铁电FM25CL64

经验分享牙子男男 发表了文章 • 2 个评论 • 85 次浏览 • 2019-01-13 15:55 • 来自相关话题

最近项目用到了STM32F3系列芯片,需要通过SPI总线实现对铁电FM25CL64的读写。以前完全没用过SPI,所以前期遇到了不少坑,特开此贴进行记录。正题开始:SPI配置:因为硬件上,铁电与芯片的SPI2连接,所以配置了SPI2的参数。我用的是软件触发模式。DMA和中断模式还没来得及尝试引脚:模式(Mode):全双工硬件片选(NSS): 禁用(不太懂这个,后面单独配置了一个GPIO当片选用)基本参数: 帧格式: Motorola (还有个TI的,得配合硬件片选信号,没用过) 数据大小:8bits (这个FM25CL64内部存储是按照8bit存储的) 第一位:MSB First (和FM25CL64的技术手册保持一致)时钟参数: 分频:8 (这个好像影响不大。 FM25CL64最高读写率是20MBit/s ,确保分频后下面的BaudRate小于20就行。)第一个坑要来了:CPOL和CPHA这两个参数有四种组合模式,这个参数配置一定要满足FM25CL64的要求。 刚开始,就是这个没配置对,能看到SPI总线上有数据发出去,但是铁电不返回数据,下面这组数据经测试没问题。 CPOL:  LOW CPHA: 1 Edge高级参数:因为我的SPI总线上只有一个从机,就没用到这些参数。 至此SPI的配置就完成了。生成的代码为:void MX_SPI2_Init(void){  hspi2.Instance = SPI2;  hspi2.Init.Mode = SPI_MODE_MASTER;  hspi2.Init.Direction = SPI_DIRECTION_2LINES;  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;  hspi2.Init.NSS = SPI_NSS_SOFT;  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;  hspi2.Init.CRCPolynomial = 7;  hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;  if (HAL_SPI_Init(&hspi2) != HAL_OK)  {    Error_Handler();  }}void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle){  GPIO_InitTypeDef GPIO_InitStruct = {0};  if(spiHandle->Instance==SPI2)  {  /* USER CODE BEGIN SPI2_MspInit 0 */  /* USER CODE END SPI2_MspInit 0 */    /* SPI2 clock enable */    __HAL_RCC_SPI2_CLK_ENABLE();      __HAL_RCC_GPIOA_CLK_ENABLE();    /**SPI2 GPIO Configuration        PA8     ------> SPI2_SCK    PA9     ------> SPI2_MISO    PA10     ------> SPI2_MOSI     */    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;    GPIO_InitStruct.Pull = GPIO_NOPULL;    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  /* USER CODE BEGIN SPI2_MspInit 1 */  /* USER CODE END SPI2_MspInit 1 */  }}下面是写FM25CL64的读写数据程序static uint8_t WRITEFRAME[4];     //向FRAM写数据的数据流数组static uint8_t READFRAME[3];      //从FRAM读数据的数据流数组static uint8_t WRITE_SR_FRAME[2];  //向FRAM写状态寄存器的数据流数组uint8_t op_code;                          //操作码static uint8_t i;片选操作代码:GPIO_PORT_CS是片选信号GPIO的组,GPIO_PIN_CS是片选信号GPIO的PIN号。void FRAMCS()    //CS信号拉低{ HAL_GPIO_WritePin(GPIO_PORT_CS,GPIO_PIN_CS,GPIO_PIN_RESET);   }void nFRAMCS() //CS信号拉高{ HAL_GPIO_WritePin(GPIO_PORT_CS,GPIO_PIN_CS,GPIO_PIN_SET);}使能写操作:根据FM25CL64的技术手册,进行任何写操作(写状态寄存器和写数据)之前,都需要先使能写操作。void FRAM_WREN(void){ op_code = FM25CL64_WREN; FRAMCS();                                                                                        //将CS信号拉低 HAL_SPI_Transmit(&hspi2,&op_code,1,FRAM_TIMEOUT);            //  通过SPI2,向铁电发送使能写操作的操作码0000 0110b。 nFRAMCS();                                                                                      //将CS信号拉高}禁止写操作:void FRAM_WRDI(void){ op_code = FM25CL64_WRDI; FRAMCS();                     HAL_SPI_Transmit(&hspi2,&op_code,1,FRAM_TIMEOUT);           // 原理和使能写操作一样,只是发送的操作码是 禁止写操作码:0000 0100b nFRAMCS();                    }读状态寄存器:uint8_t FRAM_RDSR(void){ uint8_t SRData; op_code = FM25CL64_RDSR;  FRAMCS();                     HAL_SPI_Transmit(&hspi2,&op_code,1,FRAM_TIMEOUT);          // 发送读状态寄存器操作码 00000101b HAL_SPI_Receive(&hspi2,&SRData,1,FRAM_TIMEOUT) ;           //将接收到的数据放在SRData  nFRAMCS();                       // Disable FRAM CS return(SRData);                     }写状态寄存器:void FRAM_WRSR(uint8_t Data){  FRAM_WREN();                   //使能写操作  一定要有!!! FRAMCS();                        // Enable FRAM CS WRITE_SR_FRAME[0] = FM25CL64_WRSR;      //写状态寄存器的数据流是先发写状态寄存器的操作码,紧跟着发送要写的数据。  WRITE_SR_FRAME[1] = Data;                             //所以,建立一个数组,存放操作码和数据。 用SPI将这两个数据连续发送 HAL_SPI_Transmit(&hspi2,&WRITE_SR_FRAME[0],2,FRAM_TIMEOUT);    // &WRITE_SR_FRAME[0]表示第一个数的起始地址,这里的“2”表示要发送两个8位数。 nFRAMCS();                       // Disable FRAM CS FRAM_WRDI();                   // Disable Write operation   //写操作结束后,记得调用禁止写操作}初始化状态寄存器:void InitFRAM(void){ FRAM_WRSR(0x80);               // 其实就是向状态寄存器写0x80这个数。至于为什么是这个,看技术手册就知道了。}读FRAM数据:uint8_t ReadFRAM(uint16_t Addr)   //Addr是要读取的数据在FRAM中存放的地址。 FM25CL64的地址是0000到1FFF。{  uint8_t Data;                                   //声明一个无符号8位数,准备存放读取到的数据 uint8_t AddrH,AddrL;                      //将16位的地址分为两个8位数 AddrH = (Addr>>8); AddrL = (uint8_t)Addr;  FRAMCS();                           // Enable FRAM CS  READFRAME[0] = FM25CL64_READ;    //读FRAM数据的数据流为,向FRAM发送读数据操作码,紧接着为数据地址高8位,紧接着为数据地址低8位。 READFRAME[1] = AddrH;                        //声明一个数组存放读数据操作码,地址高八位,地址低八位 READFRAME[2] = AddrL;  HAL_SPI_Transmit(&hspi2,&READFRAME[0],3,FRAM_TIMEOUT);    // 调用发送函数,将数组里的三个数连续发送 HAL_SPI_Receive(&hspi2,&Data,1,FRAM_TIMEOUT);        //  铁电收到读数据操作后,会返回地址对应的数据。用SPI的接收函数接收数据,存放在Data里。 nFRAMCS();                          // Disable FRAM CS  return(Data);                                                                       }向FRAM写数据:void WriteFRAM(uint16_t Addr, uint8_t Data)    //将Data写在FRAM内部Addr这个地址{  uint8_t AddrH,AddrL;                                       //将16位地址分为两个8位数 AddrH = (Addr>>8); AddrL = (uint8_t)Addr;  FRAM_WREN();                   // 一定要使能写操作 FRAMCS();                        // Enable FRAM CS  WRITEFRAME[0] = FM25CL64_WRITE;            //FM25CL64的写操作数据流是:先发操作码,再发地址高八位,再发地址低八位,再发数据。 WRITEFRAME[1] = AddrH;                                 //用数组依次存放操作码,地址高八位,地址低八位,数据 WRITEFRAME[2] = AddrL; WRITEFRAME[3] = Data;  HAL_SPI_Transmit(&hspi2,&WRITEFRAME[0],4,FRAM_TIMEOUT);           // 调用SPI发送函数,将数组里的四个数据连续发送                   nFRAMCS();                       // Disable FRAM CS FRAM_WRDI();                   // 禁止写操作}头文件#define GPIO_PORT_CS         GPIOA                             //铁电的CS信号连接到了芯片的PA11,在此定义#define GPIO_PIN_CS          GPIO_PIN_11//FM25CL64指令定义#define        FM25CL64_WREN                0x06                //使能#define        FM25CL64_WRDI                0x04                //失能#define        FM25CL64_RDSR                0x05                //读状态#define        FM25CL64_WRSR                0x01                //写状态#define        FM25CL64_READ                0x03                //读数据#define        FM25CL64_WRITE            0x02                //写数据#define FRAM_TIMEOUT   100                                      //SPI超时检测#define READ_WRITE_ADDR    ((uint16_t)0x00FF)     //我自己定义的一个读写数据的起始地址               void InitFRAM(void);                                                    //初始化FRAM状态寄存器函数声明void WriteFRAM(uint16_t Addr, uint8_t Data);            //向FRAM写数据函数声明uint8_t ReadFRAM(uint16_t Addr);                             //从FRAM读数据函数声明写到这里基本就结束了。 在合适的地方,调用合适的读写函数就好。 查看全部

最近项目用到了STM32F3系列芯片,需要通过SPI总线实现对铁电FM25CL64的读写。以前完全没用过SPI,所以前期遇到了不少坑,特开此贴进行记录。正题开始:


SPI配置:因为硬件上,铁电与芯片的SPI2连接,所以配置了SPI2的参数。我用的是软件触发模式。DMA和中断模式还没来得及尝试

引脚:

image.png

模式(Mode):全双工

硬件片选(NSS): 禁用(不太懂这个,后面单独配置了一个GPIO当片选用)

基本参数:

 帧格式: Motorola (还有个TI的,得配合硬件片选信号,没用过)

 数据大小:8bits (这个FM25CL64内部存储是按照8bit存储的)

 第一位:MSB First (和FM25CL64的技术手册保持一致)

时钟参数:

 分频:8 (这个好像影响不大。 FM25CL64最高读写率是20MBit/s ,确保分频后下面的BaudRate小于20就行。)

第一个坑要来了:CPOL和CPHA这两个参数有四种组合模式,这个参数配置一定要满足FM25CL64的要求。 刚开始,就是这个没配置对,能看到SPI总线上有数据发出去,但是铁电不返回数据,下面这组数据经测试没问题。

 CPOL:  LOW

 CPHA: 1 Edge

高级参数:

因为我的SPI总线上只有一个从机,就没用到这些参数。 

image.png

至此SPI的配置就完成了。

生成的代码为:

void MX_SPI2_Init(void)
{

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 7;
  hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspInit 0 */

  /* USER CODE END SPI2_MspInit 0 */
    /* SPI2 clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();
 
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI2 GPIO Configuration   
    PA8     ------> SPI2_SCK
    PA9     ------> SPI2_MISO
    PA10     ------> SPI2_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN SPI2_MspInit 1 */

  /* USER CODE END SPI2_MspInit 1 */
  }
}

下面是写FM25CL64的读写数据程序

static uint8_t WRITEFRAME[4];     //向FRAM写数据的数据流数组
static uint8_t READFRAME[3];      //从FRAM读数据的数据流数组
static uint8_t WRITE_SR_FRAME[2];  //向FRAM写状态寄存器的数据流数组
uint8_t op_code;                          //操作码
static uint8_t i;


片选操作代码:GPIO_PORT_CS是片选信号GPIO的组,GPIO_PIN_CS是片选信号GPIO的PIN号。

void FRAMCS()    //CS信号拉低
{
 HAL_GPIO_WritePin(GPIO_PORT_CS,GPIO_PIN_CS,GPIO_PIN_RESET);   
}

void nFRAMCS() //CS信号拉高
{
 HAL_GPIO_WritePin(GPIO_PORT_CS,GPIO_PIN_CS,GPIO_PIN_SET);
}

使能写操作:根据FM25CL64的技术手册,进行任何写操作(写状态寄存器和写数据)之前,都需要先使能写操作。

void FRAM_WREN(void)
{
 op_code = FM25CL64_WREN;

 FRAMCS();                                                                                        //将CS信号拉低

 HAL_SPI_Transmit(&hspi2,&op_code,1,FRAM_TIMEOUT);            //  通过SPI2,向铁电发送使能写操作的操作码0000 0110b。
 nFRAMCS();                                                                                      //将CS信号拉高
}

禁止写操作:

void FRAM_WRDI(void)
{
 op_code = FM25CL64_WRDI;
 FRAMCS();                    

 HAL_SPI_Transmit(&hspi2,&op_code,1,FRAM_TIMEOUT);           // 原理和使能写操作一样,只是发送的操作码是 禁止写操作码:0000 0100b

 nFRAMCS();                    
}

读状态寄存器:

uint8_t FRAM_RDSR(void)
{
 uint8_t SRData;
 op_code = FM25CL64_RDSR; 

 FRAMCS();                    

 HAL_SPI_Transmit(&hspi2,&op_code,1,FRAM_TIMEOUT);          // 发送读状态寄存器操作码 00000101b
 HAL_SPI_Receive(&hspi2,&SRData,1,FRAM_TIMEOUT) ;           //将接收到的数据放在SRData 

 nFRAMCS();                       // Disable FRAM CS

 return(SRData);                    
}

写状态寄存器:

void FRAM_WRSR(uint8_t Data)

 FRAM_WREN();                   //使能写操作  一定要有!!!
 FRAMCS();                        // Enable FRAM CS

 WRITE_SR_FRAME[0] = FM25CL64_WRSR;      //写状态寄存器的数据流是先发写状态寄存器的操作码,紧跟着发送要写的数据。 

 WRITE_SR_FRAME[1] = Data;                             //所以,建立一个数组,存放操作码和数据。 用SPI将这两个数据连续发送
 HAL_SPI_Transmit(&hspi2,&WRITE_SR_FRAME[0],2,FRAM_TIMEOUT);    // &WRITE_SR_FRAME[0]表示第一个数的起始地址,这里的“2”表示要发送两个8位数。
 nFRAMCS();                       // Disable FRAM CS

 FRAM_WRDI();                   // Disable Write operation   //写操作结束后,记得调用禁止写操作
}

初始化状态寄存器:

void InitFRAM(void)
{
 FRAM_WRSR(0x80);               // 其实就是向状态寄存器写0x80这个数。至于为什么是这个,看技术手册就知道了。
}

读FRAM数据:

uint8_t ReadFRAM(uint16_t Addr)   //Addr是要读取的数据在FRAM中存放的地址。 FM25CL64的地址是0000到1FFF。

 uint8_t Data;                                   //声明一个无符号8位数,准备存放读取到的数据
 uint8_t AddrH,AddrL;                      //将16位的地址分为两个8位数
 AddrH = (Addr>>8);
 AddrL = (uint8_t)Addr; 
 FRAMCS();                           // Enable FRAM CS 
 READFRAME[0] = FM25CL64_READ;    //读FRAM数据的数据流为,向FRAM发送读数据操作码,紧接着为数据地址高8位,紧接着为数据地址低8位。
 READFRAME[1] = AddrH;                        //声明一个数组存放读数据操作码,地址高八位,地址低八位
 READFRAME[2] = AddrL; 

 HAL_SPI_Transmit(&hspi2,&READFRAME[0],3,FRAM_TIMEOUT);    // 调用发送函数,将数组里的三个数连续发送

 HAL_SPI_Receive(&hspi2,&Data,1,FRAM_TIMEOUT);        //  铁电收到读数据操作后,会返回地址对应的数据。用SPI的接收函数接收数据,存放在Data里。

 nFRAMCS();                          // Disable FRAM CS 

 return(Data);                                                                       
}

向FRAM写数据:

void WriteFRAM(uint16_t Addr, uint8_t Data)    //将Data写在FRAM内部Addr这个地址

 uint8_t AddrH,AddrL;                                       //将16位地址分为两个8位数
 AddrH = (Addr>>8);
 AddrL = (uint8_t)Addr; 
 FRAM_WREN();                   // 一定要使能写操作

 FRAMCS();                        // Enable FRAM CS 
 WRITEFRAME[0] = FM25CL64_WRITE;            //FM25CL64的写操作数据流是:先发操作码,再发地址高八位,再发地址低八位,再发数据。
 WRITEFRAME[1] = AddrH;                                 //用数组依次存放操作码,地址高八位,地址低八位,数据
 WRITEFRAME[2] = AddrL;
 WRITEFRAME[3] = Data; 
 HAL_SPI_Transmit(&hspi2,&WRITEFRAME[0],4,FRAM_TIMEOUT);           // 调用SPI发送函数,将数组里的四个数据连续发送                  

 nFRAMCS();                       // Disable FRAM CS

 FRAM_WRDI();                   // 禁止写操作
}


头文件

#define GPIO_PORT_CS         GPIOA                             //铁电的CS信号连接到了芯片的PA11,在此定义
#define GPIO_PIN_CS          GPIO_PIN_11
//FM25CL64指令定义
#define        FM25CL64_WREN                0x06                //使能
#define        FM25CL64_WRDI                0x04                //失能
#define        FM25CL64_RDSR                0x05                //读状态
#define        FM25CL64_WRSR                0x01                //写状态
#define        FM25CL64_READ                0x03                //读数据
#define        FM25CL64_WRITE            0x02                //写数据

#define FRAM_TIMEOUT   100                                      //SPI超时检测
#define READ_WRITE_ADDR    ((uint16_t)0x00FF)     //我自己定义的一个读写数据的起始地址
               

void InitFRAM(void);                                                    //初始化FRAM状态寄存器函数声明

void WriteFRAM(uint16_t Addr, uint8_t Data);            //向FRAM写数据函数声明
uint8_t ReadFRAM(uint16_t Addr);                             //从FRAM读数据函数声明



写到这里基本就结束了。 在合适的地方,调用合适的读写函数就好。



timebase source

问题困惑凤城之约 回复了问题 • 3 人关注 • 2 个回复 • 1384 次浏览 • 2019-01-13 10:10 • 来自相关话题