cubeMX配置stm32 F103 实现串口IAP

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

分享一个串口的使用技巧(单字节中断后续字节处理)

自带腹肌的西装 回复了问题 • 10 人关注 • 5 个回复 • 3620 次浏览 • 4 天前 • 来自相关话题

程序中DA配置正确后,开启DA但是程序不设置DA的value值,电压会输出多少?

黑牛 回复了问题 • 2 人关注 • 2 个回复 • 2124 次浏览 • 5 天前 • 来自相关话题

I2C实现,不定长读写 测试通过

admin 回复了问题 • 1 人关注 • 1 个回复 • 76 次浏览 • 2019-03-15 23:10 • 来自相关话题

STM32cubeMx配置I2C出现BUSY问题的解决办法

hezheng 回复了问题 • 4 人关注 • 2 个回复 • 1867 次浏览 • 2019-03-15 09:05 • 来自相关话题

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

Odyssey 回复了问题 • 5 人关注 • 2 个回复 • 3865 次浏览 • 2019-03-14 14:05 • 来自相关话题

STM32HAL库SPI的16位数据中断发送与接收

老是忙 发表了文章 • 0 个评论 • 333 次浏览 • 2019-01-29 11:56 • 来自相关话题

HAL库的SPI发送接收函数的确令人迷惑,明明支持16位传输,却必须使用8位的指针。如果没能正确理解SPI发送接收函数,很容易导致程序接入HardFault_Handler中断死循环。    最近用到STM32F407的HAL编程,SPI通讯的外设要求16位通讯,对HAL库的SPI的16位通讯做了个深入研究。发现HAL库提供的函数除了入口参数含义不太明确,还是很好用的。下面以中断方式发送接收
uint16_t 数据为例,阐述函数调用的过程和要点。   1、
首先,建立发送和接收缓冲区,用16位的数组: uint16_t SPI_TxBuff[1],
SPI_RxBuff[1];   2、SPI初始化: HAL_SPI_DeInit(&hspi1);   //SPI1 复位 hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction =
SPI_DIRECTION_2LINES;   //双向通讯 hspi1.Init.DataSize =
SPI_DATASIZE_16BIT;       //16位传输 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler =
SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation =
SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) {   Error_Handler(); } 其中,DataSize 设为16位。  3、在程序中的调用: SPI_TxBuff[0] =
data;   //data
是uint16_t 类型,复制到发送缓存。 HAL_SPI_TransmitReceive_IT(&hspi1,
(uint8_t*)SPI_TxBuff, (uint8_t*)SPI_RxBuff, 1);
//中断方式的传输 注意1:一定要用 (uint8_t*)
临时改变SPI_TxBuff和SPI_RxBuff的类型。 经对函数内部分析,实际执行时,将把这两个指针重新变换为( uint16_t *) 。 注意2:DataSize 必须是按 uint16_t
数据的数量来设置,而不是按uint8_t, 。此例中,要传输一个16位数据,所以 DataSize 设为1 。.  4、修改SPI中断程序(在stm32f4xx_it.c中)。void SPI1_IRQHandler(void){  HAL_SPI_IRQHandler(&hspi1);    //如果接收完毕,处理数据   if(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY)   {      IT_ProcessingData();   //接收完成的数据已经保存在 SPI_RxBuff[0], 调用此函数进行处理   } }  注意:
HAL_SPI_TransmitReceive_IT() 函数将产生两次中断!
第一次开始发送(接收)数据;当发送(接收)完成后产生第二次中断。我们在中断程序中,用     HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY来判断是否是第二次,也就是传输完成后的中断,是的话,就调用数据处理函数。 查看全部

HAL库的SPI发送接收函数的确令人迷惑,明明支持16位传输,却必须使用8位的指针。如果没能正确理解SPI发送接收函数,很容易导致程序接入HardFault_Handler中断死循环。

    最近用到STM32F407的HAL编程,SPI通讯的外设要求16位通讯,对HAL库的SPI的16位通讯做了个深入研究。发现HAL库提供的函数除了入口参数含义不太明确,还是很好用的。下面以中断方式发送接收
uint16_t 数据为例,阐述函数调用的过程和要点。

   1、
首先,建立发送和接收缓冲区,用16位的数组:

 uint16_t SPI_TxBuff[1],
SPI_RxBuff[1];


   2、SPI初始化:

 HAL_SPI_DeInit(&hspi1);   //SPI1 复位

 hspi1.Instance = SPI1;
 hspi1.Init.Mode = SPI_MODE_MASTER;
 hspi1.Init.Direction =
SPI_DIRECTION_2LINES;   //双向通讯
 hspi1.Init.DataSize =
SPI_DATASIZE_16BIT;       //16位传输
 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
 hspi1.Init.NSS = SPI_NSS_SOFT;
 hspi1.Init.BaudRatePrescaler =
SPI_BAUDRATEPRESCALER_16;
 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
 hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
 hspi1.Init.CRCCalculation =
SPI_CRCCALCULATION_DISABLE;
 hspi1.Init.CRCPolynomial = 10;
 if (HAL_SPI_Init(&hspi1) != HAL_OK)
 {
   Error_Handler();
 }

 其中,DataSize 设为16位。


  3、在程序中的调用:

 SPI_TxBuff[0] =
data;   //data
是uint16_t 类型,复制到发送缓存。
 HAL_SPI_TransmitReceive_IT(&hspi1,
(uint8_t*)SPI_TxBuff, (uint8_t*)SPI_RxBuff, 1);
//中断方式的传输


 注意1:一定要用 (uint8_t*)
临时改变SPI_TxBuff和SPI_RxBuff的类型。 经对函数内部分析,实际执行时,将把这两个指针重新变换为( uint16_t *) 。

 注意2:DataSize 必须是按 uint16_t
数据的数量来设置,而不是按uint8_t, 。此例中,要传输一个16位数据,所以 DataSize 设为1 。.


  4、修改SPI中断程序(在stm32f4xx_it.c中)。

void SPI1_IRQHandler(void)
{
  HAL_SPI_IRQHandler(&hspi1);
 
   //如果接收完毕,处理数据
   if(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY)
   {
      IT_ProcessingData();   //接收完成的数据已经保存在 SPI_RxBuff[0], 调用此函数进行处理
   }
 
}

  注意:
HAL_SPI_TransmitReceive_IT() 函数将产生两次中断!
第一次开始发送(接收)数据;当发送(接收)完成后产生第二次中断。我们在中断程序中,用

     HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_READY

来判断是否是第二次,也就是传输完成后的中断,是的话,就调用数据处理函数。



CubeMX使用高级定时的重复计数功能实现制定个数脉冲PWM

回复

乌龟也会飞 发起了问题 • 1 人关注 • 0 个回复 • 240 次浏览 • 2019-01-28 16:56 • 来自相关话题

CubeMX配置定时器产生指定个数的脉冲

乌龟也会飞 发表了文章 • 4 个评论 • 358 次浏览 • 2019-01-28 16:07 • 来自相关话题

1、配置定时器PWM输出2、配置定时参数并开启中断3、生成工程并定义一个全局变量来保存要发送脉冲的个数uint8_t PWMnum;4、使用库函数封装一个设置脉冲个数并开启PWM输出的函数void setPWMNum(uint8_t num) {    PWMnum = num;    HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_1);}5、重写PWM输出完成回调函数void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {    static uint8_t cnt = 0;    if(htim->Instance == htim1.Instance) {        cnt++;        if(cnt == PWMnum) {            HAL_TIM_PWM_Stop_IT(&htim1, TIM_CHANNEL_1);    PWMnum = 0;        }    }}6,在main函数中调用setPWMNum来启动PWM输出setPWMNum(5);设置为输出5个脉冲编译下载7、通过逻辑分析仪捕获的波形可以看书其只输出我们程序中制定个数的脉冲,另外可以将调整占空比,频率的代码也集成到一起这样更有实用性 查看全部

1、配置定时器PWM输出

image.png

2、配置定时参数并开启中断

image.png

3、生成工程并定义一个全局变量来保存要发送脉冲的个数

uint8_t PWMnum;

4、使用库函数封装一个设置脉冲个数并开启PWM输出的函数

void setPWMNum(uint8_t num) {

    PWMnum = num;

    HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_1);

}


5、重写PWM输出完成回调函数

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {

    static uint8_t cnt = 0;

    if(htim->Instance == htim1.Instance) {

        cnt++;

        if(cnt == PWMnum) {

            HAL_TIM_PWM_Stop_IT(&htim1, TIM_CHANNEL_1);

   PWMnum = 0;

        }

    }

}

6,在main函数中调用setPWMNum来启动PWM输出setPWMNum(5);设置为输出5个脉冲编译下载

7、image.png

通过逻辑分析仪捕获的波形可以看书其只输出我们程序中制定个数的脉冲,

另外可以将调整占空比,频率的代码也集成到一起这样更有实用性


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

Wise Up 回复了问题 • 13 人关注 • 14 个回复 • 22293 次浏览 • 2019-01-18 17:49 • 来自相关话题

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

大博哥 回复了问题 • 8 人关注 • 5 个回复 • 4096 次浏览 • 2019-01-16 22:07 • 来自相关话题

发现CubeMX有一个BUG

admin 回复了问题 • 2 人关注 • 1 个回复 • 345 次浏览 • 2019-01-16 20:12 • 来自相关话题

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

牙子男男 发表了文章 • 2 个评论 • 321 次浏览 • 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 个评论 • 1342 次浏览 • 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

君莫笑 发表了文章 • 3 个评论 • 412 次浏览 • 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荒野大嫖客