STM32F103RCT6 FLASH 256k RAM 48k
分区 地址 大小 作用
bootloader 0x8000 0000 32K 校验、引导APP、升级
APP 0x8000 8000 192K 主应用程序
外设w25x16 m24c02
main.c
#include "stm32f10x.h" // Device header
#include "main.h"
#include "usart.h"
#include "iic.h"
#include "m24c02.h"
#include "delay.h"
#include "w25x16.h"
#include "spi.h"
#include "flash.h"
#include "boot.h"
/*
STM32F103RCT6 FLASH 256k RAM 48k
分区 地址 大小 作用
bootloader 0x8000 0000 32K 校验、引导APP、升级
APP 0x8000 8000 192K 主应用程序
*/
OTA_InfoCB OTA_Info;
UpdateA_CB UpdateA;
uint32_t BootStateFlag;
int main(void){
uint16_t i; //用于for循环
//各个函数初始化
//Usart0_Init(921600);
Usart0_Init(115200);
USART_DMA_Init();
U0Rx_PtrInit();
delay_init();
IIC_Init();
M24C02_ReadOTAInfo();
W25X16_Init();
BootLoader_Branch();
while(1){
delay_ms(10);
if(U0CB.URxDataOUT != U0CB.URxDataIN){
BootLoader_Event( U0CB.URxDataOUT->start,U0CB.URxDataOUT->end - U0CB.URxDataOUT->start +1);
U0CB.URxDataOUT++;
if(U0CB.URxDataOUT == U0CB.URxDataEND){
U0CB.URxDataOUT = &U0CB.URxDataPtr[0];
}
}
if(BootStateFlag&IAP_XMODEMC_FLAG){
if(UpdateA.XmodemTimer>=100){
u0_printf("C");
UpdateA.XmodemTimer=0;
}
UpdateA.XmodemTimer++;
}
if(BootStateFlag&UPDATA_A_FLAG){
//更新A区
u0_printf("长度%d字节\r\n",OTA_Info.Filelen[UpdateA.W25X16_BlockNUM]);
if(OTA_Info.Filelen[UpdateA.W25X16_BlockNUM]%4==0){
ST32_EraseFlash(ST32_A_START_PAGE,ST32_A_PAGE_NUM);
for(i=0;i<OTA_Info.Filelen[UpdateA.W25X16_BlockNUM]/ST32_PAGE_SIZE;i++){
W25X16_Read(UpdateA.Updatabuff,i*1024+UpdateA.W25X16_BlockNUM*64*1024,ST32_PAGE_SIZE);
ST32_WriteFlash(ST32_A_SADDR+i*1024,(uint32_t *)UpdateA.Updatabuff,ST32_PAGE_SIZE);
}
if(OTA_Info.Filelen[UpdateA.W25X16_BlockNUM]%ST32_PAGE_SIZE!=0){
W25X16_Read(UpdateA.Updatabuff,i*1024+UpdateA.W25X16_BlockNUM*64*1024,OTA_Info.Filelen[UpdateA.W25X16_BlockNUM]%ST32_PAGE_SIZE);
ST32_WriteFlash(ST32_A_SADDR+i*1024,(uint32_t *)UpdateA.Updatabuff,OTA_Info.Filelen[UpdateA.W25X16_BlockNUM]%ST32_PAGE_SIZE);
}
if(UpdateA.W25X16_BlockNUM==0){
OTA_Info.OTA_flag = 0;
M24C02_WriteOTAInfo();
}
u0_printf("A区更新完毕\r\n");
NVIC_SystemReset();
}
else{
u0_printf("长度错误\r\n");
BootStateFlag &=~ UPDATA_A_FLAG;
}
}
}
}
main.h
#ifndef __MAIN_H
#define __MAIN_H
#include "stm32f10x.h"
#include "stdint.h"
#define ST32_FLASH_SADDR 0X08000000 //flash起始地址
#define ST32_PAGE_SIZE 1024 //flash扇区大小
#define ST32_PAGE_NUM 256 //flash扇区总个数
#define ST32_B_PAGE_NUM 32 //B区扇区个数
#define ST32_A_PAGE_NUM ST32_PAGE_NUM-ST32_B_PAGE_NUM //A区扇区个数
#define ST32_A_START_PAGE ST32_B_PAGE_NUM //A区起始扇区编号
#define ST32_A_SADDR ST32_FLASH_SADDR+ST32_A_START_PAGE*ST32_PAGE_SIZE //A区起始地址
#define UPDATA_A_FLAG 0x00000001
#define IAP_XMODEMC_FLAG 0x00000002
#define IAP_XMODEMD_FLAG 0x00000004
#define SET_VERSION_FLAG 0x00000008
#define CMD_5_FLAG 0x00000010
#define CMD5_XMODEM_FLAG 0x00000020
#define CMD_6_FLAG 0x00000040
#define OTA_SET_FLAG 0xAABB1122
typedef struct{ //24c02
uint32_t OTA_flag;
uint32_t Filelen[11]; //0号成员固定对应OTA升级时的大小
uint8_t OTA_ver[32]; //eg:VER-1.0.0-2023/10/08-20:00
}OTA_InfoCB; //24c02一页16个字节,12*4=48刚好三页
#define OTA_INFOCB_SIZE sizeof(OTA_InfoCB)
typedef struct{
uint8_t Updatabuff[ST32_PAGE_SIZE];
uint32_t W25X16_BlockNUM;
uint32_t XmodemTimer;
uint32_t XmodemNUM;
uint32_t XmodemCRC;
}UpdateA_CB;
extern OTA_InfoCB OTA_Info;
extern UpdateA_CB UpdateA;
extern uint32_t BootStateFlag;
#endif
uart.c
#include "usart.h"
/*
1.串口接收缓冲区,一维数组 DMA+空闲中断
2.确定单次接收的最大量 MAX (一旦剩余的空闲内存小于max值,就让指针回到s[0])
3.还有一个重要的变量,统计当前接收缓冲区已经存放的累加值(确定空闲内存)
4.se[]指针对(记录每一次数据start和end的位置),结构体
5.se指针对数组,上方结构体数组
6.IN+OUT+END指针
*/
uint8_t U0_RxBuff[U0_RX_SIZE];
uint8_t U0_TxBuff[U0_TX_SIZE];
UCB_CB U0CB;
void NVIC_Config(void){
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //中断处理函数USART1_IRQHandler
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //0级优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //0级抢占
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能NVIC
NVIC_Init(&NVIC_InitStruct);
}
void Usart0_Init(uint32_t bandrate){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO|RCC_APB2Periph_USART1, ENABLE); //开启时钟
//TXD PA9
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//RXD PA10
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
// USART_TypeDef USART1;
USART_InitTypeDef USART_InitStruct;
USART_DeInit(USART1);
USART_InitStruct.USART_BaudRate = bandrate; //波特率
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_InitStruct.USART_StopBits = USART_StopBits_1; //一位停止位
USART_InitStruct.USART_Parity = USART_Parity_No; //无校验
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流
USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; //收发模式
USART_Init(USART1,&USART_InitStruct);
NVIC_Config();
//USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //使能接收中断
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //开启 USART1 总线空闲中断
USART_Cmd(USART1,ENABLE); //使能usart1
}
void USART_DMA_Init(void){
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//开启时钟
DMA_InitTypeDef DMA_InitStruct;
DMA_DeInit(DMA1_Channel5); //dma1的通道1有usart1的接收功能
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; //外设是usart1的数据寄存器
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据大小为1字节
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)U0_RxBuff; //内存地址是U0_RxBuff数组
DMA_InitStruct.DMA_BufferSize = U0_RX_MAX+1; //数组大小,加1是为了防止DMA产生完成中断
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //传输的数据大小为1字节
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //正常模式
DMA_InitStruct.DMA_Priority = DMA_Priority_High; //高优先级
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址使能
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为数据源
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //失能内存到内存
DMA_Init(DMA1_Channel5 , &DMA_InitStruct);
DMA_ITConfig(DMA1_Channel5,DMA1_IT_GL5, DISABLE);//关闭DMA中断 //DMA1_IT_GL5 通道5 全局中断
DMA_Cmd(DMA1_Channel5, ENABLE); //使能DMA1_Channel5 //DMA1_IT_TC5 通道5 传输完成中断
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); // 使能 USART1接收DMA //DMA1_IT_TE5 通道5 传输错误中断
//使能串口的dma非常重要 //DMA1_IT_HT5 通道5 传输过半中断
}
//初始化数组指针
void U0Rx_PtrInit(void){
U0CB.URxDataIN = &U0CB.URxDataPtr[0]; //接收数组的写入指针
U0CB.URxDataOUT = &U0CB.URxDataPtr[0]; //接收数组的读出指针
U0CB.URxDataEND = &U0CB.URxDataPtr[NUM-1]; //标记数组尾的指针
U0CB.URxDataIN->start = U0_RxBuff; //接收数组的写入指针的头指针指向缓冲区的开始
U0CB.URxCounter=0; //初始化记录接收数据计数
}
//中断处理函数
void USART1_IRQHandler(void){
// uint8_t tmp;
if(USART_GetITStatus(USART1, USART_IT_IDLE)!=0){ //IDLE=1时,说明产生了空闲中断
//清除USART_FLAG_IDLE的步骤:由软件序列清除该位(先读USART_SR,然后读USART_DR)。
USART_GetFlagStatus(USART1,USART_FLAG_IDLE);
USART_ReceiveData(USART1);
//USART_ClearITPendingBit(USART1,USART_FLAG_IDLE);
//已经接收的数据单元数 = 总的DMA的大小 - 当前DMA1_Channel5传输中剩余的数据单元数
U0CB.URxCounter += (U0_RX_MAX+1) - DMA_GetCurrDataCounter(DMA1_Channel5);
U0CB.URxDataIN->end = &U0_RxBuff[U0CB.URxCounter-1];
U0CB.URxDataIN++;
if(U0CB.URxDataIN == U0CB.URxDataEND){ //如果写入指针等于数组尾的指针,则让写入指针指向数组头部
U0CB.URxDataIN = &U0CB.URxDataPtr[0];
}
if((U0_RX_SIZE - U0CB.URxCounter)>= U0_RX_MAX){
//数组的大小 - 计数值 >= 一次DMA传输数据的大小,说明还能接收数据
U0CB.URxDataIN->start = &U0_RxBuff[U0CB.URxCounter];
}else{
U0CB.URxDataIN->start = U0_RxBuff; //内存不够,接收数组的写入指针的头指针返回缓冲区头部
U0CB.URxCounter = 0;
}
DMA_Cmd(DMA1_Channel5, DISABLE); //使能DMA,修改DMA参数
DMA_SetCurrDataCounter(DMA1_Channel5,U0_RX_MAX+1); //重新设置DMA传输的数据单元数
DMA1_Channel5->CMAR = (uint32_t)U0CB.URxDataIN->start; //让DMA的内存地址等于接收数组的写入指针的头指针的地址
DMA_Cmd(DMA1_Channel5, ENABLE); //使能DMA
}
}
//重写printf函数,能打印不同类型的数据
void u0_printf(char *format,...){
uint16_t i;
va_list listdata;
va_start(listdata,format);
vsprintf((char *)U0_TxBuff,format,listdata);
va_end(listdata);
for(i=0;i<strlen((const char *)U0_TxBuff);i++){
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);//若USART_FLAG_TXE=1,则缓冲区为空,可以发送数据
USART_SendData(USART1,U0_TxBuff[i]);
}
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET); //等数据发送完了,再退出函数u0_printf
}
uart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "stdarg.h"
#include "string.h"
#include "stm32f10x.h"
#define U0_RX_SIZE 2048
#define U0_TX_SIZE 2048
#define U0_RX_MAX 256
#define NUM 10
typedef struct{
uint8_t *start;
uint8_t *end;
}UCB_URxBuffptr;
typedef struct{
uint16_t URxCounter;
UCB_URxBuffptr URxDataPtr[NUM];
UCB_URxBuffptr *URxDataIN;
UCB_URxBuffptr *URxDataOUT;
UCB_URxBuffptr *URxDataEND;
}UCB_CB;
extern UCB_CB U0CB;
extern uint8_t U0_RxBuff[U0_RX_SIZE];
void NVIC_Config(void);
void Usart0_Init(uint32_t bandrate);
void USART_DMA_Init(void);
void USART1_IRQHandler(void);
void U0Rx_PtrInit(void);
void u0_printf(char *format,...);
#endif
IIC.c
#include "iic.h"
#include "usart.h"
void IIC_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC |RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12; //
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOC, &GPIO_InitStruct);
IIC_SCL_H;
IIC_SDA_H;
}
void IIC_Start(void){
IIC_SCL_H; //时钟线拉高
IIC_SDA_H; //数据线拉高,初始化两条总线
delay_us(2);
IIC_SDA_L; //拉低数据线,表示开始信号
delay_us(2);
IIC_SCL_L; //拉低时钟线,方便数据线改变数据
}
void IIC_Stop(void){
IIC_SCL_L;
IIC_SDA_L;//STOP:when CLK is high DATA change form low to high
delay_us(2);
IIC_SCL_H; //拉高时钟线
delay_us(2);
IIC_SDA_H;
}
void IIC_Send_Byte(uint8_t txd){
int8_t i;
for(i=7;i>=0;i--){ //从高地址往低地址传输
IIC_SCL_L; //拉低时钟线,为数据线改变数据做准备
if(txd&BIT(i)) //八位的txd数据和BIT(x)与操作,当txd对应的位为1时,sda拉高输出高电平
IIC_SDA_H;
else
IIC_SDA_L; //与操作结果位0时,sda输出低电平
delay_us(2);
IIC_SCL_H; //scl拉高,保证sda正确传输数据
delay_us(2);
}
IIC_SCL_L; //传输结束,双总线初始化
IIC_SDA_H;
}
//等待从机应答信号
uint8_t ICC_Wait_Ack(int16_t timeout){
do{
timeout--;
delay_us(2);
}while((READ_SDA)&&(timeout>=0));
if(timeout<0)return 1; //接收异常
IIC_SCL_H;
delay_us(2);
if(READ_SDA !=0) return 2;//若在timeout倒数结束前READ_SDA =0,就在拉高时钟总线后,再次判断数据总线是不是稳定的低电平
IIC_SCL_L; //应答正常,拉低时钟总线,防止数据总线误操作
delay_us(2);
return 0;
}
uint8_t IIC_Read_Byte(uint8_t ack){
int8_t i;
uint8_t rxd;
rxd = 0x00;
for(i=7;i>=0;i--){
IIC_SCL_L;
delay_us(2);
IIC_SCL_H;
// u0_printf("%d",READ_SDA);
if(READ_SDA){
rxd |= BIT(i);
}
delay_us(2);
}
// u0_printf("\r\n");
IIC_SCL_L;
delay_us(2);
if(ack){
IIC_SDA_L;
IIC_SCL_H;
delay_us(2);
IIC_SCL_L;
IIC_SDA_H;
delay_us(2);
}else{
IIC_SDA_H;
IIC_SCL_H;
delay_us(2);
IIC_SCL_L;
delay_us(2);
}
return rxd;
}
IIC.h
#ifndef __IIC_H
#define __IIC_H
#include "stm32f10x.h"
#include "delay.h"
#define IIC_SCL_H GPIO_SetBits(GPIOC,GPIO_Pin_12)
#define IIC_SCL_L GPIO_ResetBits(GPIOC,GPIO_Pin_12)
#define IIC_SDA_H GPIO_SetBits(GPIOC,GPIO_Pin_11)
#define IIC_SDA_L GPIO_ResetBits(GPIOC,GPIO_Pin_11)
#define BIT(x) ((uint32_t)((uint32_t)(0x01U<<x))) //作用是把1左移x位
#define READ_SDA GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_11)
void IIC_Init(void);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_Send_Byte(uint8_t txd);
uint8_t ICC_Wait_Ack(int16_t timeout);
uint8_t IIC_Read_Byte(uint8_t ack);
#endif
SPI.c
#include "spi.h"
#include "usart.h"
//DMA1的通道3 :SPI1_TX
//DMA1的通道2 :SPI1_RX
void SPI1_INIT(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO|RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; //PA5: SPI_CLK; PA7: SPI_MOSI
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; //PA6: SPI_MISO
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双向全双工
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //主机
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //8位数据帧
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //串行时钟在不操作时,时钟为低电平
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //第一个跳变边沿开始采样数据 配置为模式0
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; //软件管理
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //2分频
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //设置数据传输顺序,高位在前,低位在前两种
SPI_InitStruct.SPI_CRCPolynomial = 7; //设置CRC校验多项式,提高通信可靠性,大于1即可
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);//使能SPI1
}
//spi收发1个字节
uint8_t SPI1_RW_Byte(uint8_t txd){
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=1);
SPI_I2S_SendData(SPI1,txd);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=1);
return SPI_I2S_ReceiveData(SPI1);
}
void SPI1_Write(uint8_t *wdata,uint16_t datalen){
int16_t i;
for(i=0;i<datalen;i++){
SPI1_RW_Byte(wdata[i]);
// u0_printf("%d\r\n",wdata[i]);
}
}
void SPI1_Read(uint8_t *rdata,uint16_t datalen){
int16_t i;
for(i=0;i<datalen;i++){
rdata[i] = SPI1_RW_Byte(0xff);
}
}
//void SPI_DMA_config(void){
//
// RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//开启时钟
// RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1, ENABLE );
//
// SPI_InitTypeDef SPI_InitStruct;
// /* SPI_MASTER configuration ------------------------------------------------*/
// SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Tx;//设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
// SPI_InitStruct.SPI_Mode = SPI_Mode_Master;//主机
// SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;//8位数据帧
// SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //串行时钟在不操作时,时钟为低电平
// SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //第一个跳变边沿开始采样数据
// SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//软件管理
// SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//2分频
// SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//设置数据传输顺序,MSB位在前,LSB位在前两种 LSB:least significant bit 表示二进制数据 MSB : most significant bit 表示二进制数据的最高位
// SPI_InitStruct.SPI_CRCPolynomial = 7; //设置CRC校验多项式,提高通信可靠性,大于1即可
// SPI_Init(SPI1, &SPI_InitStruct);
// /* SPI_MASTER_Tx_DMA_Channel configuration ---------------------------------*/
// DMA_InitTypeDef DMA_InitStruct;
// DMA_DeInit(DMA1_Channel3);
// DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR; //主机的DR基址
// DMA_InitStruct.DMA_BufferSize = 128;
// //DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)SPI_MASTER_Buffer_Tx;
// DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; //从机作为目的地址
//
// DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设置外设地址是否递增
// DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //设置内存地址是否递增
// DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度为8位
// DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //内存数据宽度为8位
//
// DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; //普通缓存模式 (DMA仅传输一次)
// DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; //高优先级
// DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; //禁止DMA2个内存相互访问
// DMA_Init(DMA1_Channel3, &DMA_InitStruct);
//
//
// //DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE); //开启 DMA1_Channel3 传输完成中断
// //DMA_Cmd(DMA1_Channel3, ENABLE);
// //SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,ENABLE); /*使能SPI1 DMA发送功能*/
// SPI_Cmd(SPI1, ENABLE);//使能SPI1
// DMA_Cmd(DMA1_Channel3, DISABLE);//关闭DMA,方便后续更改BUFFER的地址和传输数据的长度
//}
SPI.h
#ifndef _SPI_H
#define _SPI_H
#include "stm32f10x.h"
void SPI1_INIT(void);
uint8_t SPI1_RW_Byte(uint8_t txd);
void SPI1_Write(uint8_t *wdata,uint16_t datalen);
void SPI1_Read(uint8_t *rdata,uint16_t datalen);
#endif
M24C02.c
#include "m24c02.h"
//#include "usart.h"
//function:按字节写入
uint8_t M24C02_WriteByte(uint8_t addr,uint8_t wdata){
IIC_Start();
IIC_Send_Byte(M24C02_WADDR); //写入的器件地址
if(ICC_Wait_Ack(100)!=0) return 1;
IIC_Send_Byte(addr); //存储地址(0~255)
if(ICC_Wait_Ack(100)!=0) return 2;
IIC_Send_Byte(wdata); //存储的数据
if(ICC_Wait_Ack(100)!=0) return 3;
IIC_Stop();
return 0;
}
//页写入的功能:从地址7写到15的时候,不会写到地址16(即不会写到第二页),它会回卷到地址0开始写
//所以写入的起始地址最好是页的起始地址
uint8_t M24C02_WritePage(uint8_t addr,uint8_t *wdata){
uint8_t i;
IIC_Start();
IIC_Send_Byte(M24C02_WADDR); //写入的器件地址
if(ICC_Wait_Ack(100)!=0) return 1;
IIC_Send_Byte(addr); //存储地址(0~255)
if(ICC_Wait_Ack(100)!=0) return 2;
for(i=0;i<16;i++){
IIC_Send_Byte(wdata[i]);
if(ICC_Wait_Ack(100)!=0) return 3+i;
}
IIC_Stop();
return 0;
}
uint8_t M24C02_ReadData(uint8_t addr,uint8_t *rdata,uint16_t datalen){
uint8_t i;
IIC_Start();
IIC_Send_Byte(M24C02_WADDR); //写入的器件地址
if(ICC_Wait_Ack(100)!=0) return 1;
IIC_Send_Byte(addr); //存储地址(0~255)
if(ICC_Wait_Ack(100)!=0) return 2;
IIC_Start();
IIC_Send_Byte(M24C02_RADDR);
if(ICC_Wait_Ack(100)!=0) return 3;
for(i=0;i<datalen-1;i++){
rdata[i] = IIC_Read_Byte(1);
// u0_printf(" %d\r\n",rdata[i]);
}
rdata[datalen-1] = IIC_Read_Byte(0);
IIC_Stop();
return 0;
}
void M24C02_ReadOTAInfo(void){
memset(&OTA_Info,0,OTA_INFOCB_SIZE);
M24C02_ReadData(0,(uint8_t *)&OTA_Info,OTA_INFOCB_SIZE);
}
void M24C02_WriteOTAInfo(void){
uint8_t i;
uint8_t *wptr;
wptr = (uint8_t *)&OTA_Info;
for(i=0;i<OTA_INFOCB_SIZE/16;i++){
M24C02_WritePage(i*16,wptr+i*16);
delay_ms(5);
}
}
M24C02.h
#ifndef __M24C02_H
#define __M24C02_H
#include "stm32f10x.h"
#include "iic.h"
#include "delay.h"
#include "stdint.h"
#include "main.h"
#include "string.h"
#define M24C02_WADDR 0xA0
#define M24C02_RADDR 0xA1
uint8_t M24C02_WriteByte(uint8_t addr,uint8_t wdata);
uint8_t M24C02_WritePage(uint8_t addr,uint8_t *wdata);
uint8_t M24C02_ReadData(uint8_t addr,uint8_t *rdata,uint16_t datalen);
void M24C02_ReadOTAInfo(void);
void M24C02_WriteOTAInfo(void);
#endif
W25X16.c
#include "w25x16.h"
void W25X16_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; //PA2:CS
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
CS_DISABLE;
SPI1_INIT();
}
//Read Status Register 发送0x05
//W25X16是用指令驱动的
void W25X16_WaitBusy(void){
uint8_t res;
do{
CS_ENABLE; //拉低片选线,选中w25x16
SPI1_RW_Byte(0x05); //告诉w25x16,我要读状态寄存器,返回值没有意义,不接收
res = SPI1_RW_Byte(0xff); //发0xff是为了防止误发送其他指令,返回来的值就是需要的状态寄存器的值
CS_DISABLE; //拉高片选线,告诉w25x16,指令已经发完了
}while((res&0x01)==0x01);//监测最低位s0(busy),如果s0位为0,则说明等到器件空闲了,可以进行其他的操作了
}
//使能写操作
void W25X16_Enable(void){
W25X16_WaitBusy();
CS_ENABLE; //拉低片选线,
SPI1_RW_Byte(0x06); //Write Enable
CS_DISABLE; //拉高片选线,
}
// 16Mbit /8 = 2M byte
// 2M * 1024 = 2048k
// 2048k /64 = 32
// W25X16可以分为32块(block),每块64k
// 擦除块内存
void W25X16_Erase64K(uint8_t blockNum){
uint8_t wdata[4];
wdata[0] = 0xD8; //Block Erase (64KB)
wdata[1] = (blockNum*64*1024)>>16;//用块号*64*1024作为当期块号起始地址
wdata[2] = (blockNum*64*1024)>>8;
wdata[3] = (blockNum*64*1024)>>0;
W25X16_WaitBusy();
W25X16_Enable();
CS_ENABLE; //拉低片选线,
SPI1_Write(wdata,4);
CS_DISABLE; //拉高片选线,
W25X16_WaitBusy();//再次访问,等待擦除完毕再退出函数
}
//param: 需要写进w25x16的数据数组 页号
void W25X16_PageWrite(uint8_t *wbuff,uint16_t pageNum){
uint8_t wdata[4];
wdata[0] = 0x02; //Page Program (Up to 256 bytes)
wdata[1] = (pageNum*256)>>16;//用页号*256作为当前页起始地址
wdata[2] = (pageNum*256)>>8;
wdata[3] = (pageNum*256)>>0;
W25X16_WaitBusy();
W25X16_Enable();
CS_ENABLE; //拉低片选线,
SPI1_Write(wdata,4);
SPI1_Write(wbuff,256);
CS_DISABLE; //拉高片选线,
}
//param: 读到数据存放的数组 读数据的地址 数据长度
void W25X16_Read(uint8_t *rbuff,uint32_t addr,uint32_t datalen){
uint8_t wdata[4];
wdata[0] = 0x03; //Read Data
wdata[1] = (addr)>>16;
wdata[2] = (addr)>>8;
wdata[3] = (addr)>>0;
W25X16_WaitBusy();
W25X16_Enable();
CS_ENABLE; //拉低片选线,
SPI1_Write(wdata,4);
SPI1_Read(rbuff,datalen);
CS_DISABLE; //拉高片选线
}
W25X16.h
#ifndef _W25X16_H
#define _W25X16_H
#include "stm32f10x.h"
#include "spi.h"
#define CS_ENABLE GPIO_ResetBits(GPIOA,GPIO_Pin_2)
#define CS_DISABLE GPIO_SetBits(GPIOA,GPIO_Pin_2)
void W25X16_Init(void);
void W25X16_WaitBusy(void);
void W25X16_Enable(void);
void W25X16_Erase64K(uint8_t blockNum);
void W25X16_PageWrite(uint8_t *wbuff,uint16_t pageNum);
void W25X16_Read(uint8_t *rbuff,uint32_t addr,uint32_t datalen);
#endif
Flash.c
#include "flash.h"
/*按页擦除flash(一次擦除1k)闪存的页大小是1KB*/
//start:起始地址 num:擦除的次数
void ST32_EraseFlash(uint16_t start,uint16_t num){
uint16_t i;
FLASH_Unlock();
for(i=0;i<num;i++){
FLASH_ErasePage((0x08000000+start*1024)+(1024*i)); //stm32的起始地址是0x08000000
}
FLASH_Lock();
}
/*写flash,按字写入*/
//saddr:起始地址 *wdata:写入的数据 wnum:数据的个数
void ST32_WriteFlash(uint32_t saddr,uint32_t *wdata,uint32_t wnum){
FLASH_Unlock();
while(wnum){
FLASH_ProgramWord(saddr,*wdata);
wnum-=4;
saddr+=4;
wdata++;
}
FLASH_Lock();
}
Flash.h
#ifndef __FLASH_H
#define __FLASH_H
#include "stm32f10x.h"
#include "stdint.h"
void ST32_EraseFlash(uint16_t start,uint16_t num);
void ST32_WriteFlash(uint32_t saddr,uint32_t *wdata,uint32_t num);
#endif
boot.c
#include "boot.h"
load_a load_A;
void BootLoader_Branch(void){
if(BootLoader_Enter(20)==0){
if(OTA_Info.OTA_flag==OTA_SET_FLAG){
u0_printf("OTA更新\r\n");
BootStateFlag |= UPDATA_A_FLAG;
UpdateA.W25X16_BlockNUM = 0;
}else{
u0_printf("跳转A区\r\n");
LOAD_A(ST32_A_SADDR);
}
}
u0_printf("进入bootloader命令行\r\n");
BootLoader_Info();
}
uint8_t BootLoader_Enter(uint8_t timeout){
u0_printf("%ds内,输入小写字母w,进入bootloader命令行\r\n",timeout*100);
while(timeout--){
delay_ms(100);
if(U0_RxBuff[0]=='w'){
return 1; //进入命令行
}
}
return 0; //不进入命令行
}
/*-------------------------------------------------*/
/*函数名:串口输出命令行信息 */
/*参 数:无 */
/*返回值:无 */
/*-------------------------------------------------*/
void BootLoader_Info(void)
{
u0_printf("\r\n"); //串口1输出信息
u0_printf("[1]擦除A区\r\n"); //串口1输出信息
u0_printf("[2]串口IAP下载A区程序\r\n"); //串口1输出信息
u0_printf("[3]设置OTA版本号\r\n"); //串口1输出信息
u0_printf("[4]查询OTA版本号\r\n"); //串口1输出信息
u0_printf("[5]向外部Flash下载程序\r\n"); //串口1输出信息
u0_printf("[6]使用外部Flash内程序\r\n"); //串口1输出信息
u0_printf("[7]重启\r\n");
}
void BootLoader_Event(uint8_t *data,uint16_t datalen){
int temp,i;
if(BootStateFlag==0){
if((datalen==1)&&(data[0]=='1')){
u0_printf("擦除A区\r\n");
ST32_EraseFlash(ST32_A_START_PAGE,ST32_A_PAGE_NUM);
}
else if((datalen==1)&&(data[0]=='2')){
u0_printf("通过Xmodem协议,串口IAP下载A区程序,请使用bin格式文件\r\n");
ST32_EraseFlash(ST32_A_START_PAGE,ST32_A_PAGE_NUM);
BootStateFlag |= (IAP_XMODEMC_FLAG|IAP_XMODEMD_FLAG);
UpdateA.XmodemTimer = 0;
UpdateA.XmodemNUM = 0;
}
else if((datalen==1)&&(data[0]=='3')){
u0_printf("设置版本号\r\n");
BootStateFlag |= SET_VERSION_FLAG;
}
else if((datalen==1)&&(data[0]=='4')){
u0_printf("查询版本号\r\n");
M24C02_ReadOTAInfo();
u0_printf("版本号:%s\r\n",OTA_Info.OTA_ver);
BootLoader_Info();
}
else if((datalen==1)&&(data[0]=='5')){
u0_printf("向外部FLASH下载程序,输入需要使用的块编号(1-9)\r\n");
BootStateFlag |= CMD_5_FLAG;
}
else if((datalen==1)&&(data[0]=='6')){
u0_printf("使用外部FLASH内部的程序,输入需要使用的块编号(1-9)\r\n");
BootStateFlag |= CMD_5_FLAG;
}
else if((datalen==1)&&(data[0]=='7')){
u0_printf("重启\r\n");
delay_ms(100);
NVIC_SystemReset();
}
}
else if(BootStateFlag&IAP_XMODEMD_FLAG){
if((datalen==133)&&(data[0]==0x01)){
BootStateFlag &=~ IAP_XMODEMC_FLAG;
UpdateA.XmodemCRC = Xmodem_CRC16(&data[3],128);
if(UpdateA.XmodemCRC == data[131]*256+data[132]){
UpdateA.XmodemNUM++;
memcpy(&UpdateA.Updatabuff[(UpdateA.XmodemNUM-1)%(ST32_PAGE_SIZE/128)*128],&data[3],128);
if((UpdateA.XmodemNUM%(ST32_PAGE_SIZE/128))==0){
if(BootStateFlag&CMD_5_FLAG){
for(i=0;i<4;i++){
W25X16_PageWrite(&UpdateA.Updatabuff[i*256],((UpdateA.XmodemNUM/8-1)*4+i)+UpdateA.W25X16_BlockNUM*64*4);
}
}
else{
ST32_WriteFlash(ST32_A_SADDR+((UpdateA.XmodemNUM/(ST32_PAGE_SIZE/128))-1)*1024 ,(uint32_t *)UpdateA.Updatabuff , ST32_PAGE_SIZE);
}
}
u0_printf("\x06");
}else{
u0_printf("\x15");
}
}
if((datalen==1)&&(data[0]==0x04)){
u0_printf("\x06");
if((UpdateA.XmodemNUM%(ST32_PAGE_SIZE/128))!=0){
if(BootStateFlag&CMD_5_FLAG){
for(i=0;i<4;i++){
W25X16_PageWrite(&UpdateA.Updatabuff[i*256],((UpdateA.XmodemNUM/8)*4+i)+UpdateA.W25X16_BlockNUM*64*4);
}
}
else{
ST32_WriteFlash(ST32_A_SADDR+(UpdateA.XmodemNUM/(ST32_PAGE_SIZE/128))*ST32_PAGE_SIZE ,(uint32_t *)UpdateA.Updatabuff ,(UpdateA.XmodemNUM%(ST32_PAGE_SIZE/128))*128);
}
}
BootStateFlag &=~ IAP_XMODEMD_FLAG;
if(BootStateFlag&CMD_5_FLAG){
BootStateFlag&=~CMD_5_FLAG;
OTA_Info.Filelen[UpdateA.W25X16_BlockNUM] = UpdateA.XmodemNUM*128;
M24C02_WriteOTAInfo();
delay_ms(100);
BootLoader_Info();
}
else{
delay_ms(100);
NVIC_SystemReset();
}
}
}
else if(BootStateFlag&SET_VERSION_FLAG){
if(datalen==26){
if(sscanf((char*)data,"VER-%d.%d.%d-%d/%d/%d-%d:%d",&temp,&temp,&temp,&temp,&temp,&temp,&temp,&temp)==8){
memset(OTA_Info.OTA_ver,0,0);
memcpy(OTA_Info.OTA_ver,data,26);
M24C02_WriteOTAInfo();
u0_printf("设置版本号正确\r\n");
BootLoader_Info();
BootStateFlag &=~ SET_VERSION_FLAG;
}else u0_printf("设置版本号格式错误\r\n");
}else u0_printf("设置版本号长度错误\r\n");
}
else if(BootStateFlag&CMD_5_FLAG){
if(datalen==1){
if((data[0]>=0x31)&&(data[0]<=0x39)){ //字符1的ascii码 0x31
UpdateA.W25X16_BlockNUM = data[0] - 0x30;
BootStateFlag |= (IAP_XMODEMC_FLAG|IAP_XMODEMD_FLAG|CMD5_XMODEM_FLAG);
UpdateA.XmodemTimer = 0;
UpdateA.XmodemNUM = 0;
OTA_Info.Filelen[UpdateA.W25X16_BlockNUM] = 0;
W25X16_Erase64K(UpdateA.W25X16_BlockNUM);
BootStateFlag&=~CMD_5_FLAG;
u0_printf("通过Xmodem协议,向外部FLASH(w25x16)第%d个下载程序,请使用bin格式文件\r\n",UpdateA.W25X16_BlockNUM);
}else u0_printf("编号错误\r\n");
}else u0_printf("数据长度错误\r\n");
}
else if(BootStateFlag&CMD_6_FLAG){
if(datalen==1){
if((data[0]>=0x31)&&(data[0]<=0x39)){ //字符1的ascii码 0x31
UpdateA.W25X16_BlockNUM = data[0] - 0x30;
BootStateFlag |= UPDATA_A_FLAG;
BootStateFlag&=~CMD_6_FLAG;
u0_printf("通过Xmodem协议,向外部FLASH(w25x16)第%d个下载程序,请使用bin格式文件\r\n",UpdateA.W25X16_BlockNUM);
}else u0_printf("编号错误\r\n");
}else u0_printf("数据长度错误\r\n");
}
}
/*
跳转A区的关键
1.设置sp,A区的起始位置0x080008000,给到sp
2.设置pc,A区的起始位置0x080008000+4,给到pc
补充:把A区用到的外设,寄存器reset
RCT6的RAM:0x20000000 ~ 0x2000BFFF
*/
__ASM void MSR_SP(uint32_t addr){
MSR MSP,R0
BX R14
// #pragma asm
// MSR MSP,R0
// BX R14
// #pragma endasm
}
void LOAD_A(uint32_t addr){
if(*(uint32_t *)addr>=0x20000000&&*(uint32_t *)addr<=0x2000BFFF){
MSR_SP(*(uint32_t *)addr);
load_A = (load_a)*(uint32_t *)(addr+4);// 把DCD Reset_Handler的地址给load_A
load_A();
}else{
u0_printf("跳转A分区失败\r\n");
}
}
void BootLoader_Clear(void){
USART_DeInit(USART1);
GPIO_DeInit(GPIOA);
GPIO_DeInit(GPIOC);
}
uint16_t Xmodem_CRC16(uint8_t *data,uint16_t datalen){
uint8_t i;
uint16_t Crcinit = 0x0000;
uint16_t Crcpoly = 0x1021;
while(datalen--){
Crcinit = (*data << 8) ^ Crcinit;
for(i=0;i<8;i++){
if(Crcinit&0x8000){
Crcinit = (Crcinit<<1) ^ Crcpoly;
}
else{
Crcinit = (Crcinit<<1);
}
}
data++;
}
return Crcinit;
}
boot.h
#ifndef __BOOT_H
#define __BOOT_H
#include "stm32f10x.h" // Device header
#include "main.h"
#include "usart.h"
#include "iic.h"
#include "m24c02.h"
#include "delay.h"
#include "w25x16.h"
#include "spi.h"
#include "flash.h"
typedef void (*load_a)(void);
void BootLoader_Branch(void);
__ASM void MSR_SP(uint32_t addr);
void LOAD_A(uint32_t addr);
void BootLoader_Clear(void);
uint8_t BootLoader_Enter(uint8_t timeout);
void BootLoader_Info(void);
void BootLoader_Event(uint8_t *data,uint16_t datalen);
uint16_t Xmodem_CRC16(uint8_t *data,uint16_t datalen);
#endif