[MM32软件] MM32SPIN0230的USART实现SPI读写Flash存储器

[复制链接]
2412|5
 楼主| MindMotion 发表于 2023-11-6 16:35 | 显示全部楼层 |阅读模式
本帖最后由 MindMotion 于 2023-11-6 16:35 编辑

1. MM32SPIN0230的USART简介

MM32SPIN0230通用同步/异步收发器(USART)可以灵活地与外部设备进行全双工数据交换。通过内置波特率(包含整数及小数设定)发生器, USART 可以支持宽范围的波特率。
USART 支持异步模式(UART)、同步模式。其中 UART 支持单线半双工通信, UART 和同步模式支持调制解调器(CTS/RTS)操作。

2. MM32SPIN0230的USART功能框图

USART 的功能框图如下图1所示,由寄存器相关的控制单元、收发数据控制器、时钟控制器、硬件流控制单元以及引脚控制逻辑单元组成。

图片1.png

图1 MM32SPIN0230USART功能框图

3. USART的同步模式

通过配置,USART_CR1.SAS 位为‘1’来使能同步模式(时钟引脚功能将同时有效)。
在同步模式下, USART_CR3.HDSEL 位应配置为‘0’。
同步模式支持主模式和从模式:主模式时使用内部波特率生成器生成的时钟,同时输出时钟;从模式时由 SCLK 引脚输入时钟。 USART 在同步模式下,能与 SPI 实现数据通信(此时,用户应配置 SPI 与USART 的时钟极性、时钟相位为一致)。

4. USART的时钟

USART的时钟挂载在APB1总线上,由APB1总线提供工作时钟,以配置 USART_CR2.CLKEN 位为‘1’来使能时钟引脚功能, 同时根据 USART_CR3.CKINE 位配置来选择使用内部波特率时钟或从 SCLK 引脚输入时钟,以进行数据通信。
当选择内部波特率时钟时,可通过 SCLK 引脚输出同步时钟。
1 帧数据的收发包含 8 个时钟脉冲。
当 RE 和 TE 都为‘0’, 时钟输出会停止, 并固定在 USART_CR2.CPOL 配置的电平。
通过配置 USART_CR2.CPOL 位选择时钟极性。
通过配置 USART_CR2.CPHA 位选择外部时钟相位。

5. USART时钟同步功能

SCLK 引脚作为发送器的时钟输出时,仅在数据段输出时钟,一帧数据输出 8 个时钟脉冲,最后一位发送完后,通信线保持最后一位的值,时钟输出固定在高电平或低电平(由 CPOL 位决定)。
USART 接收器在同步模式下的工作方式与异步模式下不同。如果 RE=1,则数据在 SCLK 变化边沿上采样(上升或下降沿,取决于 CPOL 和 CPHA 位配置情况),而不会进行任何过采样。此时必须确保足够的建立时间和保持时间,以符合时序要求(类同于 SPI 协议)。
内部时钟源时,内部波特率生成器生成的波特率计算公式为:
图片4.png
其中通信波特率的单为 MBps; PCLK 为内部时钟源的频率; MFD 为 波特率寄存器 USART_BRR 中整数分频(注意, 在同步模式下应配置 MFD ≥ 2, 且小数分频 FFD 无效, 用户应配置 FFD[3: 0] 位为4’h0)。
使用内部时钟源且 MFD=2 时,同步模式的最高波特率为 PCLK/8(MBps)。
外部时钟源时,要求外部输入时钟的最大频率为 PCLK/8( MHz),此时最高波特率也为 PCLK/8(MBps)。

6. USART的特性描述

如下图2所示为USART的数据帧类型示意图,当USART用作同步通信时,可通过配置同步时钟引脚即USART_SCK引脚的时钟极性和时钟相位实现SPI功能的通信。此时没有起始位,校验位以及停止位功能,仅支持8位。通过配置时钟极性CPOL和时钟相位CPHA可以实现基于USART的SPI通信的工作模式0-3。

图片2.png

图2 MM32SPIN0230 USART数据帧类型示意图

7.  USART同步模式实现SPI通信的配置步骤

MM32SPIN0230有一路USART1,USART1同步模式实现SPI通信的配置步骤如下所示。
  • 使能USART1的时钟和用作USART1的GPIO时钟
  • 配置GPIO复用为USART1功能
  • 配置一路普通GPIO为推挽输出模式用于USART同步通信的CS引脚。
  • 配置 USART1_CR1.SAS 位为‘1’来使能同步模式(时钟引脚功能将同时有效)。
  • 配置 USART1_CR1.MLS 位为‘0’或 为‘1’来选择数据格式为LSB或MSB
  • 配置 USART1_CR3.HDSEL位为‘0’来选择数据通信为全双工模式
  • 配置 USART1_CR2.CPOL位为‘0’ 或 为‘1’设置时钟空闲时为低电平或高电平
  • 配置 USART1_CR2.CPHA位为‘0’ 或 为‘1’设置在时钟第一个变化沿捕获数据或在时钟第二个变化沿捕获数据
  • 配置USART1_BRR波特率寄存器小数分频FFD[3:0]为0(USART同步模式,波特率小数分频无效)
  • 配置USART1同步模式波特率,USART1_BRR波特率寄存器整数分频MFD[15:0],需配置MFD>=2
  • 使能USART1接收和发送中断以及NVIC中断优先级(如有使用到USART接收和发送中断功能)
  • 使能USART1功能。
  • 编写应用层USART1同步通信模式下数据接收、数据发送函数以及中断处理接收和中断发送函数。(如使能了接收和发送中断功能)

8. USART同步模式实现SPI通信读写W25Q32存储器

根据以上配置步骤USART1同步模式实现SPI通信的配置步骤,使用MM32SPIN0230的库函数配置USART1为同步模式主机功能实现SPI通信读写W25Q32 Flash存储器,分别配置GPIO PA11复用为USART1_TX功能,PA12复用为USART1_RX功能,PB2复用为USART1_SCLK功能(注:USART1用作同步模式主机功能PA11 USART1_TX当做SPI_MOSI引脚使用,PA12 USART1_RX当做SPI_MISO引脚使用,PB2 USART1_SCLK当做SPI_SCL使用)并配置GPIO PB4为推挽输出模式当做SPI_CSS引脚使用。
USART1同步通信模式实现SPI通信的初始化代码如下所示:

  1. #define SPI_FLASH_CS_H() GPIO_WriteBit(GPIOB, GPIO_Pin_4, Bit_SET)
  2. #define SPI_FLASH_CS_L() GPIO_WriteBit(GPIOB, GPIO_Pin_4, Bit_RESET)
  3. uint16_t DeviceID = 0;                                /* W25Q64 Device ID */
  4. uint32_t JEDEC_ID = 0;                                /* W25Q64 JEDEC ID */
  5. void USART_Configure(uint32_t Baudrate)
  6. {
  7.     GPIO_InitTypeDef GPIO_InitStruct;
  8.     NVIC_InitTypeDef NVIC_InitStruct;

  9.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART1, ENABLE);
  10.     USART_SyncMasterConfig(USART1, USART_Clock_Idle_Low, USART_Clock_Phase_1Edge, Baudrate);    /* USART Init configure SPI Mode0 */
  11.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  12.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

  13.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_5);   /* PA11 AF USART_TX for SPI_MOSI function */
  14.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_5);   /* PA12 AF USART_RX for SPI_MISO function */
  15.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource2,  GPIO_AF_6);   /* PB2 AF USART_SCLK for SPI_SCLK function */

  16.     GPIO_StructInit(&GPIO_InitStruct);
  17.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_4;        
  18.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  19.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_Out_PP;  /* PB4 for SPI_CSS function */
  20.     GPIO_Init(GPIOB, &GPIO_InitStruct);

  21.     SPI_FLASH_CS_H();                           /* Set PB4 for SPI_CSS High leve */
  22.     GPIO_StructInit(&GPIO_InitStruct);
  23.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_11;
  24.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  25.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
  26.     GPIO_Init(GPIOA, &GPIO_InitStruct);

  27.     GPIO_StructInit(&GPIO_InitStruct);
  28.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_12;
  29.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  30.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_FLOATING;
  31.     GPIO_Init(GPIOA, &GPIO_InitStruct);

  32.     GPIO_StructInit(&GPIO_InitStruct);
  33.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_2;
  34.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  35.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
  36.     GPIO_Init(GPIOB, &GPIO_InitStruct);

  37.     NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
  38.     NVIC_InitStruct.NVIC_IRQChannelPriority = 0x01;
  39.     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  40.     NVIC_Init(&NVIC_InitStruct);
  41.     USART_Cmd(USART1, ENABLE);
  42. }

USART1同步通信模式中断接收数据,代码如下所示。

  1. void USART_Synchronous_RxData_Interrupt(uint8_t *Buffer, uint8_t Length)
  2. {
  3.     uint8_t i = 0;

  4.     for (i = 0; i < Length; i++)
  5.     {
  6.         USART_RxStruct.Buffer[i] = 0;
  7.         USART_TxStruct.Buffer[i] = 0;
  8.     }
  9.     USART_RxStruct.Length = Length;
  10.     USART_RxStruct.CurrentCount = 0;
  11.     USART_RxStruct.CompleteFlag = 0;
  12.     USART_TxStruct.Length = Length;
  13.     USART_TxStruct.CurrentCount = 0;
  14.     USART_TxStruct.CompleteFlag = 0;
  15.     USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  16.     USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
  17.     while (0 == USART_TxStruct.CompleteFlag)
  18.     {
  19.     }
  20.     while (0 == USART_RxStruct.CompleteFlag)
  21.     {
  22.     }
  23.     for (i = 0; i < Length; i++)
  24.     {
  25.         Buffer[i] = USART_RxStruct.Buffer[i];
  26.     }
  27. }  

USART1同步通信模式中断发送数据,代码如下所示。

  1. void USART_Synchronous_TxData_Interrupt(uint8_t *Buffer, uint8_t Length)
  2. {
  3.     uint8_t i = 0;

  4.     for (i = 0; i < Length; i++)
  5.     {
  6.         USART_RxStruct.Buffer[i] = 0;
  7.         USART_TxStruct.Buffer[i] = Buffer[i];
  8.     }
  9.     USART_RxStruct.Length = Length;
  10.     USART_RxStruct.CurrentCount = 0;
  11.     USART_RxStruct.CompleteFlag = 0;
  12.     USART_TxStruct.Length = Length;
  13.     USART_TxStruct.CurrentCount = 0;
  14.     USART_TxStruct.CompleteFlag = 0;
  15.     USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  16.     USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
  17.     while (0 == USART_TxStruct.CompleteFlag)
  18.     {
  19.     }
  20.     while (0 == USART_RxStruct.CompleteFlag)
  21.     {
  22.     }
  23. }

USART1同步通信模式读SPI Flash,代码如下所示

  1. void SPI_FLASH_RxBuffer(uint8_t *Buffer, uint8_t Length)
  2. {
  3.     if (Length)
  4.     {
  5.         USART_Synchronous_RxData_Interrupt(Buffer, Length);
  6.     }
  7. }     

USART1同步通信模式写SPI Flash,代码如下所示

  1. void SPI_FLASH_TxBuffer(uint8_t *Buffer, uint8_t Length)
  2. {
  3.     if (Length)
  4.     {
  5.         USART_Synchronous_TxData_Interrupt(Buffer, Length);
  6.     }
  7. }     

USART1同步通信模式处理中断接收和发送命令和读写数据,即读写SPI Flash,代码如下所示

  1. void USART1_IRQHandler(void)
  2. {
  3.     uint8_t RxData = 0;

  4.     if (RESET != USART_GetITStatus(USART1, USART_IT_RXNE))
  5.     {
  6.         RxData = USART_ReceiveData(USART1);

  7.         if (0 == USART_RxStruct.CompleteFlag)
  8.         {
  9.             USART_RxStruct.Buffer[USART_RxStruct.CurrentCount++] = RxData;
  10.             if (USART_RxStruct.CurrentCount == USART_RxStruct.Length)
  11.             {
  12.                 USART_RxStruct.CompleteFlag = 1;
  13.                 USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
  14.                 USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
  15.             }
  16.         }
  17.     }
  18.     if (RESET != USART_GetITStatus(USART1, USART_IT_TXE))
  19.     {
  20.         if (0 == USART_TxStruct.CompleteFlag)
  21.         {
  22.             USART_SendData(USART1, USART_TxStruct.Buffer[USART_TxStruct.CurrentCount++]);
  23.             if (USART_TxStruct.CurrentCount == USART_TxStruct.Length)
  24.             {
  25.                 USART_TxStruct.CompleteFlag = 1;
  26.             }
  27.         }
  28.     }
  29. }

USART1同步通信模式SPI Flash写使能,代码如下所示

  1. void SPI_FLASH_WriteEnable(void)
  2. {
  3.     uint8_t Command = 0x06;

  4.     SPI_FLASH_CS_L();
  5.     SPI_FLASH_TxBuffer(&Command, 0x01);
  6.     SPI_FLASH_CS_H();
  7. }

USART1同步通信模式判断SPI Flash是否处于总线忙转态,代码如下所示

  1. void SPI_FLASH_WaitBusy(void)
  2. {
  3.     uint8_t  Status     = 0;
  4.     uint8_t  Command[2] =
  5.     {
  6.         0x05, 0xFF
  7.     };
  8.     uint32_t Timeout = 0;

  9.     do
  10.     {
  11.         SPI_FLASH_CS_L();
  12.         SPI_FLASH_TxBuffer(Command, 0x02);
  13.         SPI_FLASH_RxBuffer(&Status, 0x01);
  14.         SPI_FLASH_CS_H();

  15.         if (Timeout++ > 0xFFFF)
  16.         {
  17.             break;
  18.         }
  19.     }
  20.     while (Status & 0x01);
  21. }

USART1同步通信模式读SPI Flash DeviceID,代码如下所示

  1. void SPI_FLASH_ReadDeviceID(void)
  2. {
  3.     uint8_t  Command[4] =
  4.     {
  5.         0x90, 0xFF, 0xFF, 0x00
  6.     };
  7.     uint8_t  Buffer[2];
  8.     SPI_FLASH_CS_L();
  9.     SPI_FLASH_TxBuffer(Command, 0x04);
  10.     SPI_FLASH_RxBuffer(Buffer, 0x02);
  11.     SPI_FLASH_CS_H();
  12.     DeviceID   = Buffer[0];
  13.     DeviceID <<= 8;
  14.     DeviceID  |= Buffer[1];
  15. }

USART1同步通信模式读SPI Flash JEDEC ID,代码如下所示

  1. void SPI_FLASH_ReadJEDEC_ID(void)
  2. {
  3.     uint8_t  Command = 0x9F;
  4.     uint8_t  Buffer[3];

  5.     SPI_FLASH_CS_L();
  6.     SPI_FLASH_TxBuffer(&Command, 0x01);
  7.     SPI_FLASH_RxBuffer(Buffer, 0x03);
  8.     SPI_FLASH_CS_H();
  9.     JEDEC_ID   = Buffer[0];
  10.     JEDEC_ID <<= 8;
  11.     JEDEC_ID  |= Buffer[1];
  12.     JEDEC_ID <<= 8;
  13.     JEDEC_ID  |= Buffer[2];
  14. }

USART1同步通信模式SPI Flash 扇区擦除,代码如下所示

  1. void SPI_FLASH_SectorErase(uint16_t Index)
  2. {
  3.     uint8_t  Command[4] =
  4.     {
  5.         0x20, 0x00, 0x00, 0x00
  6.     };
  7.     uint32_t Address = Index * 4 * 1024;

  8.     Command[1] = (uint8_t)((Address >> 16) & 0x000000FF);
  9.     Command[2] = (uint8_t)((Address >> 8) & 0x000000FF);
  10.     Command[3] = (uint8_t)((Address >> 0) & 0x000000FF);
  11.     SPI_FLASH_WriteEnable();
  12.     SPI_FLASH_CS_L();
  13.     SPI_FLASH_TxBuffer(Command, 4);
  14.     SPI_FLASH_CS_H();
  15.     SPI_FLASH_WaitBusy();
  16. }

USART1同步通信模式快速读SPI Flash,代码如下所示

  1. void SPI_FLASH_FastRead(uint32_t Address, uint8_t *Buffer, uint32_t Length)
  2. {
  3.     uint8_t Command[5] =
  4.     {
  5.         0x0B, 0x00, 0x00, 0x00, 0xFF
  6.     };
  7.     Command[1] = (uint8_t)((Address >> 16) & 0x000000FF);
  8.     Command[2] = (uint8_t)((Address >> 8) & 0x000000FF);
  9.     Command[3] = (uint8_t)((Address >> 0) & 0x000000FF);
  10.     SPI_FLASH_CS_L();
  11.     SPI_FLASH_TxBuffer(Command, 0x05);
  12.     SPI_FLASH_RxBuffer(Buffer, Length);
  13.     SPI_FLASH_CS_H();
  14. }

USART1同步通信模式页编程SPI Flash,代码如下所示

  1. void SPI_FLASH_PageProgram(uint32_t Address, uint8_t *Buffer, uint32_t Length)
  2. {
  3.     uint8_t Command[4] =
  4.     {
  5.         0x02, 0x00, 0x00, 0x00
  6.     };
  7.     Command[1] = (uint8_t)((Address >> 16) & 0x000000FF);
  8.     Command[2] = (uint8_t)((Address >> 8) & 0x000000FF);
  9.     Command[3] = (uint8_t)((Address >> 0) & 0x000000FF);
  10.     SPI_FLASH_WriteEnable();
  11.     SPI_FLASH_CS_L();
  12.     SPI_FLASH_TxBuffer(Command, 0x04);
  13.     SPI_FLASH_TxBuffer(Buffer, Length);
  14.     SPI_FLASH_CS_H();
  15.     SPI_FLASH_WaitBusy();
  16. }

验证USART1同步通信模式读写W25Q32 SPI Flash,本实例在MDK Keil环境下编译验演示,在main函数中调用读写W25Q32 SPI Flash的函数,代码如下所示:

  1. int main(void)
  2. {
  3.     uint8_t i = 0;
  4.     uint8_t EraseBuffer[100], WriteBuffer[100], ReadBuffer[100];
  5.     USART_RxStruct.CompleteFlag = 0;
  6.     USART_TxStruct.CompleteFlag = 1;
  7.     USART_Configure(8000000); /* Configure USART Read/write SPI Flash baud rate 8M */
  8.     SPI_FLASH_ReadDeviceID();
  9.     SPI_FLASH_ReadJEDEC_ID();
  10.     SPI_FLASH_SectorErase(0); /* USART erases the SPI Flash sector */
  11.     SPI_FLASH_FastRead(0, EraseBuffer, 100);
  12.     for (i = 0; i < 100; i++)
  13.     {
  14.         WriteBuffer[i] = i;
  15.     }
  16.     SPI_FLASH_PageProgram(0, WriteBuffer, 100); /* USART page programming SPI Flash writes 100 bytes of data */
  17.     SPI_FLASH_FastRead(0, ReadBuffer, 100); /* The USART reads 100 bytes of data written to the SPI Flash */

  18.     PLATFORM_DeInitUSART1();
  19.     PLATFORM_InitConsole(115200);
  20.     printf("\r\n\r\n");
  21.     printf("\r\nSPI Flash DeviceID : 0x%04x", DeviceID);
  22.     printf("\r\nSPI Flash JEDEC ID : 0x%06x", JEDEC_ID);
  23.     printf("\r\nSPI FLASH Sector Erase...");
  24.     printf("\r\nSPI FLASH Read...");
  25.     for (i = 0; i < 100; i++)
  26.     {
  27.         if (0 == (i % 10))
  28.         {
  29.             printf("\r\n");
  30.         }
  31.         printf("0x%02x ", EraseBuffer[i]);
  32.     }
  33.     printf("\r\nSPI FLASH Page Program...");
  34.     printf("\r\nSPI FLASH Read...");
  35.     for (i = 0; i < 100; i++)
  36.     {
  37.         if (0 == (i % 10))
  38.         {
  39.             printf("\r\n");
  40.         }
  41.         printf("0x%02x ", ReadBuffer[i]);
  42.     }
  43.     while (1)
  44.     {
  45.     }
  46. }

使用MM32-LINK Mini调试下载工具连接MM32SPIN0230 MiniBoard,板子USB口连接USB串口工具连接到电脑端,打开串口调试助手,并配置串口波特率为115200,按快捷键F7编译工程,编译成功后按快捷键F8下载程序到MM32SPIN0230 MiniBoard,如下图3所示,串口调试助手分别打印输出了W25Q32 SPI Flash的DeviceID和JEDEC ID,USART1同步通信模式扇区擦除W25Q32 SPI Flash数据后读出的100字节0xFF数据(说明擦除成功),以及USART1同步通信模式写入到W25Q32 SPI Flash中的0-99共100字节数据(每行10字节,共10行),读出了写入的0-99共100字节数据,写入和读出的数据一致即0-99对应十六进制为0x00-0x63。

图片3.png

图3 测试结果

评论

@luobeihai :进入页面后,点击“工具和软件”,就可以看到下载按钮  发表于 2023-11-8 09:18
@luobeihai :https://www.mindmotion.com.cn/products/mm32mcu/mm32spin/mm32spin_specific_mcu/mm32spin0230/ 以上是MM32SPIN0230 库函数和例程下载地址,可以在USART例程里找到。  发表于 2023-11-8 09:16
感谢分享!没想到USART也可以读写SPI Flash。请问这个demo哪里下载?  发表于 2023-11-7 14:55
rgjinxuan 发表于 2023-11-10 18:38 | 显示全部楼层
感谢分享
weifeng90 发表于 2023-11-11 10:35 来自手机 | 显示全部楼层
USART怎么读取SPI接口的FLASH?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司
简介:上海灵动微电子股份有限公司成立于 2011 年,是中国本土通用 32 位 MCU 产品及解决方案供应商。 灵动股份的 MCU 产品以 MM32 为标识,基于 Arm Cortex-M 系列内核,自主研发软硬件和生态系统。目前已量产近 300 多款型号,累计交付超 4 亿颗,在本土通用 32 位 MCU 公司中位居前列。

93

主题

111

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部