stm32f4外设学习篇(代码集合)(三)

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: stm32f4外设学习篇(代码集合)

stm32f4外设学习篇(代码集合)(二)https://developer.aliyun.com/article/1472516


15、24C02

.h文件

#ifndef __24C02_H
#define __24C02_H
#include "stm32f4xx.h"
/* 
 * AT24C02 2kb = 2048bit = 2048/8 B = 256 B
 * 32 pages of 8 bytes each
 *
 * Device Address
 * 1 0 1 0 A2 A1 A0 R/W
 * 1 0 1 0 0  0  0  0 = 0XA0
 * 1 0 1 0 0  0  0  1 = 0XA1 
 */
/* AT24C01/02每页有8个字节 
 * AT24C04/08A/16A每页有16个字节 
 */
#define EEPROM_DEV_ADDR 0xA0 /* 24xx02的设备地址 */
#define EEPROM_PAGE_SIZE 8   /* 24xx02的页面大小 */
#define EEPROM_SIZE 256      /* 24xx02总容量 */
uint8_t ee_Init(void);
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize);
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize);
void ee_Erase(void);
uint8_t ee_Test(void);
#endif /* __24C02_H */

.c文件

#include "_24C02.h"
#include "i2c.h"
/*
*********************************************************************************************************
* 函 数 名: ee_Init()
* 功能说明: 判断串行EERPOM是否正常
* 形    参:无
* 返 回 值: 1 表示正常, 0 表示不正常
*********************************************************************************************************
*/
uint8_t ee_Init(void)
{
  if (i2c_CheckDevice(EEPROM_DEV_ADDR) == 0)
  {
    return 1;
  }
  else
  {
    /* 失败后,切记发送I2C总线停止信号 */
    i2c_Stop();
    return 0;
  }
}
/*
*********************************************************************************************************
* 函 数 名: ee_ReadBytes
* 功能说明: 从串行EEPROM指定地址处开始读取若干数据
* 形    参:_usAddress : 起始地址
*      _usSize : 数据长度,单位为字节
*      _pReadBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
  uint16_t i;
  /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
  /* 第1步:发起I2C总线启动信号 */
  i2c_Start();
  /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); /* 此处是写指令 */
  /* 第3步:等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail; /* EEPROM器件无应答 */
  }
  /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
  i2c_SendByte((uint8_t)_usAddress);
  /* 第5步:等待ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail; /* EEPROM器件无应答 */
  }
  /* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
  i2c_Start();
  /* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
  i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD); /* 此处是读指令 */
  /* 第8步:发送ACK */
  if (i2c_WaitAck() != 0)
  {
    goto cmd_fail; /* EEPROM器件无应答 */
  }
  /* 第9步:循环读取数据 */
  for (i = 0; i < _usSize; i++)
  {
    _pReadBuf[i] = i2c_ReadByte(); /* 读1个字节 */
    /* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
    if (i != _usSize - 1)
    {
      i2c_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
    }
    else
    {
      i2c_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
    }
  }
  /* 发送I2C总线停止信号 */
  i2c_Stop();
  return 1; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  /* 发送I2C总线停止信号 */
  i2c_Stop();
  return 0;
}
/*
*********************************************************************************************************
* 函 数 名: ee_WriteBytes
* 功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
* 形    参:_usAddress : 起始地址
*      _usSize : 数据长度,单位为字节
*      _pWriteBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
{
  uint16_t i, m;
  uint16_t usAddr;
  /* 
    写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
    对于24xx02,page size = 8
    简单的处理方法为:按字节写操作模式,没写1个字节,都发送地址
    为了提高连续写的效率: 本函数采用page wirte操作。
  */
  usAddr = _usAddress;
  for (i = 0; i < _usSize; i++)
  {
    /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
    if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0)
    {
      /* 第0步:发停止信号,启动内部写操作 */
      i2c_Stop();
      /* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms       
        CLK频率为200KHz时,查询次数为30次左右
      */
      for (m = 0; m < 1000; m++)
      {
        /* 第1步:发起I2C总线启动信号 */
        i2c_Start();
        /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
        i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR); /* 此处是写指令 */
        /* 第3步:发送一个时钟,判断器件是否正确应答 */
        if (i2c_WaitAck() == 0)
        {
          break;
        }
      }
      if (m == 1000)
      {
        goto cmd_fail; /* EEPROM器件写超时 */
      }
      /* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
      i2c_SendByte((uint8_t)usAddr);
      /* 第5步:等待ACK */
      if (i2c_WaitAck() != 0)
      {
        goto cmd_fail; /* EEPROM器件无应答 */
      }
    }
    /* 第6步:开始写入数据 */
    i2c_SendByte(_pWriteBuf[i]);
    /* 第7步:发送ACK */
    if (i2c_WaitAck() != 0)
    {
      goto cmd_fail; /* EEPROM器件无应答 */
    }
    usAddr++; /* 地址增1 */
  }
  /* 命令执行成功,发送I2C总线停止信号 */
  i2c_Stop();
  return 1;
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
  /* 发送I2C总线停止信号 */
  i2c_Stop();
  return 0;
}
void ee_Erase(void)
{
  uint16_t i;
  uint8_t buf[EEPROM_SIZE];
  /* 填充缓冲区 */
  for (i = 0; i < EEPROM_SIZE; i++)
  {
    buf[i] = 0xFF;
  }
  /* 写EEPROM, 起始地址 = 0,数据长度为 256 */
  if (ee_WriteBytes(buf, 0, EEPROM_SIZE) == 0)
  {
    //    printf("擦除eeprom出错!\r\n");
    return;
  }
  else
  {
    //    printf("擦除eeprom成功!\r\n");
  }
}

16、W25QXX

.h文件

#ifndef __W25QXX_H
#define __W25QXX_H
#include "stm32f4xx.h"
//W25X系列/Q系列芯片列表
//W25Q80  ID  0XEF13
//W25Q16  ID  0XEF14
//W25Q32  ID  0XEF15
//W25Q64  ID  0XEF16
//W25Q128 ID  0XEF17
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
#define NM25Q80 0X5213
#define NM25Q16 0X5214
#define NM25Q32 0X5215
#define NM25Q64 0X5216
#define NM25Q128 0X5217
#define NM25Q256 0X5218
extern uint16_t W25QXX_TYPE; //定义W25QXX芯片型号
#define W25QXX_CS *((volatile unsigned long *)((((GPIOB_BASE + 20) & 0xF0000000) + 0x2000000 + (((GPIOB_BASE + 20) & 0xFFFFF) << 5) + (14 << 2)))) //W25QXX的片选信号
//
//指令表
#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
void W25QXX_Init(void);
uint16_t W25QXX_ReadID(void);     //读取FLASH ID
uint8_t W25QXX_ReadSR(void);      //读取状态寄存器
void W25QXX_Write_SR(uint8_t sr); //写状态寄存器
void W25QXX_Write_Enable(void);   //写使能
void W25QXX_Write_Disable(void);  //写保护
void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);    //读取flash
void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); //写入flash
void W25QXX_Erase_Chip(void);                                                     //整片擦除
void W25QXX_Erase_Sector(uint32_t Dst_Addr);                                      //扇区擦除
void W25QXX_Wait_Busy(void);                                                      //等待空闲
void W25QXX_PowerDown(void);                                                      //进入掉电模式
void W25QXX_WAKEUP(void);                                                         //唤醒
// 不精确的延时
static void _w25qxx_delay(__IO uint32_t nCount)
{
  for (; nCount != 0; nCount--)
    ;
}
#endif

.c文件

#include "w25qxx.h"
#include "spi.h"
u16 W25QXX_TYPE = W25Q128; //默认是W25Q128
//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q128
//容量为16M字节,共有128个Block,4096个Sector
//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOB时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); //使能GPIOG时钟
  //GPIOB14
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;         //PB14
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;      //输出
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;     //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;       //上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);             //初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //PG7
  GPIO_Init(GPIOG, &GPIO_InitStructure);    //初始化
  GPIO_SetBits(GPIOG, GPIO_Pin_7);        //PG7输出1,防止NRF干扰SPI FLASH的通信
  W25QXX_CS = 1;                          //SPI FLASH不选中
  SPI1_Init();                            //初始化SPI
  SPI1_SetSpeed(SPI_BaudRatePrescaler_4); //设置为21M时钟
  W25QXX_TYPE = W25QXX_ReadID();          //读取FLASH ID.
}
//读取W25QXX的状态寄存器
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
u8 W25QXX_ReadSR(void)
{
  u8 byte = 0;
  W25QXX_CS = 0;                          //使能器件
  SPI1_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令
  byte = SPI1_ReadWriteByte(0Xff);        //读取一个字节
  W25QXX_CS = 1;                          //取消片选
  return byte;
}
//写W25QXX状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
void W25QXX_Write_SR(uint8_t sr)
{
  W25QXX_CS = 0;                           //使能器件
  SPI1_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
  SPI1_ReadWriteByte(sr);                  //写入一个字节
  W25QXX_CS = 1;                           //取消片选
}
//W25QXX写使能
//将WEL置位
void W25QXX_Write_Enable(void)
{
  W25QXX_CS = 0;                        //使能器件
  SPI1_ReadWriteByte(W25X_WriteEnable); //发送写使能
  W25QXX_CS = 1;                        //取消片选
}
//W25QXX写禁止
//将WEL清零
void W25QXX_Write_Disable(void)
{
  W25QXX_CS = 0;                         //使能器件
  SPI1_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
  W25QXX_CS = 1;                         //取消片选
}
//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
u16 W25QXX_ReadID(void)
{
  u16 Temp = 0;
  W25QXX_CS = 0;
  SPI1_ReadWriteByte(0x90); //发送读取ID命令
  SPI1_ReadWriteByte(0x00);
  SPI1_ReadWriteByte(0x00);
  SPI1_ReadWriteByte(0x00);
  Temp |= SPI1_ReadWriteByte(0xFF) << 8;
  Temp |= SPI1_ReadWriteByte(0xFF);
  W25QXX_CS = 1;
  return Temp;
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
  u16 i;
  W25QXX_CS = 0;                              //使能器件
  SPI1_ReadWriteByte(W25X_ReadData);          //发送读取命令
  SPI1_ReadWriteByte((u8)((ReadAddr) >> 16)); //发送24bit地址
  SPI1_ReadWriteByte((u8)((ReadAddr) >> 8));
  SPI1_ReadWriteByte((u8)ReadAddr);
  for (i = 0; i < NumByteToRead; i++)
  {
    pBuffer[i] = SPI1_ReadWriteByte(0XFF); //循环读数
  }
  W25QXX_CS = 1;
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  u16 i;
  W25QXX_Write_Enable();                       //SET WEL
  W25QXX_CS = 0;                               //使能器件
  SPI1_ReadWriteByte(W25X_PageProgram);        //发送写页命令
  SPI1_ReadWriteByte((u8)((WriteAddr) >> 16)); //发送24bit地址
  SPI1_ReadWriteByte((u8)((WriteAddr) >> 8));
  SPI1_ReadWriteByte((u8)WriteAddr);
  for (i = 0; i < NumByteToWrite; i++)
    SPI1_ReadWriteByte(pBuffer[i]); //循环写数
  W25QXX_CS = 1;                    //取消片选
  W25QXX_Wait_Busy();               //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  u16 pageremain;
  pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
  if (NumByteToWrite <= pageremain)
    pageremain = NumByteToWrite; //不大于256个字节
  while (1)
  {
    W25QXX_Write_Page(pBuffer, WriteAddr, pageremain);
    if (NumByteToWrite == pageremain)
      break; //写入结束了
    else     //NumByteToWrite>pageremain
    {
      pBuffer += pageremain;
      WriteAddr += pageremain;
      NumByteToWrite -= pageremain; //减去已经写入了的字节数
      if (NumByteToWrite > 256)
        pageremain = 256; //一次可以写入256个字节
      else
        pageremain = NumByteToWrite; //不够256个字节了
    }
  };
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
  u32 secpos;
  u16 secoff;
  u16 secremain;
  u16 i;
  u8 *W25QXX_BUF;
  W25QXX_BUF = W25QXX_BUFFER;
  secpos = WriteAddr / 4096; //扇区地址
  secoff = WriteAddr % 4096; //在扇区内的偏移
  secremain = 4096 - secoff; //扇区剩余空间大小
  //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
  if (NumByteToWrite <= secremain)
    secremain = NumByteToWrite; //不大于4096个字节
  while (1)
  {
    W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096); //读出整个扇区的内容
    for (i = 0; i < secremain; i++)               //校验数据
    {
      if (W25QXX_BUF[secoff + i] != 0XFF)
        break; //需要擦除
    }
    if (i < secremain) //需要擦除
    {
      W25QXX_Erase_Sector(secpos);    //擦除这个扇区
      for (i = 0; i < secremain; i++) //复制
      {
        W25QXX_BUF[i + secoff] = pBuffer[i];
      }
      W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096); //写入整个扇区
    }
    else
      W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.
    if (NumByteToWrite == secremain)
      break; //写入结束了
    else     //写入未结束
    {
      secpos++;   //扇区地址增1
      secoff = 0; //偏移位置为0
      pBuffer += secremain;        //指针偏移
      WriteAddr += secremain;      //写地址偏移
      NumByteToWrite -= secremain; //字节数递减
      if (NumByteToWrite > 4096)
        secremain = 4096; //下一个扇区还是写不完
      else
        secremain = NumByteToWrite; //下一个扇区可以写完了
    }
  };
}
//擦除整个芯片
//等待时间超长...
void W25QXX_Erase_Chip(void)
{
  W25QXX_Write_Enable(); //SET WEL
  W25QXX_Wait_Busy();
  W25QXX_CS = 0;                      //使能器件
  SPI1_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
  W25QXX_CS = 1;                      //取消片选
  W25QXX_Wait_Busy();                 //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个山区的最少时间:150ms
void W25QXX_Erase_Sector(uint32_t Dst_Addr)
{
  //监视falsh擦除情况,测试用
  //  printf("fe:%x\r\n",Dst_Addr);
  Dst_Addr *= 4096;
  W25QXX_Write_Enable(); //SET WEL
  W25QXX_Wait_Busy();
  W25QXX_CS = 0;                              //使能器件
  SPI1_ReadWriteByte(W25X_SectorErase);       //发送扇区擦除指令
  SPI1_ReadWriteByte((u8)((Dst_Addr) >> 16)); //发送24bit地址
  SPI1_ReadWriteByte((u8)((Dst_Addr) >> 8));
  SPI1_ReadWriteByte((u8)Dst_Addr);
  W25QXX_CS = 1;      //取消片选
  W25QXX_Wait_Busy(); //等待擦除完成
}
//等待空闲
void W25QXX_Wait_Busy(void)
{
  while ((W25QXX_ReadSR() & 0x01) == 0x01)
    ; // 等待BUSY位清空
}
//进入掉电模式
void W25QXX_PowerDown(void)
{
  W25QXX_CS = 0;                      //使能器件
  SPI1_ReadWriteByte(W25X_PowerDown); //发送掉电命令
  W25QXX_CS = 1;                      //取消片选
  _w25qxx_delay(0xff);                //等待TPD
}
//唤醒
void W25QXX_WAKEUP(void)
{
  W25QXX_CS = 0;                             //使能器件
  SPI1_ReadWriteByte(W25X_ReleasePowerDown); //  send W25X_PowerDown command 0xAB
  W25QXX_CS = 1;                             //取消片选
  _w25qxx_delay(0xff);                       //等待TRES1
}

完整工程

天翼云盘:下载链接:https://cloud.189.cn/t/uINbm2F7bYrm (访问码:5ptj)

码云:https://gitee.com/stylle/stm32f4-bsp-all/tree/master

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
6月前
【STM32】基于HAL库的360度编码器、摇杆代码编写
【STM32】基于HAL库的360度编码器、摇杆代码编写
|
1月前
stm32学习 3-2 LED流水灯
stm32学习 3-2 LED流水灯
69 4
|
1月前
stm32学习3-1 LED闪烁
stm32学习3-1 LED闪烁
34 4
|
5月前
|
开发者
【经典案例】使用HAL库配置STM32F407的SPI外设
在嵌入式系统开发中,STM32F407是一款广泛应用的微控制器,而SPI(Serial Peripheral Interface)是一种常用的通信接口。本文将详细介绍如何使用STM32的硬件抽象层(HAL)库配置STM32F407的SPI外设,并提供完整的代码示例。
558 1
|
5月前
|
存储 数据安全/隐私保护 芯片
【STM32】详解嵌入式中FLASH闪存的特性和代码示例
【STM32】详解嵌入式中FLASH闪存的特性和代码示例
【STM32】详解独立看门狗的本质和使用步骤&代码
【STM32】详解独立看门狗的本质和使用步骤&代码
|
5月前
|
传感器 数据格式
【STM32】DHT11温湿度模块传感器详解&代码
【STM32】DHT11温湿度模块传感器详解&代码
|
6月前
|
传感器
STM32循迹小车原理介绍和代码示例
STM32循迹小车原理介绍和代码示例
|
6月前
stm32f4外设学习篇(代码集合)(二)
stm32f4外设学习篇(代码集合)
|
6月前
|
芯片
stm32f4外设学习篇(代码集合)(一)
stm32f4外设学习篇(代码集合)
147 0