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

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

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

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

发现CubeMX有一个BUG

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

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读数据函数声明



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



初见STM32Cube MX

I_mURfather 发表了文章 • 2 个评论 • 759 次浏览 • 2018-12-26 23:28 • 来自相关话题

本人刚入坑STM32Cube MX不到一个月,这里留下自己的一些心得体会,帮助更多的新人入坑。由于本人水平有限,难免会有出错的地方,望大佬指点改正。初见STM32Cube MXNotice:这里使用的版本为MX5.0,最新版本,界面较老版本发生了很多改变,不过功能基本一致(大部分教程都是老版本的这也是为什么我要开坑这个教程的原因)STM32Cube MX是意法半导体(ST)公司开发的一款针对STM32系列芯片进行图形化配置的一个工具。也可以说是有一个GUI界面的配置工具。为什么要用Cube MX呢,难道直接在Keil MDK5上编程和Cube MX不一样吗?答案自然是否定的。在Cube MX上能完成的功能,可以直接在MDK5上完成。以前传统的编程方式有寄存器版和库函数版,而Cube MX是全新的一种方式,使用图形化界面配置底层硬件,用库函数,不过不是标准库,而是集成度更高,更简单的HAL库。还记得原子哥讲的STM32库函数版本,一开始就是教初学者新建Temple模板文件,但是使用Cube MX就完全不需要这么多繁琐的步骤,配置好你需要的资源之后,直接生成代码。看到这里是不是心动了,相信有了Cube MX这样一款利器能供让初学者们能够轻松入门STM32。以下内容来自机翻:“STM32CubeMX是一个图形工具,可以通过逐步过程轻松配置STM32微控制器并生成相应的初始化C代码。第一步是选择符合所需外设集的STMicroelectronics STM32微控制器。然后,用户必须配置每个所需的嵌入式软件,这要归功于引脚冲突解算器,时钟树设置帮助器,功耗计算器以及执行MCU外设配置(如GPIO或USART)和中间件堆栈(如USB或TCP / IP)。最后,用户基于所选择的配置启动初始化C代码的生成。此代码已准备好在多个开发环境中使用。用户代码保留在下一代代码中。”值得一提的是这个软件没有汉化版,只有英文版,所以英文不好的小伙伴只能一个个对着词典查了,反正这玩意说来说去就那几个单词,死记硬背下来难度比中学阅读理解还简单(滑稽)。打开Cube MX,最上面一排我们可以看到三个菜单栏:FLIE、WINDOW、HELP。第一个FLIE菜单用于打开或者新建工程;第二个WINDOW菜单用于打开或者关闭OUTPUT(目前为止我还没用到过);第三个菜单用于管理软件版本(就是检查更新啦),以及支持包的安装/更新(建议保持软件版本最新,支持包根据需要安装,有的工程可能用的是老板的支持包)。那么让我们新建一个工程吧,在FILE中点击NEW PROJECT,然后会进到一个列表里面去,这个列表用于选择主控芯片,我用的是STM32F427IIH6,所以我直接在搜索框中输入:“STM32F427”,选择后面出来的“STM32F427IIHx”。这时你将进入到如下界面:                                              在三个菜单选项下方出现了你的Cube MX资源管理器的路径,显示你现在使用的芯片以及工程名字。路径下方就是你的工作区辣~接着,让我们仔细来看看工作区:工作区有四个选项卡:Pinout & Configuration、Clock Configuration、Project Manager、Tools。Pinout&Configuration:这个选项卡是用来快速配置系统核心,GPIO分配,定时器配置,通讯方式等单片机资源的。通过工作区的图像我们可以看到左侧是一个可以拉动的菜单,右侧则是引脚图形化的一个快速预览,通过点击左侧菜单进行配置,右侧的引脚也会随之变化。当然你也可以提前布局好你的引脚,再去分配资源。以下是引脚颜色意义的说明:1已经分配好资源,并且启用的引脚会用绿色标出;带有大头针的引脚则是用户自己写有标签(USER LABER)类似于注释的作用;2黄色的引脚则是系统工作必需引脚,用户无法对其定义,比如:VDD、BOOT等;3橘黄色的引脚则是你已经对其模式进行了选择,比如ADC(模数转换),TIM4_CH2(定时器4通道2),但是没有在相应的地方配置系统资源,比如ADC中未配置打开ADC,TIM4没有打开CH2。经过细心的观察会发现在引脚的图形化界面上方有两个选项,一个是Pinout View(引脚显示),另外一个是System view(系统显示)。默认情况下都是显示的引脚情况。有些时候会忘记自己的资源怎么配置的,GPIO怎么分配的,那么不用一个个去左侧的菜单中寻找,选择System View即可快速对你已配置的资源进行查看。让我们进入第二个选项卡:Clock configuration。这是系统时钟配置界面:认识STM32Cube MX.pdf这个界面东西比较复杂,要了解的东西比较多,我会在后面配置工程的时候详细说明。最基本的看法就是从左往右,顺着箭头看。接下来就是第三个选项卡了:Project Manager。在这个选项卡中是用于管理生成我们工程文件的设置。如下图:值得一提的是在桌面建立工程的时候一定要注意自己的计算机名称是否含有中文,如果含有的话那么会在生成工程的时候失败。    第四个选项卡,Tools。内容如下:暂时还未用到,如果用到以后会做详细说明。这一章节就这样结束了,撒哟啦啦~PS:由于图片在我这里不能正常显示,所以我上传了一份PDF文件(其实图片看不看无所谓啦~) 查看全部

本人刚入坑STM32Cube MX不到一个月,这里留下自己的一些心得体会,帮助更多的新人入坑。

由于本人水平有限,难免会有出错的地方,望大佬指点改正。

初见STM32Cube MX

Notice:这里使用的版本为MX5.0,最新版本,界面较老版本发生了很多改变,不过功能基本一致(大部分教程都是老版本的这也是为什么我要开坑这个教程的原因)

STM32Cube MX是意法半导体(ST)公司开发的一款针对STM32系列芯片进行图形化配置的一个工具。也可以说是有一个GUI界面的配置工具。

为什么要用Cube MX呢,难道直接在Keil MDK5上编程和Cube MX不一样吗?答案自然是否定的。在Cube MX上能完成的功能,可以直接在MDK5上完成。以前传统的编程方式有寄存器版和库函数版,而Cube MX是全新的一种方式,使用图形化界面配置底层硬件,用库函数,不过不是标准库,而是集成度更高,更简单的HAL库。还记得原子哥讲的STM32库函数版本,一开始就是教初学者新建Temple模板文件,但是使用Cube MX就完全不需要这么多繁琐的步骤,配置好你需要的资源之后,直接生成代码。看到这里是不是心动了,相信有了Cube MX这样一款利器能供让初学者们能够轻松入门STM32。以下内容来自机翻:

STM32CubeMX是一个图形工具,可以通过逐步过程轻松配置STM32微控制器并生成相应的初始化C代码。

第一步是选择符合所需外设集的STMicroelectronics STM32微控制器。

然后,用户必须配置每个所需的嵌入式软件,这要归功于引脚冲突解算器,时钟树设置帮助器,功耗计算器以及执行MCU外设配置(如GPIO或USART)和中间件堆栈(如USB或TCP / IP)。

最后,用户基于所选择的配置启动初始化C代码的生成。此代码已准备好在多个开发环境中使用。用户代码保留在下一代代码中。

值得一提的是这个软件没有汉化版,只有英文版,所以英文不好的小伙伴只能一个个对着词典查了,反正这玩意说来说去就那几个单词,死记硬背下来难度比中学阅读理解还简单(滑稽)。

打开Cube MX,最上面一排我们可以看到三个菜单栏:FLIEWINDOWHELP

第一个FLIE菜单用于打开或者新建工程;第二个WINDOW菜单用于打开或者关闭OUTPUT(目前为止我还没用到过);第三个菜单用于管理软件版本(就是检查更新啦),以及支持包的安装/更新(建议保持软件版本最新,支持包根据需要安装,有的工程可能用的是老板的支持包)。

那么让我们新建一个工程吧,在FILE中点击NEW PROJECT,然后会进到一个列表里面去,这个列表用于选择主控芯片,我用的是STM32F427IIH6,所以我直接在搜索框中输入:“STM32F427”,选择后面出来的“STM32F427IIHx”。这时你将进入到如下界面:

                                             

在三个菜单选项下方出现了你的Cube MX资源管理器的路径,显示你现在使用的芯片以及工程名字。

路径下方就是你的工作区辣~

接着,让我们仔细来看看工作区:工作区有四个选项卡:Pinout & ConfigurationClock ConfigurationProject ManagerTools

Pinout&Configuration:这个选项卡是用来快速配置系统核心,GPIO分配,定时器配置,通讯方式等单片机资源的。通过工作区的图像我们可以看到左侧是一个可以拉动的菜单,右侧则是引脚图形化的一个快速预览,通过点击左侧菜单进行配置,右侧的引脚也会随之变化。当然你也可以提前布局好你的引脚,再去分配资源。

以下是引脚颜色意义的说明:1已经分配好资源,并且启用的引脚会用绿色标出;带有大头针的引脚则是用户自己写有标签(USER LABER)类似于注释的作用;2黄色的引脚则是系统工作必需引脚,用户无法对其定义,比如:VDD、BOOT等;3橘黄色的引脚则是你已经对其模式进行了选择,比如ADC(模数转换),TIM4_CH2(定时器4通道2),但是没有在相应的地方配置系统资源,比如ADC中未配置打开ADC,TIM4没有打开CH2。

经过细心的观察会发现在引脚的图形化界面上方有两个选项,一个是Pinout View(引脚显示),另外一个是System view(系统显示)。默认情况下都是显示的引脚情况。有些时候会忘记自己的资源怎么配置的,GPIO怎么分配的,那么不用一个个去左侧的菜单中寻找,选择System View即可快速对你已配置的资源进行查看。

让我们进入第二个选项卡:Clock configuration。这是系统时钟配置界面:

认识STM32Cube MX.pdf

这个界面东西比较复杂,要了解的东西比较多,我会在后面配置工程的时候详细说明。最基本的看法就是从左往右,顺着箭头看。

接下来就是第三个选项卡了:Project Manager。在这个选项卡中是用于管理生成我们工程文件的设置。如下图:

值得一提的是在桌面建立工程的时候一定要注意自己的计算机名称是否含有中文,如果含有的话那么会在生成工程的时候失败。

 

 

 

 

第四个选项卡,Tools。内容如下:

暂时还未用到,如果用到以后会做详细说明。

这一章节就这样结束了,撒哟啦啦~


PS:由于图片在我这里不能正常显示,所以我上传了一份PDF文件(其实图片看不看无所谓啦~)


Stm32F103RCT6硬件i2c HAL库函数bug

君莫笑 发表了文章 • 1 个评论 • 194 次浏览 • 2018-12-25 09:24 • 来自相关话题

问题描述 使用stm32f103rcT6芯片的i2c引脚PB6 PB7连接EEROM AT24C02进行EEROM读写时调用 HAL_I2C_Mem_Write和HAL_I2C_Mem_Read函数进行读写操作不能成功,使用示波器测的引脚也无波形产生,最终在函数void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)中发现其代码如下void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle){  GPIO_InitTypeDef GPIO_InitStruct;  if(i2cHandle->Instance==I2C1)  {  /* USER CODE BEGIN I2C1_MspInit 0 */  /* USER CODE END I2C1_MspInit 0 */      /**I2C1 GPIO Configuration        PB6     ------> I2C1_SCL    PB7     ------> I2C1_SDA     */    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);    /* I2C1 clock enable */    __HAL_RCC_I2C1_CLK_ENABLE();  /* USER CODE BEGIN I2C1_MspInit 1 */  /* USER CODE END I2C1_MspInit 1 */  }}将其更改为如下 即先使能时钟 再初始化引脚问题得以解决可以正常读写EEROMvoid HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle){  GPIO_InitTypeDef GPIO_InitStruct;  if(i2cHandle->Instance==I2C1)  {  /* USER CODE BEGIN I2C1_MspInit 0 */  /* USER CODE END I2C1_MspInit 0 */  /* I2C1 clock enable */   __HAL_RCC_I2C1_CLK_ENABLE();    /**I2C1 GPIO Configuration        PB6     ------> I2C1_SCL    PB7     ------> I2C1_SDA     */    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);      /* USER CODE BEGIN I2C1_MspInit 1 */  /* USER CODE END I2C1_MspInit 1 */  }}希望对后面遇到的人有用 by荒野大嫖客 查看全部

问题描述 使用stm32f103rcT6芯片的i2c引脚PB6 PB7连接EEROM AT24C02进行EEROM读写时调用 HAL_I2C_Mem_Write和HAL_I2C_Mem_Read函数进行读写操作不能成功,使用示波器测的引脚也无波形产生,最终在函数void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)中发现其代码如下

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)

{


  GPIO_InitTypeDef GPIO_InitStruct;

  if(i2cHandle->Instance==I2C1)

  {

  /* USER CODE BEGIN I2C1_MspInit 0 */


  /* USER CODE END I2C1_MspInit 0 */

  

    /**I2C1 GPIO Configuration    

    PB6     ------> I2C1_SCL

    PB7     ------> I2C1_SDA 

    */

    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);


    /* I2C1 clock enable */

    __HAL_RCC_I2C1_CLK_ENABLE();

  /* USER CODE BEGIN I2C1_MspInit 1 */


  /* USER CODE END I2C1_MspInit 1 */

  }

}

将其更改为如下 即先使能时钟 再初始化引脚问题得以解决可以正常读写EEROM

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)

{


  GPIO_InitTypeDef GPIO_InitStruct;

  if(i2cHandle->Instance==I2C1)

  {

  /* USER CODE BEGIN I2C1_MspInit 0 */


  /* USER CODE END I2C1_MspInit 0 */

  /* I2C1 clock enable */

  __HAL_RCC_I2C1_CLK_ENABLE();

    /**I2C1 GPIO Configuration    

    PB6     ------> I2C1_SCL

    PB7     ------> I2C1_SDA 

    */

    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;

    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);


    

  /* USER CODE BEGIN I2C1_MspInit 1 */


  /* USER CODE END I2C1_MspInit 1 */

  }

}

希望对后面遇到的人有用 by荒野大嫖客


一个实用的按键扫描方案

乌龟也会飞 发表了文章 • 3 个评论 • 185 次浏览 • 2018-12-21 09:00 • 来自相关话题

由于项目需要外置一个控制盒通过按键来控制系统移动、启动功能,其中感觉所写的按键部分还算实用就分享出来,如果更好的方案不妨给出指点和分享出来大家都学习学习 以下文章按键弹起状态为1 按下状态为0 基本原理 机械按键由于其特殊性在按下或者弹起的时刻会有机械抖动引起误差,会出现采集状态在0和1之间来回切换,直到稳定状态。我所设计的系统中有4个按键上行按键UpKey,停止按键StopKey,下行按键DownKey活启动/停止按键OnOffKey,设计思路就是不停的扫描对应的IO口状态步骤1,开启一个定时器频率为1000Hz(周期为1ms)的定时器,在其回调函数中进行对应的IO口扫描 //开启定时器中断 频率1000Hz  HAL_TIM_Base_Start_IT(&htim6);步骤二定义一个按键标志数组,和一个按键灵敏度控制变量uint8_t KeyMask[4] = {0x0}; //分别对应上行按键 停止按键 下行按键 启动/关闭按键状态 0x01对应按键按下 0x00对应无按键按下状态#define KEYMASK 0x0000ffff   //定义按键灵敏度,即当按键按下处于稳定状态0达到多少ms后才算按键按下 16ms可以根据自己所需要进行调节步骤三编写中断定时器回调函数void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ //缓存32次按键扫描结果,每次扫描保存左移一位 static uint32_t KeyUpCoder = 0xffffffff; //按键没有按下时刻为高电平状态 static uint32_t KeyStopCoder = 0xffffffff; static uint32_t KeyDownCoder = 0xffffffff; static uint32_t KeySysCoder = 0xffffffff; if(htim->Instance == htim6.Instance){                //判断定时器中断触发源 //左移一位,剔除最早采集的状态准备加入最新的采集状态 KeyUpCoder   <<= 1; KeyStopCoder <<= 1; KeyDownCoder <<= 1; KeySysCoder  <<= 1; //采样按键值 KeyUpCoder   |= HAL_GPIO_ReadPin(KeyUp_GPIO_Port,KeyUp_Pin); KeyStopCoder |= HAL_GPIO_ReadPin(KeyStop_GPIO_Port,KeyStop_Pin); KeyDownCoder |= HAL_GPIO_ReadPin(KeyDown_GPIO_Port,KeyDown_Pin); KeySysCoder  |= HAL_GPIO_ReadPin(KeySys_GPIO_Port,KeySys_Pin); //进行按键解析是否存在按键, //首先判断之前是否发生过按键按下情况,且主程序是否完成了对应按键的程序处理 //如果主程序还未完成相关程序处理,则本次按键无效 //KeyMask代表按键是否有效,且表达了主程序是否完成了相关服务程序的                 //通过判断只有连续出现了大于等于灵敏度次稳定状态电平,才会判断定按键按下一般机械抖动在10ms左右,可根据自己需要调动                //在主程序中通过判断KeyMask来识别是否有按键按下 完成相关服务函数处理同时需要释放KeyMask,来开始新的一次扫描 //判断上行按键 if((!KeyMask[0])&&((KeyUpCoder&KEYMASK)== 0x00000000)) //完成消抖判断 KeyMask[0] = 0x01; //判断停止按键 if((!KeyMask[1])&&((KeyStopCoder&KEYMASK)== 0x00000000)) KeyMask[1] = 0x01; //判断下行按键 if((!KeyMask[2])&&((KeyDownCoder&KEYMASK)== 0x00000000)) KeyMask[2] = 0x01; //判断系统按键 if((!KeyMask[3])&&((KeySysCoder&KEYMASK)== 0x00000000)) KeyMask[3] = 0x01; }}by 荒野大嫖客 查看全部

由于项目需要外置一个控制盒通过按键来控制系统移动、启动功能,其中感觉所写的按键部分还算实用就分享出来,如果更好的方案不妨给出指点和分享出来大家都学习学习 以下文章按键弹起状态为1 按下状态为0 

基本原理 机械按键由于其特殊性在按下或者弹起的时刻会有机械抖动引起误差,会出现采集状态在0和1之间来回切换,直到稳定状态。我所设计的系统中有4个按键上行按键UpKey,停止按键StopKey,下行按键DownKey活启动/停止按键OnOffKey,设计思路就是不停的扫描对应的IO口状态

步骤1,开启一个定时器频率为1000Hz(周期为1ms)的定时器,在其回调函数中进行对应的IO口扫描

 //开启定时器中断 频率1000Hz

  HAL_TIM_Base_Start_IT(&htim6);

步骤二定义一个按键标志数组,和一个按键灵敏度控制变量

uint8_t KeyMask[4] = {0x0}; //分别对应上行按键 停止按键 下行按键 启动/关闭按键状态 0x01对应按键按下 0x00对应无按键按下状态

#define KEYMASK 0x0000ffff   //定义按键灵敏度,即当按键按下处于稳定状态0达到多少ms后才算按键按下 16ms可以根据自己所需要进行调节

步骤三编写中断定时器回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){

//缓存32次按键扫描结果,每次扫描保存左移一位

static uint32_t KeyUpCoder = 0xffffffff; //按键没有按下时刻为高电平状态

static uint32_t KeyStopCoder = 0xffffffff;

static uint32_t KeyDownCoder = 0xffffffff;

static uint32_t KeySysCoder = 0xffffffff;

if(htim->Instance == htim6.Instance){                //判断定时器中断触发源

//左移一位,剔除最早采集的状态准备加入最新的采集状态

KeyUpCoder   <<= 1;

KeyStopCoder <<= 1;

KeyDownCoder <<= 1;

KeySysCoder  <<= 1;

//采样按键值

KeyUpCoder   |= HAL_GPIO_ReadPin(KeyUp_GPIO_Port,KeyUp_Pin);

KeyStopCoder |= HAL_GPIO_ReadPin(KeyStop_GPIO_Port,KeyStop_Pin);

KeyDownCoder |= HAL_GPIO_ReadPin(KeyDown_GPIO_Port,KeyDown_Pin);

KeySysCoder  |= HAL_GPIO_ReadPin(KeySys_GPIO_Port,KeySys_Pin);

//进行按键解析是否存在按键,

//首先判断之前是否发生过按键按下情况,且主程序是否完成了对应按键的程序处理

//如果主程序还未完成相关程序处理,则本次按键无效

//KeyMask代表按键是否有效,且表达了主程序是否完成了相关服务程序的 

                //通过判断只有连续出现了大于等于灵敏度次稳定状态电平,才会判断定按键按下一般机械抖动在10ms左右,可根据自己需要调动

                //在主程序中通过判断KeyMask来识别是否有按键按下 完成相关服务函数处理同时需要释放KeyMask,来开始新的一次扫描

//判断上行按键

if((!KeyMask[0])&&((KeyUpCoder&KEYMASK)== 0x00000000)) //完成消抖判断

KeyMask[0] = 0x01;

//判断停止按键

if((!KeyMask[1])&&((KeyStopCoder&KEYMASK)== 0x00000000))

KeyMask[1] = 0x01;

//判断下行按键

if((!KeyMask[2])&&((KeyDownCoder&KEYMASK)== 0x00000000))

KeyMask[2] = 0x01;

//判断系统按键

if((!KeyMask[3])&&((KeySysCoder&KEYMASK)== 0x00000000))

KeyMask[3] = 0x01;

}

}

by 荒野大嫖客

解决st_link下载一次就不能再下载程序

sdnx1 发表了文章 • 1 个评论 • 343 次浏览 • 2018-11-14 21:25 • 来自相关话题

今天调试一块stm32f103c8t6的最小系统板,在boot0=0,boot1=X,的情况下用st_link下载程序后,就无法再次下载程序了,提示target not load。原因是程序没有配置sw两个引脚,导致这两个引脚被默认成普通i/o没有打开调试功能。因之前一直在M4上做程序,所以不知道M3还得配置。解决方法:①将boot0=1,boot1=1,下载一个程序,就是刚才的程序就行。②将boot0=0,boot1=X,这时是可以下载程序的,但下载一次后,又会无法下载,所以要配置sw脚。三、打开调试口,并设置两个脚为sw模式这时候就可以多次下载程序了。 查看全部

今天调试一块stm32f103c8t6的最小系统板,在boot0=0,boot1=X,的情况下用st_link下载程序后,就无法再次下载程序了,提示target not load。

原因是程序没有配置sw两个引脚,导致这两个引脚被默认成普通i/o没有打开调试功能。因之前一直在M4上做程序,所以不知道M3还得配置。

解决方法:

①将boot0=1,boot1=1,下载一个程序,就是刚才的程序就行。

②将boot0=0,boot1=X,这时是可以下载程序的,但下载一次后,又会无法下载,所以要配置sw脚。

三、打开调试口,并设置两个脚为sw模式

image.pngimage.png

这时候就可以多次下载程序了。


超高效率dma 串口调试程序,DMA构造PRINTF函数

wangjun110 回复了问题 • 11 人关注 • 6 个回复 • 3997 次浏览 • 2018-10-25 17:26 • 来自相关话题

STM32F103ZET(基于秉火开发板)+Cubemx(F1 V1.60库)+RTC(修复官方例程日期不更新bug)

HaoYou 回复了问题 • 10 人关注 • 6 个回复 • 2739 次浏览 • 2018-09-11 00:04 • 来自相关话题

stm32实现SSI接口

乌龟也会飞 发表了文章 • 2 个评论 • 1011 次浏览 • 2018-08-22 13:53 • 来自相关话题

本文章采用的是2个IO口来模拟SSI协议接口读取MTS的绝对值编码器数据,经过验证一切oK第一步:首先需要配置io口一个输出 一个为输入第二步:定义较为准确的微秒级别的延时(SSI协议的对延时有较高的要求,两帧数据之间的间隔要求大于16us最好大于1倍以上32us比较保险,故此读取SSI编码器的数据时钟周期必须要小于16us,否则SSI编码器不停的认为开始了新的一帧数据发送,会致使结果读取失败)。可以采用定时器的方式来实现较为准确的延时可以参考http://www.stm32cube.com/article/176 文章 第三步:实现SSI协议接口(具体下一请百度)大概就是空闲没有数据交换时刻数据线和时钟线应该维持为高电平的状态 在第一个时钟下降沿时刻编码器锁存当前的位移值,然后再接下来的每个上升沿脉冲时刻将数据发送发送到数据线上,发送完成后SSI编码器会将数据线拉低16us然后恢复为空闲状态拉高数据线,故此两针数据的读取间隔必须大于16us我采用的是30us,然后空闲手机壳遇到时钟下降沿又是新的一帧数据开始我所实现的MTS传感器的24位数据(故此以下代码为24位SSI编码器读取代码,至于其他位数的编码器时序相同的话只需更改for循环的次数即可)以上提供by荒野大嫖客 如有疑问可以留言或者至cubeMX中文QQ群里 里面的大牛较多以下为FPGA实现SSI接口by 荒野大嫖客 查看全部

本文章采用的是2个IO口来模拟SSI协议接口读取MTS的绝对值编码器数据,经过验证一切oK

第一步:首先需要配置io口一个输出 一个为输入

第二步:定义较为准确的微秒级别的延时(SSI协议的对延时有较高的要求,两帧数据之间的间隔要求大于16us最好大于1倍以上32us比较保险,故此读取SSI编码器的数据时钟周期必须要小于16us,否则SSI编码器不停的认为开始了新的一帧数据发送,会致使结果读取失败)。可以采用定时器的方式来实现较为准确的延时可以参考

http://www.stm32cube.com/article/176 文章 

第三步:实现SSI协议接口(具体下一请百度)大概就是空闲没有数据交换时刻数据线和时钟线应该维持为高电平的状态 在第一个时钟下降沿时刻编码器锁存当前的位移值,然后再接下来的每个上升沿脉冲时刻将数据发送发送到数据线上,发送完成后SSI编码器会将数据线拉低16us然后恢复为空闲状态拉高数据线,故此两针数据的读取间隔必须大于16us我采用的是30us,然后空闲手机壳遇到时钟下降沿又是新的一帧数据开始

我所实现的MTS传感器的24位数据(故此以下代码为24位SSI编码器读取代码,至于其他位数的编码器时序相同的话只需更改for循环的次数即可)


image.png

以上提供by荒野大嫖客 如有疑问可以留言或者至cubeMX中文QQ群里 里面的大牛较多

以下为FPGA实现SSI接口

image.png


image.png


by 荒野大嫖客


STM32 HAL库实现微秒级别延时

乌龟也会飞 发表了文章 • 4 个评论 • 1961 次浏览 • 2018-08-22 13:30 • 来自相关话题

HAL库函数中有延时函数HAL_Delay();进行毫秒级别的延时,但是在实际的开发中编写时序时有时需要进行较为准确的微秒级别延时像SSI协议接口等,本文章采用一个通用定时器的方式实现微秒级别的延时,而不采用滴答定时器是由于HAL库函数中很多地方需要使用滴答定时器进行超时判断,更改滴答配置恐引发意外,另外传统的for循环延时不太准确。首先配置使能定时器(我选择的是定时器6)第二步:配置定时器(当然在此步骤前默认读者已经完成了系统时钟的配置,F1系列我配置为72MHz,那么对于定时器6来说时钟也是72MHz)进行72分频(71+1)那么得出分频后定时器的时钟为1MHz即1us进行数据变化一次,根据这个思想,我将定时器配置为向上计数,自动重装,好方目前位置cubeMX配置方面已经完成了,生成工程就行第三步:打开工程 在tim.c 文本中的begin 和end之间添加以下代码即可/*********************************************功能:实现us级别延时***作者:荒野大嫖客***版本:V1.0*******************************************/void delayXus(uint16_t us){ uint16_t differ=0xffff-us-5; //设定定时器计数器起始值 __HAL_TIM_SET_COUNTER(&htim6,differ); HAL_TIM_Base_Start(&htim6); //启动定时器  while(differ<0xffff-6) //补偿,判断  {    differ=__HAL_TIM_GET_COUNTER(&htim6); //查询计数器的计数值  }  HAL_TIM_Base_Stop(&htim6);}第四步:将void delayXus(uint16_t us);添加至tim.h头文件中 在其他地方引用该头文件即可使用该延时,经过实际测试可得还是比较准备的以上整理提供by 荒野大嫖客 有问题可以一起探讨探讨或者cubemx中文QQ群发表 里面大牛比较多 查看全部

HAL库函数中有延时函数HAL_Delay();进行毫秒级别的延时,但是在实际的开发中编写时序时有时需要进行较为准确的微秒级别延时像SSI协议接口等,本文章采用一个通用定时器的方式实现微秒级别的延时,而不采用滴答定时器是由于HAL库函数中很多地方需要使用滴答定时器进行超时判断,更改滴答配置恐引发意外,另外传统的for循环延时不太准确。

首先配置使能定时器(我选择的是定时器6)

image.png

第二步:配置定时器(当然在此步骤前默认读者已经完成了系统时钟的配置,F1系列我配置为72MHz,那么对于定时器6来说时钟也是72MHz)

image.png

进行72分频(71+1)那么得出分频后定时器的时钟为1MHz即1us进行数据变化一次,根据这个思想,我将定时器配置为向上计数,自动重装,好方目前位置cubeMX配置方面已经完成了,生成工程就行

第三步:打开工程 在tim.c 文本中的begin 和end之间添加以下代码即可

/******************************************

***功能:实现us级别延时

***作者:荒野大嫖客

***版本:V1.0

*******************************************/

void delayXus(uint16_t us)

{

uint16_t differ=0xffff-us-5; //设定定时器计数器起始值

__HAL_TIM_SET_COUNTER(&htim6,differ);

HAL_TIM_Base_Start(&htim6); //启动定时器

  while(differ<0xffff-6) //补偿,判断

  {

    differ=__HAL_TIM_GET_COUNTER(&htim6); //查询计数器的计数值

  }

  HAL_TIM_Base_Stop(&htim6);

}

第四步:将void delayXus(uint16_t us);添加至tim.h头文件中 在其他地方引用该头文件即可使用该延时,经过实际测试可得还是比较准备的


以上整理提供by 荒野大嫖客 有问题可以一起探讨探讨或者cubemx中文QQ群发表 里面大牛比较多


关于stm32cube中UART串口的新库函数的一些东西理解,长久记录

風影旋新月 回复了问题 • 28 人关注 • 10 个回复 • 28861 次浏览 • 2018-08-20 16:24 • 来自相关话题

(直播)基于Freemodbus的灵活应用--Freemodbus的使用技巧和简单改造

zjczm 回复了问题 • 17 人关注 • 13 个回复 • 5902 次浏览 • 2018-07-30 18:03 • 来自相关话题

查表法CRC16和CRC8校验程序,HAL库中的CRC32不能用于modbus校验

回复

匿名用户 回复了问题 • 4 人关注 • 1 个回复 • 3561 次浏览 • 2018-07-24 15:49 • 来自相关话题