基于STM32的bootloader

 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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值