STM32F103配合STM32CubeMX实现SPI读写flash

本人采用的是正点原子的精英STM32F103开发板,其包含一块W25Q128型号的flash芯片。该flash与STM32F103的SPI2相连。

下面根据正点原子提供的开发指南文档,实现FreeRTOS的SPI实验版本。

芯片型号为:STM32F103ZET6

启用的模块有USART1,SPI2,其中SPI2的CS片选采用软件控制,是PB12管脚。

此外,为了触发读写flash操作,这里使用KEY0(PE4)作为触发开关。

1. 启动STM32CubeMX选择芯片型号

打开CubeMX后,在左侧搜索框输入型号STM32F103,即可看到有两种型号,选择匹配的STM32F103ZETx即可。

2. 选好芯片型号,点击右上角start project,开始配置

1)配置USART1

在左上角的搜索框,输入usart,选择USART1,并按照下图进行配置(未截图表示无改动):

 

2)对SPI2进行配置

时钟极性CPOL=1,串行同步时钟的空闲状态为高电平;
时钟相位CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样;
CRC 值计算的多项式的值参照教程设置为7,也就是X0+X1+X2,可参照STM32CubeMX工具配置SPI的CRC时数字和表达式转换。亲测,其实使用默认的X1+X3也是可以的,应该是值只要大于1即可,什么表达式都可以。

 3)对GPIO管脚进行配置

KEY0(PE4)作为触发开关,配置为input;

PB12作为SPI12的片选CS,配置为output。

3.时钟配置

时钟部分暂时不动,按照默认配置即可。

4.选择生成代码格式

1)保存工程

点击左上角file-->save,选择路径,保存工程

 2)生成初始代码

根据使用的开发工具IDE类型,选择对应的生成格式,点击generate,完成初始代码的生成。

5.usart1功能测试

#include <stdio.h>
#include <string.h>
//....
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint8_t msg[100] = {0}; 
  /* 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_SPI2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	sprintf((char*)msg, "hello world\r\n");
    HAL_UART_Transmit(&huart1, msg, sizeof(msg), 100);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

6.KEY0(PE4)功能验证

#include <stdio.h>
#include <string.h>
//......
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint8_t msg[100] = {0};
  uint8_t val = 0;	
  /* 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_SPI2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {	
		memset(msg,0,100);
		val = HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4);
		if(0 == val)
		{
			sprintf((char*)msg, "current val is %d, key0 is pressed\r\n", val);
            HAL_UART_Transmit(&huart1, msg, sizeof(msg), 100);
		}
		else
		{
			sprintf((char*)msg, "current val is %d, key0 is not pressed\r\n", val);
            HAL_UART_Transmit(&huart1, msg, sizeof(msg), 100);
		}
		HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

这里需要注意KEY0的值默认为1,按压后的值为0。

 上图即为上电后,按压前后的打印信息。

7.flash读写测试

读flash很简单,仅需要执行spi对应的指令即可;
写flash较为复杂,首先要知道,flash不支持覆盖写,仅支持擦除后再写,原因是flash的写操作只能将1变为0,不能将0变为1,若想将0变为1则只能通过擦除操作,所以写flash时,如果对应的位置值不是全1,则需要先执行擦除操作,再执行写操作。此外,W25Q128 将 16M 的容量分为 256 个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字。W25Q128的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。所以在程序中定义一个至少4K的数组变量,用于保存待擦除扇区的内容。

#include <stdio.h>
#include <string.h>
//....
void spi_read_flash(uint8_t* pbuffer, uint32_t read_addr, uint16_t read_len)
{
	uint8_t send_cmd;
	uint8_t recv_cmd;
	uint16_t i;

	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
	send_cmd = 0x03;
    HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd = (uint8_t)(read_addr>>16);
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd = (uint8_t)(read_addr>>8);
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd = (uint8_t)read_addr;
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd = 0xff;
	for(i=0;i<read_len;i++)
	{
		HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
		pbuffer[i] = recv_cmd;
	}
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);

}

uint8_t flash_buffer[4096];

void flash_write_page(uint8_t* pbuffer, uint32_t write_addr, uint16_t write_len)
{
	uint16_t i=0;
	uint8_t send_cmd = 0;
	uint8_t recv_cmd = 0;

	// write enable
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
	send_cmd=0x06;
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
	
	// write page
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
	send_cmd=0x02;
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd=(uint8_t)(write_addr>>16);
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd=(uint8_t)(write_addr>>8);
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd=(uint8_t)write_addr;
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	for(i=0;i<write_len;i++)
	{
		send_cmd=pbuffer[i];
		HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	}
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
	
	while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY);
}

void flash_erase(uint32_t fan_addr)
{
	uint8_t send_cmd = 0;
	uint8_t recv_cmd = 0;
	fan_addr *=4096;
	send_cmd=0x06;
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
	while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY);
	
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
	send_cmd=0x20;
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd=(uint8_t)(fan_addr>>16);
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd=(uint8_t)(fan_addr>>8);
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	send_cmd=(uint8_t)fan_addr;
	HAL_SPI_TransmitReceive(&hspi2, &send_cmd, &recv_cmd, 1, 0);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
	
	while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY);
}

void flash_write(uint8_t* pbuffer, uint32_t write_addr, uint16_t write_len)
{

	uint16_t page_remain;
	page_remain = 256 - write_addr%256;
	
	if(write_len<=page_remain) page_remain=write_len;
	while(1)
	{
		flash_write_page(pbuffer, write_addr, write_len);
		if(write_len == page_remain)
		{
			break;
		}
		else
		{
			pbuffer+=page_remain;
			write_addr+=page_remain;
			write_len-=page_remain;
			if(write_len>256)
			{
				page_remain=256;
			}
			else
			{
				page_remain = write_len;
			}
		}
	}
}

void spi_write_flash(uint8_t* pbuffer, uint32_t write_addr, uint16_t write_len)
{
	uint32_t sector_index;
	uint16_t sector_offset;
	uint16_t sector_remain;
	uint16_t i = 0;
	sector_index = write_addr/4096;
	sector_offset = write_addr%4096;
	sector_remain = 4096 - sector_offset;
	
	if(write_len <= sector_remain) sector_remain = write_len;
	while(1)
	{
		spi_read_flash(flash_buffer, sector_index*4096, 4096);
		for(i=0;i<sector_remain;i++)
		{
			if(flash_buffer[sector_offset+i]!=0xff)break;
		}
		if(i<sector_remain)
		{
			flash_erase(sector_index);
			for(i=0;i<sector_remain;i++)
			{
				flash_buffer[sector_offset + i] = pbuffer[i];
			}
			flash_write(flash_buffer, sector_index*4096, 4096);
		}
		else
		{
			flash_write(pbuffer, write_addr, sector_remain);
		}

		if(write_len == sector_remain)
		{
			break;
		}
		else
		{
			sector_index++;
			sector_offset=0;
			
			pbuffer+=sector_remain;
			write_addr+=sector_remain;
			write_len-=sector_remain;
			if(write_len > 4096)
			{
				sector_remain=4096;
			}
			else
			{
				sector_remain=write_len;
			}
		}
	}
}

int main(void)
{
  /* USER CODE BEGIN 1 */
	uint8_t msg[100] = {0};
    uint8_t val = 0;
	uint32_t flash_size = 128*1024*1024;	//FLASH ´óСΪ16M×Ö½Ú
  /* 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_SPI2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_Delay(3000);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {	
		memset(msg,0,100);
		val = HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4);
		if(0 == val)
		{
			sprintf((char*)msg, "33333333333333333333\r\n");
			HAL_UART_Transmit(&huart1, msg, sizeof(msg), 100);
			spi_write_flash(msg, flash_size-100, sizeof(msg));
		}
		else
		{
			sprintf((char*)msg, "key0 not pressed:");
			HAL_UART_Transmit(&huart1, msg, sizeof(msg), 100);
			spi_read_flash(msg,flash_size-100,30);
			HAL_UART_Transmit(&huart1, msg, sizeof(msg), 100);
		}
		HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

上电设备后,增加了HAL_Delay(3000),要不然好像KEY0状态会有跳变。

之前已在flash中写入了222222,按下KEY0后,写入33333,松开KEY0,再次读取flash中的值,可以看到发生了变化。

8.总结

使用STM32CubeMX配置USART、GPIO和SPI十分傻瓜,不过也需要对配置参数有一定的认知。配置好后生成的代码基本不再需要更改,可直接使用FreeRTOS的库函数进行操作。不过这样可能并不适合初学者学习理解每个接口的功能,且库函数有时并不能满足特定的使用场景,仍然需要修改删减代码。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值