ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《正点原子BEE BLOCK模块资料》ATK-MB015模块使用说明

《正点原子BEE BLOCK模块资料》ATK-MB015模块使用说明

原创 嵌入式/内核开发 作者:正点原子 时间:2025-03-26 09:21:00 0 删除 编辑

ATK-MB015模块使用说明


        1,硬件连接

       这里以正点原子M48Z-M3最小系统板STM32F103版为例,给大家介绍一下模块和板卡的连接方法。其它板卡与模块的硬件连接方法,请大家在“ATK-MB015 SPI FLASH模块\3,程序源码\相应板卡例程文件夹\readme.txt”路径下查看。

       SPI FLASH模块可通过杜邦线与正点原子M48Z-M3最小系统板STM32F103版进行连接,具体的连接关系,如下表所示:


表1.1.1 SPI FLASH模块与M48Z-M3最小系统板STM32F103版连接关系


        2,实验功能


       2.1 SPI FLASH模块测试实验


       2.1.1 功能说明

       在本实验中,串口会打印SPI FLASH模块的提示信息。需要查看这部分实验信息的用户,可用杜邦线将最小系统板STM32F103的PA9引脚和GND连接至外部的USB转串口设备,这样就可以通过XCOM上位机查看串口打印的信息了。

       用户通过按键控制SPI FLASH的读写,按下KEY0控制SPI FLASH的写入,按下WKUP来控制SPI FLASH的读取。

       开发板的LED0闪烁,提示程序运行。


       2.1.2 源码解读

       打开本实验的工程文件夹,能够在./Drivers/BSP目录下看到ATK_NORFLASH文件夹,和SPI文件夹,其中ATK_NORFLASH文件夹中就包含了SPI FLASH模块的驱动文件,SPI文件夹中就包含了spi的驱动文件如下图所示:


图2.1.2.1 SPI FLASH模块驱动代码


图2.1.2.2 SPI驱动代码


       2.1.2.1 SPI FLASH模块驱动

       下面将简要介绍atk_norflash.c中几个重要的API 函数。


       1. 函数atk_norflash_init ()

       首先是 NOR FLASH 初始化函数,具体的代码,如下所示:

/**
 * @brief       初始化SPI NOR FLASH
 * @param       无
 * @retval      无
 */
void atk_norflash_init(void)
{
    uint8_t temp;
 
    ATK_NORFLASH_CS_GPIO_CLK_ENABLE();      /* NORFLASH CS脚 时钟使能 */
 
    GPIO_InitTypeDef gpio_init_struct;
    gpio_init_struct.Pin = ATK_NORFLASH_CS_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(ATK_NORFLASH_CS_GPIO_PORT, &gpio_init_struct); 
 
    ATK_NORFLASH_CS(1);                 /* 取消片选 */
 
    spi2_init();                         /* 初始化SPI2 */
    spi2_set_speed(SPI_SPEED_2);         /* SPI2 切换到高速状态 18Mhz */
    
    g_norflash_type = atk_norflash_read_id();   /* 读取FLASH ID. */
    printf("ID:%x\r\n", g_norflash_type);
    if (g_norflash_type == W25Q256)  /* W25Q256, 必须使能4字节地址模式 */
    {
        temp = atk_norflash_read_sr(3); /* 读取状态寄存器3,判断地址模式 */
 
        if ((temp & 0X01) == 0)          /* 4字节地址模式 */
        {
            atk_norflash_write_enable(); /* 写使能 */
            temp |= 1 << 1;              /* ADP=1, 上电4位地址模式 */
            atk_norflash_write_sr(3, temp); /* 写SR3 */
            
            ATK_NORFLASH_CS(0);
            spi2_read_write_byte(FLASH_Enable4ByteAddr); /* 使能4字节地址指令 */
            ATK_NORFLASH_CS(1);
        }
    }
}

       在初始化函数中,将 SPI 通信协议用到的 CS 引脚配置好,同时根据 FLASH 的通信要求,通过调用 spi2_set_speed 函数把 SPI2 切换到高速状态。然后尝试读取 flash 的 ID, 由于 W25Q256的容量比较大,通信的时候需要 4 个字节,为了函数的兼容性,我们这里做了判断处理。当然,我们使用的 NM25Q128 是 3 字节地址模式的。 如果能读到 ID 则说明我们的 SPI 时序能正常操作 FLASH,便可以通过 SPI 接口读写 NOR FLASH 的数据了。


       2. 函数atk_norflash_read ()和atk_norflash_write()

       下面介绍一下 FLASH 读写函数,具体代码,如下所示:

/**
 * @brief       读取SPI FLASH
 *   @note      在指定地址开始读取指定长度的数据
 * @param       pbuf    : 数据存储区
 * @param       addr    : 开始读取的地址(最大32bit)
 * @param       datalen : 要读取的字节数(最大65535)
 * @retval      无
 */
void atk_norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
    uint16_t i;
 
    ATK_NORFLASH_CS(0);
    spi2_read_write_byte(FLASH_ReadData);        /* 发送读取命令 */
    atk_norflash_send_address(addr);           /* 发送地址 */
    
    for(i=0;i<datalen;i++)
    {
        pbuf[i] = spi2_read_write_byte(0XFF);    /* 循环读取 */
    }
    
    ATK_NORFLASH_CS(1);
}

       该函数用于从 NOR FLASH 的指定位置读出指定长度的数据,由于 NOR FLASH 支持以任意地址(但是不能超过 NOR FLASH 的地址范围)开始读取数据,所以,这个代码相对来说比较简单。首先拉低片选信号,发送读取命令,接着发送 24 位地址之后,程序就可以开始循环读数据,其地址就会自动增加,读取完数据后,需要拉高片选信号,结束通信。 

       有读函数,那肯定就有写函数,接下来我们介绍一下 NOR FLASH 写函数,其定义如下:

/**
 * @brief       写SPI FLASH
 *   @note      在指定地址开始写入指定长度的数据 , 该函数带擦除操作!
 *              SPI FLASH 一般是: 256个字节为一个Page, 4Kbytes为一个Sector, 16个扇区为1个Block
 *              擦除的最小单位为Sector.
 *
 * @param       pbuf    : 数据存储区
 * @param       addr    : 开始写入的地址(最大32bit)
 * @param       datalen : 要写入的字节数(最大65535)
 * @retval      无
 */
uint8_t g_norflash_buf[4096];   /* 扇区缓存 */
 
void atk_norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
    uint32_t secpos;
    uint16_t secoff;
    uint16_t secremain;
    uint16_t i;
    uint8_t *norflash_buf;
 
    norflash_buf = g_norflash_buf;
    secpos = addr / 4096;        /* 扇区地址 */
    secoff = addr % 4096;        /* 在扇区内的偏移 */
    secremain = 4096 - secoff;  /* 扇区剩余空间大小 */
 
    //printf("ad:%X,nb:%X\r\n", addr, datalen); /* 测试用 */
    if (datalen <= secremain)
    {
        secremain = datalen;    /* 不大于4096个字节 */
    }
 
    while (1)
{
/* 读出整个扇区的内容 */
        atk_norflash_read(norflash_buf, secpos * 4096, 4096);   
 
        for (i = 0; i < secremain; i++)   /* 校验数据 */
        {
            if (norflash_buf[secoff + i] != 0XFF)
            {
                break;       /* 需要擦除, 直接退出for循环 */
            }
        }
 
        if (i < secremain) /* 需要擦除 */
        {
            atk_norflash_erase_sector(secpos);   /* 擦除这个扇区 */
 
            for (i = 0; i < secremain; i++)    /* 复制 */
            {
                norflash_buf[i + secoff] = pbuf[i];
            }
        /* 写入整个扇区 */
            atk_norflash_write_nocheck(norflash_buf, secpos * 4096, 4096);  
        }
        else        /* 写已经擦除了的,直接写入扇区剩余区间. */
        {
            atk_norflash_write_nocheck(pbuf, addr, secremain);  /* 直接写扇区 */
        }
 
        if (datalen == secremain)
        {
            break;  /* 写入结束了 */
        }
        else        /* 写入未结束 */
        {
            secpos++;                /* 扇区地址增1 */
            secoff = 0;              /* 偏移位置为0 */
 
            pbuf += secremain;       /* 指针偏移 */
            addr += secremain;       /* 写地址偏移 */
            datalen -= secremain;    /* 字节数递减 */
 
            if (datalen > 4096)
            {
                secremain = 4096;    /* 下一个扇区还是写不完 */
            }
            else
            {
                secremain = datalen; /* 下一个扇区可以写完了 */
            }
        }
    }
}

       该函数可以在 NOR FLASH 的任意地址开始写入任意长度(必须不超过 NOR FLASH 的容量)的数据。

       我们这里简单介绍一下思路:先获得首地址(addr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过, 再先看看是否要擦除,如果不要,则直接写入数据即可。 如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。这里我们还定义了一个 g_norflash_buf 的全局变量,用于擦除时缓存扇区内的数据。


       2.1.2.2 SPI驱动

       在图2.1.2.2中,spi.c 和 spi.h 为 SPI 驱动文件,里边主要实现了SPI的驱动代码,引脚初始化,以及SPI的发送和接收函数的实现,关于SPI的驱动介绍,请查看正点原子各个开发板对应的开发指南中SPI章节。


       2.1.2.3 实验测试代码

       实验的测试代码为文件 demo.c,在工程目录下的 User 子目录中。测试代码的入口函数为 demo_run(),具体的代码,如下所示:

/* 要写入到FLASH的字符串数组 */
const uint8_t g_text_buf[] = {"STM32 SPI TEST"};
#define TEXT_SIZE sizeof(g_text_buf)   /* TEXT字符串长度 */
    
/**
 * @brief       例程演示入口函数
 * @param       无
 * @retval      无
 */
void demo_run(void)
{
    uint8_t key;
    uint16_t i = 0;
    uint8_t datatemp[TEXT_SIZE];
    uint32_t flashsize;
    uint16_t id = 0;
    
    key_init();                                /* 初始化按键 */
    atk_norflash_init();                         /* 初始化NORFLASH */
    
    id = atk_norflash_read_id();                 /* 读取FLASH ID */
 
    while ((id == 0) || (id == 0XFFFF))          /* 检测不到FLASH芯片 */
    {
        printf("FLASH Check Failed!\r\n");
        delay_ms(500);
        LED0_TOGGLE(); /* LED0闪烁 */
    }
    printf("SPI FLASH Ready!\r\n");
    flashsize = 16 * 1024 * 1024;                /* FLASH 大小为16M字节 */
    
    while (1)
    {
        key = key_scan(0);
 
        if (key == KEY0_PRES)                    /* KEY0按下,写入 */
        {
            printf("Start Write FLASH....\r\n");
            sprintf((char *)datatemp, "%s%d", (char *)g_text_buf, i);
 
/* 从倒数第100个地址处开始,写入SIZE长度的数据 */            
atk_norflash_write((uint8_t *)datatemp, flashsize - 100, TEXT_SIZE);     
            printf("FLASH Write Finished!\r\n"); /* 提示写入完成 */       
        }
 
        if (key == WKUP_PRES)                    /* WKUP按下,读取字符串并显示 */
        {
            printf("Start Read FLASH... .\r\n");
/* 从倒数第100个地址处开始,读出SIZE个字节 */
            atk_norflash_read(datatemp, flashsize - 100, TEXT_SIZE);                
            printf("The Data Readed Is: %s\r\n", datatemp);  /* 打印读到的字符串*/
        }
        i++;
        if (i == 20)
        {
            LED0_TOGGLE();  /* LED0闪烁 */
            i = 0;
        }
 
        delay_ms(10);
    }
}

       demo_run函数的流程大致是:在demo_run 函数外部定义要写入 SPI  FLASH的字符串数组 g_text_buf。在完成用户级初始化工作后,读取SPI FLASH的ID,然后通过按下 WKUP 去读取倒数第 100 个地址处开始的数据并把数据通过串口打印出来;另外还可以通过 KEY0去倒数第 100 个地址处写入 g_text_buf 数据并在串口打印提示信息,完成后在串口打印“FLASH Write Finished!”。


       2.1.3 实验现象

       将SPI FLASH模块按照第一节“硬件连接”中介绍的连接方式与开发板连接,并将实验代码编译烧录至开发板中,本实验使用串口输出调试信息,因此需将开发板的 PA9连接至 DAP 虚拟串口(或 USB 转 TTL 模块)的 RX引脚。完成连接后,可通过按键控制SPI FLASH读写,并通过串口调试助手查看实验信息输出,如下图所示:


图2.1.3.2 串口调试助手显示内容


       当按下KEY0时,此时会将数组g_text_buf中的数据写入SPI FLASH模块。写入完成后,串口将打印提示信息。随后,按下WKUP,即可读取SPI FLASH倒数第 100 个地址处开始的数据,并通过串口输出读取结果。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3079014/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    765163