基于STM32与NOR FLASH的SPI通信

发布者:daits摸鱼的最新更新时间:2018-07-23 来源: eefocus关键字:STM32  NOR  FLASH  SPI通信 手机看文章 扫描二维码
随时随地手机看文章

  SPI的通信很容易实现,相比之下,驱动FLASH反而耗费了我学习SPI整个过程的大部分时间。下面是我学习过程的一些记录。


硬件平台:秉火ISO_V2开发板

实现功能:STM32使用SPI协议读写板载NOR FLASH


1. 通讯引脚


  SPI通讯需要4个引脚,nSS、SCK、MISO和MOSI, 

这里写图片描述
  以STM32的SPI1为例,其关联GPIO如上图标(摘自《STM32中文参考手册_V10.pdf》-P120)。AFIO_MAPR寄存器的BIT0(SPI1_REMAP)为0时则不重映射SPI1的4个GPIO,nSS、SCK、MISO和MOSI依次为PA4、PA5、PA6、PA7。我们使用寄存器的复位值为0x00,即我们不重映射SPI1关联引脚。开发板原理图的设计也确实如此: 
这里写图片描述 
  另外,一般在实际工程中,nSS引脚不采用硬件SPI专用的nSS引脚,而是用STM32的一个普通GPIO功能来控制。

2. 软件设计

2.1 SPI初始化结构体

typedef struct

{

    uint16_t SPI_Direction;         //SPI的单双向模式

    uint16_t SPI_Mode;              //SPI的主/从机模式

    uint16_t SPI_DataSize;          //SPI的数据帧长度,8/16位可选

    uint16_t SPI_CPOL;              //空闲时钟极性,高低电平

    uint16_t SPI_CPHA;              //时钟相位,即奇偶边沿采样

    uint16_t SPI_NSS;               //片选引脚nSS是交由硬件控制还是软件控制

    uint16_t SPI_BaudRatePrescaler; //时钟分频系数,FSCK = FCLK / 分频系数

    uint16_t SPI_FirstBit;          //MSB/LSB先行

    uint16_t SPI_CRCPolynomial;     //CRC校验表达式

}SPI_InitTypeDef;

2.2 操作函数

(1) 使能SPI的时钟 
这里写图片描述

GPIO、SCK、MISO、MOSI都是PA组,且SPI外设跟GPIO外设一样,隶属于APB2总线:所以:


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE);


(2) 初始化GPIO引脚 

设置SPI1的相关引脚为复用输出,这样才会连接到SPI1上否则这些IO还是默认作为标准输入/输出。


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);


(3) 初始化SPI1,设置其工作模式


SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                       //设置为主SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                   //SPI发送接收8位数据帧结构

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                         //时钟悬空高

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                        //数据捕获于第二个时钟沿

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                           //nSS信号软件控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //波特率预分频值为256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                  //数据传输从MSB位开始

SPI_InitStructure.SPI_CRCPolynomial = 7;                            //CRC值计算的表达式

SPI_Init(SPI1, &SPI_InitStructure);                                 //将上述设置信息初始化外设SPIx寄存器


(4) 使能SPI1


SPI_Cmd(SPI1, ENABLE);

1

(5) SPI接收数据


uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

1

(6) SPI发送数据


void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

1

(7) 查看SPI传输过程状态


FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);

1

  在SPI的传输过程中,若要判断数据是否传输完成,发送缓冲区是否为空等状态,可通过此函数实现。以判断是否发送完成为例:


while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);     //发送缓冲区不为空则一直阻塞

1

3. FLASH相关特性

  ISO_V2开发板的板载FLASH型号为W25Q64,是一种使用SPI通讯协议的NOR FLASH存储器。 
这里写图片描述 
  W25Q64除了上述的SPI关联的4个引脚之外,还有用于控制写保护功能的WP引脚、暂停通讯控制的HOLD引脚。当WP为低电平时禁止写入数据,HOLD为低电平时暂停通讯,硬件上将这两个引脚都接到3.3V,即不使用这两个功能。其他细节,到代码实现的时候补充。

4. 软件实现

  实现功能:往板载W25Q64 FLASH写入数据,再读取出来,调试信息和运行结果通过串口打印到PC机串口工具。

  基于具有串口BSP的工程,新建bsp_spi_flash.h和bsp_spi_flash.c。

  函数声明:


//SPI通信相关函数

void SPI_GPIO_Init(void);

void SPI_FLASH_Init(void);

uint8_t SPI_FLASH_RecvSendByte(uint8_t byte);


//驱动FLASH相关函数

uint32_t SPI_FLASH_ReadID(void);

uint8_t SPI_FLASH_Read_SR(void);

void SPI_FLASH_Wait_Busy(void);

void SPI_FLASH_Read(uint8_t *Buf, uint32_t ReadAddr, uint16_t ReadCnt);

void SPI_FLASH_Write(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt);

void SPI_FLASH_WriteEnable(void);

void SPI_FLASH_Write_Page(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt);

void SPI_FLASH_Write_Sector(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt);

void SPI_FLASH_WaitForWriteEnd(void);

void SPI_FLASH_Erase_Sector(uint32_t Addr);


4.1 设置SPI通讯的关联引脚和SPI的工作模式


//设置SPI通信的相关引脚

void SPI_GPIO_Init()

{

    GPIO_InitTypeDef GPIO_InitStruct;


    //开启GPIOA以及SPI1外设的时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);


    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;    //CLK、MISO、MOSI、

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;                        //复用推挽输出

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  

    GPIO_Init(GPIOA, &GPIO_InitStruct);


    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;          //nSS

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;   //推挽输出

    GPIO_Init(GPIOA, &GPIO_InitStruct); 

    FLASH_SPI_CS_HIGH();

}


//设置SPI外设的工作模式

void SPI_FLASH_Init()

{

    SPI_InitTypeDef SPI_InitStruct;


    SPI_GPIO_Init();    

    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;                      //主机端工作模式

    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工模式

    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;                  //8位数据帧长度

    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;                       //偶数边沿采SPI信号

    SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;                        //空闲时钟为空

    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;                 //高位先行

    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;                          //使用软件控制片选信号

    SPI_InitStruct.SPI_CRCPolynomial = 7;                           //CRC校验

    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;   //256分频 

    SPI_Init(SPI1, &SPI_InitStruct);


    //打开SPI外设

    SPI_Cmd(SPI1, ENABLE);

}


4.2 通过SPI的硬件接口发送/接收数据


uint8_t SPI_FLASH_RecvSendByte(uint8_t byte)

{

    uint8_t TimeCnt = 0;


    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)      //发送缓冲区不为空说明还要数据待发送

    {

        if (TimeCnt++ > 200)

            return 0;

    }


    SPI_I2S_SendData(SPI1, byte);   //通过外设SPIx发送一个数据


    TimeCnt = 0;

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)

    {

        if (TimeCnt++ > 200)

            return 0;

    }

    return SPI_I2S_ReceiveData(SPI1);

}


  该函数含超时机制,可用于发送/接收一字节数据。至于用于发送还是接收,看用户关注哪一个。


  下来是驱动FLASH的相关函数。


4.3 读取FLASH的存储器类型(ID)


uint32_t SPI_FLASH_ReadID(void)

{

    uint32_t RET_ID;


    //选中FLASH的片选

    FLASH_SPI_CS_LOW();


    //发送读取FLASH存储器类型的指令

    SPI_FLASH_RecvSendByte(W25X_JedecDeviceID);


    //接收FLASH发来的存储器类型,它是先发送高位的

    RET_ID = SPI_FLASH_RecvSendByte(NOTUSEDAT) << 16;           

    RET_ID |= SPI_FLASH_RecvSendByte(NOTUSEDAT) << 8;       

    RET_ID |= SPI_FLASH_RecvSendByte(NOTUSEDAT);


    //放开FLASH的片选

    FLASH_SPI_CS_HIGH();


    return RET_ID;

}


  FLASH_SPI_CS_LOW()和FLASH_SPI_CS_HIGH()是分别实现选中和不选中FLASH片选的宏,其实质就是控制nSS引脚为高/低电平:


#define FLASH_SPI_CS_LOW()      GPIO_ResetBits(GPIOA, GPIO_Pin_4)   //选中FLASH

#define FLASH_SPI_CS_HIGH()     GPIO_SetBits(GPIOA, GPIO_Pin_4)     //不选中FLASH

  W25X_JedecDeviceID也是一个宏定义,这是由FLASH定义的用来控制FLASH的指令,从W25Q64手册知,支持的指令有: 
这里写图片描述

  这些命令被宏定义在bsp_spi_flash.h文件中:


#define W25X_WriteEnable        0x06 

#define W25X_WriteDisable       0x04 

#define W25X_ReadStatusReg      0x05 

#define W25X_WriteStatusReg     0x01 

#define W25X_ReadData           0x03 

#define W25X_FastReadData       0x0B 

#define W25X_FastReadDual       0x3B 

#define W25X_PageProgram        0x02 

#define W25X_BlockErase         0xD8 

#define W25X_SectorErase        0x20 

#define W25X_ChipErase          0xC7 

#define W25X_PowerDown          0xB9 

#define W25X_ReleasePowerDown   0xAB 

#define W25X_DeviceID           0xAB 

#define W25X_ManufactDeviceID   0x90 

#define W25X_JedecDeviceID      0x9F 


  发送完W25X_JedecDeviceID指令后,调用SPI_FLASH_RecvSendByte()函数用于接收FLASH发来的3字节分3次发送的DeviceID,注意,该FLASH的发送顺序是MSB先行,所以接收到的第一、二、三字节需要依次左移16、8、0位。 

  当SPI_FLASH_RecvSendByte()用于发送数据(指令)时候我们并不关注返回的内容,所以不需要接收其返回值; 

  当SPI_FLASH_RecvSendByte()用于接收数据时候我们并不关注发送的内容,所以NOTUSEDAT宏是我们任意定义的:


#define NOTUSEDAT   0xFF

1

4.4 读取FLASH的当前运行状态


uint8_t SPI_FLASH_Read_SR(void)

{

    uint8_t ret = 0;


    FLASH_SPI_CS_LOW();


    SPI_FLASH_RecvSendByte(W25X_ReadStatusReg);


    ret = SPI_FLASH_RecvSendByte(NOTUSEDAT);


    FLASH_SPI_CS_HIGH();


    return ret;

}


  获取FLASH的运行状态则是向FLASH发送获取状态的指令W25X_ReadStatusReg。跟上一个函数类似,不赘述。利用此函数的返回值,可以判断FLASH是否处于忙状态:


void SPI_FLASH_Wait_Busy(void)

{

    while ((SPI_FLASH_Read_SR() & 0x01) == 0x01);

}


4.5 读取FLASH的数据


void SPI_FLASH_Read(uint8_t *Buf, uint32_t ReadAddr, uint16_t ReadCnt)

{

    uint16_t i;


    FLASH_SPI_CS_LOW();


    SPI_FLASH_RecvSendByte(W25X_ReadData);


    SPI_FLASH_RecvSendByte(ReadAddr >> 16);

    SPI_FLASH_RecvSendByte(ReadAddr >> 8);

    SPI_FLASH_RecvSendByte(ReadAddr);


    for (i = 0; i < ReadCnt; i++)

    {

        Buf[i] = SPI_FLASH_RecvSendByte(NOTUSEDAT);     

    }


    FLASH_SPI_CS_HIGH();

}


  选中FLASH芯片后向其发送W25X_ReadData表示主机要读取FLASH的数据,接着发送要读取的目标地址,还是遵循MSB先行的发送规则,发送完毕就可以接收数据了。 

  为什么主机发送完指令后,发送的数据会被FLASH解析成目标地址?这是由FLASH定义的。主机要读取ReadCnt个数据,为什么主机不用事先告诉FLASH?这也是FLASH定义的,FLASH就是这么工作的,一旦接收到W25X_ReadData,它就会知道它接下来要收取到一个目标地址,接着只管把从目标地址后的数据发回主机,直至FLASH不被选中。


4.6 往FLASH写数据


  往FLASH写数据,有3种写范围,写一整个扇区、写一整页、写一个字节,当我们要从某个扇区的开始写入一整个扇区的数据(4096字节),程序需要将对这个扇区分为一页一页来写(256字节),对这一页的写又会转换成一字节一字节的写。 

  SPI_FLASH_Write(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt)是用户调来写数据的函数,


//写扇区 -> 写页 -> 按字节写

uint32_t SectorNum;

uint16_t SectorOffset;

uint16_t SectorRemainder;

uint16_t i = 0;

uint8_t FLASH_Buf[4096] = {0};          //整个扇区的副本


void SPI_FLASH_Write(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt)

{   

    SectorNum = WriteAddr / 4096;           //得到目标地址位于第几个扇区

    SectorOffset = WriteAddr % 4096;        //得到目标地址在扇区内的偏移量

    SectorRemainder = 4096 - SectorOffset;  //得到所在扇区还剩下多少空间


    if (WriteCnt <= SectorRemainder)        //剩下的空间足够写用户要写的数据

    {

        SectorRemainder = WriteCnt;

    }


    while (1)

    {

        SPI_FLASH_Read(FLASH_Buf, SectorNum * 4096, 4096);  //读出整个扇区的内容


        //FLASH被擦除后的状态是都为1

        for (i = 0; i < SectorRemainder; i++)

        {

            if (FLASH_Buf[i + SectorOffset] != 0xff)

                break;

        }


        //i < SectorRemainder说明存在没擦除的区域,那就擦除一整块

        if (i < SectorRemainder)

        {

            SPI_FLASH_Erase_Sector(SectorNum);


            for (i = 0; i < SectorRemainder; i++)

            {

                FLASH_Buf[i + SectorOffset] = Buf[i];

            }


            //写一整个扇区(其实现会自动转换成按照多页来写)

            SPI_FLASH_Write_Sector(FLASH_Buf, SectorNum * 4096, 4096);

        }

        else

            //写一整个扇区(其实现会自动转换成按照多页来写)

            SPI_FLASH_Write_Sector(FLASH_Buf, WriteAddr, SectorRemainder);


        if (WriteCnt == SectorRemainder)

            break;

        else

        {

            SectorNum++;

            SectorOffset = 0;

            Buf += SectorRemainder;

            WriteAddr += SectorRemainder;

            WriteCnt -= SectorRemainder;


            if (WriteCnt > 4096)

            {

                SectorRemainder = 4096;

            }

            else

                SectorRemainder = WriteCnt;         

        }

    }

}


  这个函数是将用户要写的数据进行以扇区为单位写入,一个扇区为4096字节大小,以扇区为单位写,函数为:


void SPI_FLASH_Write_Sector(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt)

{

    //uint32_t PageNum;

    uint16_t PageOffset = WriteAddr % 256;      //目标地址在单页的偏移

    uint16_t PageRemainder = 256 - PageOffset;  //单页剩余的空间


    if (WriteCnt <= PageRemainder)

        PageRemainder = WriteCnt;


    while (1)

    {

        //按照页的区域来写,其内部实现会再以字节为单位去写

        SPI_FLASH_Write_Page(Buf, WriteAddr, PageRemainder);


        if (PageRemainder == WriteCnt)

            break;

        else

        {

            WriteCnt -= PageRemainder;


            if (WriteCnt > 256)

                PageRemainder = 256;

            else

                PageRemainder = WriteCnt;

        }   

    }

}


  该函数调用到以页为单位写的函数SPI_FLASH_Write_Page(),一页的空间大小为256字节。


//按页写

void SPI_FLASH_Write_Page(uint8_t* Buf, uint32_t WriteAddr, uint16_t WriteCnt)

{

    int i;


    SPI_FLASH_WriteEnable();

    FLASH_SPI_CS_LOW();

    SPI_FLASH_RecvSendByte(W25X_PageProgram);


    SPI_FLASH_RecvSendByte(WriteAddr >> 16);

    SPI_FLASH_RecvSendByte(WriteAddr >> 8);

    SPI_FLASH_RecvSendByte(WriteAddr);


    for (i = 0; i < WriteCnt; i++)

    {

        SPI_FLASH_RecvSendByte(Buf[i]);

    }

    FLASH_SPI_CS_HIGH();

    SPI_FLASH_Wait_Busy();

}


  以页为单位的写,会调用以字节为单位的写,即前面的SPI_FLASH_RecvSendByte()函数。


4.7 使能FLASH写


  上面往FLASH写数据,在调用SPI_FLASH_RecvSendByte()写之前需要调用SPI_FLASH_WriteEnable()使能FLASH的写使能,这样才可以写入数据:


void SPI_FLASH_WriteEnable(void)

{

    FLASH_SPI_CS_LOW();


    SPI_FLASH_RecvSendByte(W25X_WriteEnable);   


    FLASH_SPI_CS_HIGH();

}


4.8 等待写操作完成


  写FLASH时一个操作过程,FLASH提供操作命令W25X_ReadStatusReg供给用户判断是否写完成。在等待期写完成后用户才去进行对FLASH的其他操作。


//等待写操作执行完毕

void SPI_FLASH_WaitForWriteEnd(void)

{

    uint8_t ret;


    FLASH_SPI_CS_LOW();


    SPI_FLASH_RecvSendByte(W25X_ReadStatusReg);


    do

    {

        ret = SPI_FLASH_RecvSendByte(NOTUSEDAT);

    }while (ret == RESET);


    FLASH_SPI_CS_HIGH();

}


4.9 擦除FLASH


  读/写FLASH的函数实现完毕,看擦除FLASH的实现。注意,擦除FLASH只能按扇区擦除,该函数的参数为指定哪一块扇区。W25Q64容量为8M,分为128块,每块有16个扇区,每个扇区4096字节。所以参数Addr不能大于2048。


//擦除整个扇区

void SPI_FLASH_Erase_Sector(uint32_t Addr)

{

    Addr *= 4096;

    SPI_FLASH_WriteEnable();

    SPI_FLASH_Wait_Busy();


    FLASH_SPI_CS_LOW();

    SPI_FLASH_RecvSendByte(W25X_SectorErase);

    SPI_FLASH_RecvSendByte(Addr >> 16);

    SPI_FLASH_RecvSendByte(Addr >> 8);

    SPI_FLASH_RecvSendByte(Addr);

    FLASH_SPI_CS_HIGH();


    SPI_FLASH_Wait_Busy();

}


4.10 main()函数


uint8_t TxBuf[] = "HelloWorld";


#define BUFSZ (sizeof(TxBuf) / sizeof(uint8_t))


uint8_t RxBuf[BUFSZ];


int main(void)

{

    LED_GPIO_Config();


    USART1_Config();

    SPI_FLASH_Init();


    printf("SPI测试实践: \r\n\r\n");


    if (SPI_FLASH_ReadID() == FLASHID)

    {

        printf("找到FLASH设备,型号为W25X64\r\n\r\n");

        LED_Flicker();


        //printf("擦出扇区中...\r\n\r\n");

        //SPI_FLASH_Erase_Sector(0x00);

        //printf("擦除完毕!\r\n\r\n");


        printf("写入的数据为: %s \r\n\r\n", TxBuf);


        SPI_FLASH_Write(TxBuf, 0x00000, BUFSZ);


        SPI_FLASH_Read(RxBuf, 0x00000, BUFSZ);

        printf("读出的数据为: %s \r\n\r\n", TxBuf);

    }

    else

    {

        printf("找不到FLASH设备\r\n\r\n");

    }


    while (1);

    return 0;

}

  编译运行: 
这里写图片描述

关键字:STM32  NOR  FLASH  SPI通信 引用地址:基于STM32与NOR FLASH的SPI通信

上一篇:STM32 用jlink下载失败 环境配置
下一篇:JLink烧写Nor Flash出错的解决方案

推荐阅读最新更新时间:2024-03-16 16:09

巧用外设复位修改只读寄存器
有STM32开发者用到STM32F429芯片开发产品,并用到其中的CAN外设。在CAN应用过程中有个专门针对收发出错情况进行次数统计的两个计数器,其值通过错误状态寄存器CAN_ESR中的REC 和TEC 两个字段来体现,CAN硬件会根据错误数据大小做适当响应或处理。 根据寄存器描述得知,TEC 和REC 的值在这个寄存器里面是只读的。而此时的STM32用户有个强烈的需求,就是期望能适时地对这两个出错记录字段做清零。他自己也尝试编写一些代码想让二者清零,均以失败告终,便邮件咨询有无解决办法。 我们在阅读CAN_ESR寄存器内容时倒有个发现,即该寄存器的复位值是0x00000000。 也就是说,芯片每次复位后其值一定是0,
[单片机]
巧用外设复位修改只读寄存器
STM32 串口烧写 FLASH 外部字库 UCGUI显示 自我学习总结
最近学习TFT显示问题,在多种汉字显示方面有点难,主要是字库太大,几个字库就不得了。开始是使用SD卡向外部FLASH---W25X16写,完全能够完成。后来觉得这样比较麻烦,有时候还没有SD接口,于是打算用串口写一下试一试,网上有很多人说会丢失数据,在后面的试验中暂时没有发现。 我是在我前段时间学习的UCGUI的基础基础上修改的。 主要功能是----启动开发板,首先写入地址指令----必须十六进制----比如---2A 23 00 05 00 03 23 2A-----其中2A,23为验证码,前后都有,第3,4位是地址码,前面就表示将要写入的起始地址是0x05*4096,第5,6位是为了写入数据将要从起始地址连续擦除的扇区
[单片机]
<font color='red'>STM32</font> 串口烧写 <font color='red'>FLASH</font> 外部字库 UCGUI显示 自我学习总结
STM32 关于定时器相关问题的探讨
STM32F4 PWM模块探讨 1.STM32定时器认识 1.1 基本定时器 基本定时器 TIM6 和 TIM7 包含一个 16 位自动重载计数器,该计数器由可编程预分频器驱动。此类定时器不仅可用作通用定时器以生成时基, 还可以专门用于驱动数模转换器 。(DAC)。 通用定时器特性 16 位自动重载递增计数器 16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数 介于 1 和 65536 之间 用于触发 DAC 的同步电路 发生如下更新事件时会生成中断/DMA 请求:计数器上溢 1.2 通用定时器 1.2.1 TIM~TIM5 通用定时器包含一个 16 位或 32 位自动重载计数器,该计数器由可
[单片机]
<font color='red'>STM32</font> 关于定时器相关问题的探讨
管理STM32 MCU中的内存保护单元
1前言 本应用笔记介绍如何管理 STM32 产品中的内存保护单元(MPU)。MPU 是用于存储器保护的可选组件。STM32 微控制器(MCU)中嵌入 MPU 之后变得更稳健可靠。在使用 MPU 之前,必须对其进行编程并加以启用。如果 MPU 没有启用,则存储系统的行为不会变化。 2概述 MPU 可以使嵌入式系统更加稳健和安全:• 禁止用户应用程序破坏关键任务(例如操作系统核心)使用的数据• 将 SRAM 存储区域定义为非可执行(禁止执行 XN),以防止代码注入攻击• 修改存储访问属性MPU可最多保护16个内存区域。在 Armv6、Armv7 架构(Cortex-M0+、M3、M4、M7)下,这些区域可以依次拥有 8 个子区域(
[单片机]
管理<font color='red'>STM32</font> MCU中的内存保护单元
STM32学习记录12 中断向量表
从stm32f10x.s可以看到,已经定义好了一大堆的中断响应函数,这就是中断向量表,标号__Vectors,表示中断向量表入口地址,例如: AREA RESET, DATA, READONLY ; 定义只读数据段,实际上是在CODE区(假设STM32从FLASH启动,则此中断向量表起始地址即为0x8000000) EXPORT __Vectors IMPORT OS_CPU_SysTickHandler IMPORT OS_CPU_PendSVHandler __Vectors DCD __initial_sp ; Top of Stack DCD Reset_
[单片机]
<font color='red'>STM32</font>学习记录12 中断向量表
stm32 ADC的规则通道和注入通道混合使用
之前完成了规则通道DMA的数据传输了,不过平时在使用ADC的时候可能就会遇到很多情况,不可能就这样简单的按规则通道来采样,DMA存储,使用数据的;可能有时候会需要立刻采样,那样我们就需要利用到注入通道了。文档关于注入通道的解释: 1 利用外部触发或通过设置ADC_CR2寄存器的ADON位,启动一组规则通道的转换。 2 如果在规则通道转换期间产生一外部注入触发,当前转换被复位,注入通道序列被以单次扫描方式进行转换。 3 然后,恢复上次被中断的规则组通道转换。如果在注入转换期间产生一规则事件,注入转换不会被中断,但是规则序列将在注入序列结束后被执行。   将变阻器的那路ADC设置为注入通道: 1 ADC_In
[单片机]
<font color='red'>stm32</font> ADC的规则通道和注入通道混合使用
STM32+DHT11读取温湿度数据显示
一、环境介绍 MCU: STM32F103C8T6 温湿度模块: DHT11 开发软件: Keil5 源码下载地址: https://download.csdn.net/download/xiaolong1126626497/18263569 二、DHT11介绍 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。 它应用专用的数字模块采集技术和温湿度传感技术, 确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。 每个DHT11传感器都在极为精确
[单片机]
STM32+DHT11读取温湿度数据显示
恒忆跃居成为世界最大NOR闪存供应商
尽管IC产业在步入2009年的时候还是前景迷茫,但在岁末之际,曾深受经济危机影响的存储产业终于看到了复苏的曙光。面对市场可预期的需求增长,恒忆 (Numonyx) 通过两方面的举措响应市场需求,扩充产能。一是通过从高节点技术向低节点技术的制程升级,进一步扩大位级产能;二是充分发挥“灵活资产(Asset Smart)”策略的优势,与合资晶圆厂、代工伙伴一起增加产能。    展望2010年的市场前景,主管恒忆亚洲区嵌入式业务及渠道的销售副总裁龚翊表示:“作为全球存储器解决方案的领跑者,恒忆不仅与亚洲数字消费电子和通信领域的领先厂商结成了战略合作伙伴关系,而且,特别是在中国,我们也看到众多本土厂商对于包括高容量存储解决方案在内的
[手机便携]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved