(实测可用)STM32CubeMX教程-STM32L431RCT6开发板研究串口通信(SPI flash)

  一、开发板平台简介:

1、开发板资源简介

(1)开发板主芯片型号:STM32L431RCT6

(2)开发板主芯片封装:LQFP-64_10x10x05P

(3)开发板主芯片内核:ARM® Cortex®-M4

(4)开发板主芯片主频:80MHz

(5)开发板主芯片Flash大小:256KB

(6)开发板主芯片RAM大小:64KB

 (7)其他外设:请参考芯片手册https://atta.szlcsc.com/upload/public/pdf/source/20161229/1482979391205.pdfhttps://atta.szlcsc.com/upload/public/pdf/source/20161229/1482979391205.pdf

2、串口简介

         串口全称为串行通讯接口,即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平,串口需要RXD、TXD、GND三根线进行通信。

        (1)我们选用的STM32L431RCT6开发板串口1已通过USB转TLL串口芯片CH340G引出,使用时,只需要用公对公USB线连接电脑即可(注意也得需要安装CH340G驱动),后期验证试验也试验该串口1进行。

        (2)开发板上的其他串口已通过排针引出,为TTL电平,通信的时候需要注意选择对应的电平模块,如USB转TTL串口模块等。

  二、新建工程

1、新建STM32CubeMX基础工程

(1)打开STM32CubeMX,点击“File”-->"New Project"

(2)等待打开主芯片选项界面(大约1分钟时间)。

(3)昨天搜索框中输入(或选择)所需的主芯片型号(因为我们用的是STM32L431RCT6开发板,所以此处现在STM32L431RC),然后在右下角选择STM32L431RCTx(因为开发板主芯片是STM32L431RCT6),左键双击即可打开新建的项目。

(4)选择时钟源。

(1)因为开发板上有8M外部时钟,此处选择外部高速时钟(HSE)。

(2)因为我们没有用到外部低速时钟(LSE),此处不做处理。

2、配置GPIO控制LED

备注:LED灯用来指示系统是否正常工作。

(1)查STM32L431RCT6开发板原理图得LED1控制引脚为PC0,则配置GPIO的引脚PC0。

鼠标左键点击PC0,选择“GPIO_Output”,表示设置该引脚为输出模式。

 

(2)根据自己的需求配置GPIO的参数,如输出方式、输出频率、上拉下拉等。因为GPIO控制LED的要求比较低,此处采用默认参数即可,不用修改。

3、设置串口1参数

1、查原理图得知,串口0使用STM32L431RCT6引脚为PA9-USART1_TX,PA10-USART1_RX,引脚设置如下:

 (1)序号1用来设置串口收发引脚的选择。

(2)序号2-3-4-5-6设置串口参数,如波特率115200、8位、NONE无奇偶校验等。

4、配置SPI Flash接口

(1)查看STM32L431RCT6开发板原理图得知,芯片原理图如下:

 (2)SPI Flash接口对应芯片的PB12、 PB13、 PB14  PB15,芯片引脚配置如下:

PB12:SPI2_NSS,此处设置普通输出IO即可。不能配置成NSS。

PB13:SPI2_SCK

PB14:SPI2_MISO

PB15:SPI_MOSI

 (3)设置SPI引脚参数,并选择 Full-Duplex Master 全双工主模式,此处不开启 NSS 即不使用硬件片选信号。

 

(4)设置SPI基础参数以及时钟。

 

 4、配置项目工程参数

(1)配置时钟树,用于系统内部时钟,以及各个外设时钟等。此处选择外部8M晶振作为主时钟频率,内部最大倍频80MHz。

(2)完成配置工程。

备注:需要注意代码生成过程中的继承关系,如图所示:需要保留开发者自己编写的代码时,请根据配置设置,不然生成代码后会删除自己编写的代码(从这个方面也可以看出开发者备份自己的代码是多么的重要。)

(3)生成代码。

 三、在KEIL 5中编写代码

1、使用KEIL 5(MDK)打开项目工程文件

(1)找到刚才新建工程的存储路径,安装项目名称,打开项目工程。

(2)添加每隔500ms,LED1闪烁一次的系统提示,用于提示程序运行正常。

2、添加使用printf函数打印log的代码

(1)在项目选项中勾选Use Micro LIB选项,勾选该项目后,可以正常使用基本的C语言库。

(2)printf函数会调用fputs函数,fputs函数为弱定义函数,即使用__weak修饰符修饰的函数,可通过自己编写函数定义覆盖原定义,我们在此处重写fputs函数。在usart.c中添加如下代码。

备注:开发者自己的代码需要注意添加位置,一定要放置到一对《USER CODE BEGIN和USER CODE END》之间,避免重新生成工程后被覆盖。

 代码段如下:可直接复制使用。

/* USER CODE BEGIN 1 */

#include <stdio.h>
#ifdef __GNUC__
	#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
	#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
		HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
		return ch;
}


/* USER CODE END 1 */

(3)使用printf打印log数据。需要注意main.c位置添加"stdio.h"头文件(printf的函数声明头文件)。

printf("hello world,this is printf function!\r\n");   //printf 打印函数

 3、在w25qxx.c文件中添加SPI底层驱动函数

#include "main.h"
#include "spi.h"
#include "w25qxx.h"
/**
  * @brief SPI1 读一个字节
  * @param None
  * @retval None
  */
static uint8_t spi2_flash_read_byte(void)
{
    uint8_t t_data, r_data;

    if(HAL_SPI_TransmitReceive(&hspi2, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
    {
        r_data = 0xff;
    }
    return r_data;
}
/**
  * @brief SPI1 写一个字节
  * @param byte 写入的字节
  * @retval 写状态 0成功 1失败
  */
static uint8_t spi2_flash_send_byte(uint8_t byte)
{
    uint8_t r_data;

    if(HAL_SPI_TransmitReceive(&hspi2, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)
    {
      return 1;
    }
    return 0;
}
/**
  * @brief FLASH 写使能
  * @param None
  * @retval None
  */
static void spi2_flash_write_enable(void)
{
    SPI_FLASH_CS_LOW();
    spi2_flash_send_byte(W25X_WriteEnable);
    SPI_FLASH_CS_HIGH();
}
/**
  * @brief FLASH 等待写结束
  * @param None
  * @retval None
  */
static void spi2_flash_wait_for_write_end(void)
{
  uint8_t state = 0;

   SPI_FLASH_CS_LOW();

		spi2_flash_send_byte(W25X_ReadStatusReg);

  do
  {
      state = spi2_flash_read_byte();
  }
  while((state & 0x01) == SET);

  SPI_FLASH_CS_HIGH();
}
/**
  * @brief FLASH 读ID
  * @param None
  * @retval None
  */
uint32_t spi_flash_read_ID(void)
{
  uint32_t temp, temp0, temp1, temp2;

  SPI_FLASH_CS_LOW();
  spi2_flash_send_byte(W25X_JedecDeviceID);

  temp0 = spi2_flash_read_byte();
  temp1 = spi2_flash_read_byte();
  temp2 = spi2_flash_read_byte();

	SPI_FLASH_CS_HIGH();

  temp = (temp0 << 16) | (temp1 << 8) | temp2;

  return temp;
}
/**
  * @brief 读FLASH
  * @param addr 读flash的起始地址
  * @param pdata 读到的数据存放起始地址
  * pdata size 读数据大小
  * @retval None
  */
void spi2_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)
{
   SPI_FLASH_CS_LOW();

  spi2_flash_send_byte(W25X_ReadData);

  spi2_flash_send_byte((addr & 0xFF0000) >> 16);
  spi2_flash_send_byte((addr & 0xFF00) >> 8);
  spi2_flash_send_byte(addr  & 0xFF);

  while (size--)
  {
    *pdata = spi2_flash_read_byte();
    pdata++;
  }

	SPI_FLASH_CS_HIGH();
}
/**
  * @brief 按页写FLASH
  * @param addr 写入flash的起始地址
  * @param pdata 写入数据的起始地址
  * pdata size 写数据大小
  * @retval None
  */
void spi2_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
{
    uint16_t i;

     spi2_flash_write_enable();

     SPI_FLASH_CS_LOW();

    spi2_flash_send_byte(W25X_PageProgram);
    spi2_flash_send_byte((uint8_t)((addr)>>16));
    spi2_flash_send_byte((uint8_t)((addr)>>8));
    spi2_flash_send_byte((uint8_t)addr);

    for(i = 0; i < size; i++)
    {
        spi2_flash_send_byte(pdata[i]);
    }

    SPI_FLASH_CS_HIGH();
    spi2_flash_wait_for_write_end();
}
/**
  * @brief 写FLASH
  * @param addr 写入flash的起始地址
  * @param pdata 写入数据的起始地址
  * pdata size 写数据大小
  * @retval None
  */
void spi2_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)
{
    uint32_t page_remain;

    page_remain = 256 - addr%256;

    if(size <= page_remain)
    {
        page_remain = size;
    }
    while(1)
    {
        spi2_flash_page_write(addr, pdata, page_remain);

        if(size == page_remain)
            break;
         else
        {
             pdata += page_remain;
             addr += page_remain;

             size -= page_remain;
            if(size > 256)
                page_remain = 256;
            else
                page_remain = size;
        }
    }
}
/**
  * @brief 擦除FLASH扇区
  * @param sector_addr 扇区的起始地址
  * @retval None
  */
void spi2_flash_sector_erase(uint32_t sector_addr)
{
    spi2_flash_write_enable();
    spi2_flash_wait_for_write_end();

    SPI_FLASH_CS_LOW();
    spi2_flash_send_byte(W25X_SectorErase);
    spi2_flash_send_byte((sector_addr & 0xFF0000) >> 16);
    spi2_flash_send_byte((sector_addr & 0xFF00) >> 8);
    spi2_flash_send_byte(sector_addr & 0xFF);

    SPI_FLASH_CS_HIGH();

    spi2_flash_wait_for_write_end();
}

/**
  * @brief 擦除FLASH块
  * @param None
  * @retval None
  */
void spi2_flash_block_erase(void)
{
    spi2_flash_write_enable();

    SPI_FLASH_CS_LOW();
    spi2_flash_send_byte(W25X_ChipErase);
    SPI_FLASH_CS_HIGH();

    spi2_flash_wait_for_write_end();
}

4、在main.c中添加功能函数

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint32_t w25q_chip_id=0;					                                        	//读取芯片ID
	uint8_t  onebyte_read[8]={0};						                                    //读取的数据
	uint8_t  onebyte_write[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};				//写入的数据
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);								//初始化LED1
	w25q_chip_id=spi_flash_read_ID();																//读取flash芯片ID
	HAL_Delay(100);
	printf("hello world,spi_flash_read_ID==0x%0x!\r\n",w25q_chip_id); //printf 芯片ID
	spi2_flash_sector_erase(0x0);																			//写之前需要先擦除扇区																	
	spi2_flash_write(0x0,onebyte_write,8);														//写八个字节
	spi2_flash_read(0x00,onebyte_read,8);															//读取写入的字节
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
				HAL_Delay(500);
				HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);									//每隔500ms闪烁一次LED1
				printf("hello world,spi2_flash_read =0x%0x,0x%0x,0x%0x,0x%0x,0x%0x,0x%0x,0x%0x,0x%0x\r\n",onebyte_read[0],onebyte_read[1],onebyte_read[2],onebyte_read[3],onebyte_read[4],onebyte_read[5],onebyte_read[6],onebyte_read[7]);   //printf 
				
  }
  /* USER CODE END 3 */
}

 5、设置编程仿真下载模式

(1)选择Options for target ...>>Debug>>J-Link/J-JTRACE Cortex,点击Settings>>选择Port(SW),可以看到搜索成功SW Device,表示芯片可用,可以下载。

(2)点击Download(或者快捷键F8),即可下载程序。

(3) 如果下载程序后,没有看到LED1灯闪烁,可以按下述方式设置一下(Reset and run表示下载后自动复位和重启运行)。或者重新彻底断电再次上电(或按开发板的Reset按键复位MCU即可)。

 

6、查看串口printf函数打印log效果

(1)设置串口助手参数为:115200、NONE、8、 1(和代码中串口初始化参数一致)。

(2)设置成功后,就可以看到串口打印的效果。每隔500ms闪烁LED1指示灯一次,且串口输出一个log。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式ZYXC

谢爷的赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值