中级会员
- 积分
- 268
- 金钱
- 268
- 注册时间
- 2021-10-7
- 在线时间
- 44 小时
|
AVR串口下载软件程序一般是USBASP,需要连接MSIO,MISO,SCK,RESET引脚。学习STM32时发现有个软件FlyMCU,就是通过USART串口下载软件的,感觉挺方便的,于是仿照FlyMCU做了个AVR的USART串口下载软件,在ATMEGA328,ATMEGA8上使用很久了,没问题。
AVR通过USART串口下载软件本质上就是使用BootLoader,这个AVR中文手册有详细说明。下面的代码大部分是网上抄的,按照自己的需求改了下,适合Atmel Studio 7.0的编译。
1、BootLoader程序
/**********************************************************************************************
**自编程,以USART串口作为接收数据
**需要进行的宏定义
**1、BOOTLOADER_BAUDRATE,用于定义串口传输速率,如果未定义,默认为9600
**2、BOOTLOADER_BOOTSTART,用于定义Boot区的起始地址,取决于单片机熔丝位设置,未定义是默认为0x1C00
**3、BOOTLOADER_RECVBUFSIZE,用于定义串口接收缓冲大小,最大不能超过256,如未定义默认为128
**4、BOOTLOADER_FLASHEND,用于定义MCU的Flash大小,如果未定义,默认8192-1=8191(0x1FFF)
**5、BOOTLOADER_EEPROMEND,用于定义MCU的EEPROM大小,如果未定义,默认512-1=511(0x1FF)
**6、BOOTLOADER_DATACRC,用于定义是否使用16位CRC校验,为避免代码过大,不建议使用,校验直接返回0xFFFF
**---------------------------------------------------------------------------------------------
**!!!!!!!!!!!!!!!!!!!!****
**烧录固件运行异常时删除原先所有编译好的文件,点击“生成”重新生成烧录文件
***********************************************************************************************/
#ifndef __ATME_BOOTLOADER__
#define __ATME_BOOTLOADER__
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <string.h>
/**********************************************************************************************
**各种MCU的寄存器定义---UCSRA
***********************************************************************************************/
#if ((defined __AVR_ATmega328P__) || (defined __AVR_ATmega328__) || (defined __AVR_ATmega8__)\
|| (defined __AVR_ATmega16__) || (defined __AVR_ATmega32__))
typedef struct {
uint8_t USART_MPCM:1; //多处理器通信模式
uint8_t USART_U2X:1; //是否倍速发送,仅对异步模式有效,同步模式清零
uint8_t USART_PE:1; //奇偶校验错误
uint8_t USART_DOR:1; //数据溢出
uint8_t USART_FE:1; //帧错误
uint8_t USART_UDRE:1; //USART数据寄存器空,1状态说明缓冲器为空
uint8_t USART_TXC:1; //USART发送结束
uint8_t USART_RXC:1; //USART接收结束
}UCSRAREG_TypeDef;
#endif
/**********************************************************************************************
**各种MCU的寄存器定义---UCSRB
***********************************************************************************************/
#if ((defined __AVR_ATmega328P__) || (defined __AVR_ATmega328__) || (defined __AVR_ATmega8__)\
|| (defined __AVR_ATmega16__) || (defined __AVR_ATmega32__))
typedef struct {
uint8_t USART_TXB8:1; //对9位串行帧进行操作时,TXB8是第9个数据位。写UDR之前首先要对它进行写操作
uint8_t USART_RXB8:1; //对9位串行帧进行操作时,RXB8是第9个数据位。读取UDR包含的低位数据之前首先要读取RXB8。
uint8_t USART_USZ2:1; //UCSZ2与UCSRC寄存器的UCSZ1:0结合在一起可以设置数据帧所包含的数据位数(字符长度),设置9位长度才会用到该位
uint8_t USART_TXEN:1; //发送使能
uint8_t USART_RXEN:1; //接收使能
uint8_t USART_UDRIE:1; //USART 数据寄存器空中断使能
uint8_t USART_TXCIE:1; //发送结束中断使能
uint8_t USART_RXCIE:1; //接收结束中断使能
}UCSRBREG_TypeDef;
#endif
/**********************************************************************************************
**各种MCU的寄存器定义---UCSRC
***********************************************************************************************/
#if ((defined __AVR_ATmega328P__) || (defined __AVR_ATmega328__))
typedef struct {
uint8_t USART_UCPIL:1; //时钟极性
uint8_t USART_UCSZ:2; //字符长度,
uint8_t USART_USBS:1; //停止位选择,0:1位,1:2位
uint8_t USART_UPM:2; //奇偶校验模式
uint8_t USART_UMSEL:2; //USART模式选择.0:已步模式,1:同步模式,3:SPI主机
}UCSRCREG_TypeDef;
#elif ((defined __AVR_ATmega8__) || (defined __AVR_ATmega16__) || (defined __AVR_ATmega32__))
typedef struct {
uint8_t USART_UCPIL:1; //时钟极性
uint8_t USART_UCSZ:2; //字符长度,
uint8_t USART_USBS:1; //停止位选择,0:1位,1:2位
uint8_t USART_UPM:2; //奇偶校验模式
uint8_t USART_UMSEL:1; //USART模式选择.0:已步模式,1:同步模式
uint8_t USART_URSEL:1; //寄存器选择
}UCSRCREG_TypeDef;
#endif
/**********************************************************************************************
**接收发送缓存结构及命令定义
***********************************************************************************************/
#define BOOTLOADER_CMD_ACK 0 //成功
#define BOOTLOADER_CMD_NACK 1 //失败
#define BOOTLOADER_CMD_QUERYFLASHSIZE 2 //查询MCU的Flash大小
#define BOOTLOADER_CMD_QUERYSPM_PAGESIZE 3 //查询MCU的SPM_PAGESIZE大小
#define BOOTLOADER_CMD_SETFLASHPAGE 4 //设置MCU定位到Flash的哪一页
#define BOOTLOADER_CMD_WRITEFLASH 5 //写入一页的Flash数据
#define BOOTLOADER_CMD_READFLASH 6 //读取一页的Flash数据
#define BOOTLOADER_CMD_QUERYRECVBUFSIZE 7 //查询一次可以发送数据长度(包含头信息)
#define BOOTLOADER_CMD_QUERYPROGSIZE 8 //获取用户区Flash大小
#define BOOTLOADER_CMD_EARSEFLASH 9 //擦除一页Flash
#define BOOTLOADER_ERROR_CRC 0xE0 //校验错误
#define BOOTLOADER_ERROR_DATAVOER 0xE1 //数据溢出
//----------------------------------------------------------------------------------------------
#define BOOTLOADER_RECVCMDFAILE 0 //没有收到数据
#define BOOTLOADER_RECVCMDBYTE 1 //收到一个数据
#define BOOTLOADER_RECVCMDEND 2 //接收到一个完成的命令
#define BOOTLOADER_RECVUPDATE 3 //进入到升级模式
//----------------------------------------------------------------------------------------------
typedef struct {
uint8_t Cmd; //命令类型
uint16_t CRC; //包含数据在内的校验值
uint16_t Page; //读写Flash时属于哪一页、读写EEPROM地址
uint16_t DataLen; //包含缓存长度
}BootLoaderDataHeader_TypeDef;
typedef struct {
BootLoaderDataHeader_TypeDef Header; //头信息
uint8_t Data[]; //数据(如果有)
}BootLoaderData_TypeDef;
#define BOOTLOADER_HEADERLEN 7 //BootLoaderDataHeader_TypeDef结构长度,如果该结构改变,需重新定义
/**********************************************************************************************
**各种MCU串口寄存器定义
***********************************************************************************************/
#if ((defined __AVR_ATmega328P__) || (defined __AVR_ATmega328__))
#define UCSRAREG UCSR0A //UCSRA寄存器
#define UCSRBREG UCSR0B //UCSRB寄存器
#define UCSRCREG UCSR0C //UCSRC寄存器
#define UBRRLREG UBRR0L //UBRRL寄存器
#define UBRRHREG UBRR0H //UBRRH寄存器
#define UDRREG UDR0 //UDR寄存器
#define UDREBIT UDRE0 //发送缓存为空标志位
#define RXCBIT RXC0 //接收完成标志位
#define RXENBIT RXEN0 //接收使能
#define TXENBIT TXEN0 //发射使能
#define RXCIEBIT RXCIE0 //接收中断使能
#define UCSZ0BIT UCSZ00 //UCSZ0位
#define UCSZ1BIT UCSZ01 //UCSZ1位
#elif ((defined __AVR_ATmega8__) || (defined __AVR_ATmega16__) || (defined __AVR_ATmega32__))
#define UCSRAREG UCSRA //UCSRA寄存器
#define UCSRBREG UCSRB //UCSRB寄存器
#define UCSRCREG UCSRC //UCSRC寄存器
#define UBRRLREG UBRRL //UBRRL寄存器
#define UBRRHREG UBRRH //UBRRH寄存器
#define UDRREG UDR //UDR寄存器
#define UDREBIT UDRE //发送缓存为空标志位
#define RXCBIT RXC //接收完成标志位
#define RXENBIT RXEN //接收使能
#define TXENBIT TXEN //发射使能
#define RXCIEBIT RXCIE //接收中断使能
#define UCSZ0BIT UCSZ0 //UCSZ0位
#define UCSZ1BIT UCSZ1 //UCSZ1位
#endif
//定义串口波特率
#ifndef BOOTLOADER_BAUDRATE
#define BOOTLOADER_BAUDRATE 9600 //串口速率
#endif
//定义MCU的Flash结束地址(大小)
#ifndef BOOTLOADER_FLASHEND
#define BOOTLOADER_FLASHEND 0x7FFF //默认8K
#endif
//定义MCU的EEPROM结束地址(大小)
#ifndef BOOTLOADER_EEPROMEND
#define BOOTLOADER_EEPROMEND 0x1FF //默认512
#endif
//定义Boot区起始地址,如果未定义,默认大小为512个字,起始地址为0x1C00(字节)
#ifndef BOOTLOADER_BOOTSTART //Boot区起始位置,取决于熔丝位设置
#define BOOTLOADER_BOOTSTART 0x7C00
#endif
//用户程序起始地
#define BOOTLOADER_PROGSTART 0x0000 //用户程序起始地址
//定义串口接收缓冲大小
#ifndef BOOTLOADER_RECVBUFSIZE
#define BOOTLOADER_RECVBUFSIZE 128
#endif
//判断实际应该需要多少接收缓存
//接收缓冲区大小不能小于 SPM_PAGESIZE
#if (BOOTLOADER_RECVBUFSIZE < SPM_PAGESIZE)
#define RECV_BUFSIZE SPM_PAGESIZE+BOOTLOADER_HEADERLEN
#else
#define RECV_BUFSIZE BOOTLOADER_RECVBUFSIZE+BOOTLOADER_HEADERLEN
#endif
//定义进入升级时通过串口返回的数据
const uint8_t BOOTLOADER_DATA_UPDATE[]={BOOTLOADER_CMD_ACK,0xFF,0xFF,BOOTLOADER_RECVUPDATE,0x00,0x07,0x00};
//接收缓冲区
uint8_t __recv_buf[RECV_BUFSIZE];
//当前缓存接收所在位置
uint16_t __recv_addr=0;
//计算波特率寄存器
#define BAUDREG ((uint16_t)((F_CPU * 10) / (16UL * BOOTLOADER_BAUDRATE) - 5) / 10)
//#define bootloader_recv_wait() {while (!(UCSRAREG&(1<<RXCBIT)));} //等待接收
#define bootloader_recv_wait() {while (!((volatile UCSRAREG_TypeDef *)&UCSRAREG)->USART_RXC);} //等待接收
//#define bootloader_send_wait() {while(!(UCSRAREG&(1<<UDREBIT)));} //等待发送
#define bootloader_send_wait() {while(!((volatile UCSRAREG_TypeDef *)&UCSRAREG)->USART_UDRE);} //等待发送
//数据校验
unsigned short bootloader_checksum(unsigned short *buffer,int sz)
{
#ifdef BOOTLOADER_DATACRC
unsigned short chksum;
unsigned long sum = 0; //sum可能会超过0xFFFF,所以要用4个字节
while(sz > 1)
{
sum += *buffer++;
sz -= 2;
}
if(sz == 1)
sum += *(unsigned char*)buffer;
sum = (sum>>16) + (sum & 0xffff);
sum += (sum>>16);
chksum = ~sum;
return (chksum);
#else
return 0xFFFF;
#endif
}
/**********************************************************************************************
**BootLoader_Init:串口初始化
***********************************************************************************************/
void BootLoader_Init()
{
void _init3()
{
//关闭串口
UCSRCREG=0;
UCSRBREG=0;
//设置为8位接收模式
((volatile UCSRCREG_TypeDef *)&UCSRCREG)->USART_UCSZ=3;
#if defined (__AVR_ATmega8__)
UCSRCREG |=BIT(URSEL);
#endif
//计算波特率
UBRRHREG=BAUDREG/256;
UBRRLREG=BAUDREG%256;
UCSRAREG=0;
//启动发送与接收
((volatile UCSRBREG_TypeDef *)&UCSRBREG)->USART_RXEN=1;
((volatile UCSRBREG_TypeDef *)&UCSRBREG)->USART_TXEN=1;
}
__recv_addr=0;
_init3();
}
/**********************************************************************************************
**BootLoader_Recv:串口接收(阻塞模式)
***********************************************************************************************/
uint8_t BootLoader_Recv(uint8_t *data,uint32_t timeout)
{
while (!(UCSRAREG & (1<<RXCBIT)))
// while (!(((volatile UCSRAREG_TypeDef *)&UCSRAREG)->USART_RXC))
{
if (0==timeout) return 0;
timeout--;
_delay_us(1);
}
*data=UDRREG;
return 1;
}
/**********************************************************************************************
**BootLoader_Send:串口发送(阻塞模式)
***********************************************************************************************/
void BootLoader_Send(const uint8_t data)
{
bootloader_send_wait(); //等待发送准备完备
UDRREG=data;
}
/**********************************************************************************************
**BootLoader_SendBytes:串口发送(阻塞模式)
***********************************************************************************************/
void BootLoader_SendBytes(const uint8_t *data,uint16_t datalen)
{
uint16_t i;
for (i=0;i<datalen;i++) BootLoader_Send(*data++);
}
/**********************************************************************************************
**BootLoader_RecvCmd:串口接收命令(阻塞模式)
***********************************************************************************************/
uint8_t BootLoader_RecvCmd(uint32_t timeout)
{
if (!BootLoader_Recv(&__recv_buf[__recv_addr],timeout))
{
__recv_addr=0;
return BOOTLOADER_RECVCMDFAILE;
}
BootLoaderDataHeader_TypeDef * cmd=(BootLoaderDataHeader_TypeDef *)__recv_buf;
if (__recv_addr==3)
{
if (0x55555555==*((uint32_t *)cmd)) //收到连续4个字符“U”,进入升级模式
{
__recv_addr=0;
BootLoader_SendBytes(BOOTLOADER_DATA_UPDATE,BOOTLOADER_HEADERLEN); //发送回应
return BOOTLOADER_RECVUPDATE;
}
}
else if (__recv_addr>=(BOOTLOADER_HEADERLEN-1))
{
if (__recv_addr>=(cmd->DataLen-1))
{
__recv_addr=0;
return BOOTLOADER_RECVCMDEND;
}
else if (cmd->DataLen>RECV_BUFSIZE)
{
__recv_addr=0;
return BOOTLOADER_RECVCMDFAILE;
}
}
__recv_addr++;
return BOOTLOADER_RECVCMDBYTE;
}
/**********************************************************************************************
**BootLoader_DoCMD:处理命令
***********************************************************************************************/
void BootLoader_DoCMD()
{
//校验数据是否正确
BootLoaderData_TypeDef *cmd=(BootLoaderData_TypeDef *)__recv_buf;
uint16_t page;
uint32_t i,addr;
uint8_t *d=(uint8_t *)cmd;
d+=BOOTLOADER_HEADERLEN;
uint16_t crc=cmd->Header.CRC;
cmd->Header.CRC=0;
if (bootloader_checksum((unsigned short *)cmd,cmd->Header.DataLen)!=crc)
{
return;
}
switch(cmd->Header.Cmd)
{
case BOOTLOADER_CMD_QUERYSPM_PAGESIZE: //查询Flash页的缓存大小
cmd->Header.Cmd=BOOTLOADER_CMD_ACK;
cmd->Header.CRC=0;
cmd->Header.Page=SPM_PAGESIZE;
cmd->Header.DataLen=BOOTLOADER_HEADERLEN;
cmd->Header.CRC=bootloader_checksum((unsigned short *)cmd,cmd->Header.DataLen);
BootLoader_SendBytes(__recv_buf,cmd->Header.DataLen);
break;
case BOOTLOADER_CMD_QUERYPROGSIZE: //查询用户代码区域大小
cmd->Header.Cmd=BOOTLOADER_CMD_ACK;
cmd->Header.CRC=0;
cmd->Header.Page=BOOTLOADER_BOOTSTART;
cmd->Header.DataLen=BOOTLOADER_HEADERLEN;
cmd->Header.CRC=bootloader_checksum((unsigned short *)cmd,cmd->Header.DataLen);
BootLoader_SendBytes(__recv_buf,cmd->Header.DataLen);
break;
case BOOTLOADER_CMD_READFLASH: //读取一页的Flash数据
page=cmd->Header.Page;
addr=page*SPM_PAGESIZE;
boot_rww_enable(); //允许读用户程序
for (i=addr;i<(addr+SPM_PAGESIZE);i++)
{
*d=pgm_read_byte(i);
d++;
}
cmd->Header.Cmd=BOOTLOADER_CMD_ACK;
cmd->Header.CRC=0;
cmd->Header.Page=BOOTLOADER_CMD_READFLASH;
cmd->Header.DataLen=BOOTLOADER_HEADERLEN+SPM_PAGESIZE;
cmd->Header.CRC=bootloader_checksum((unsigned short *)cmd,cmd->Header.DataLen);
BootLoader_SendBytes(__recv_buf,cmd->Header.DataLen);
break;
case BOOTLOADER_CMD_QUERYFLASHSIZE: //查询整个MCU的Flash大小
cmd->Header.Cmd=BOOTLOADER_CMD_ACK;
cmd->Header.CRC=0;
cmd->Header.Page=BOOTLOADER_FLASHEND+1;
cmd->Header.DataLen=BOOTLOADER_HEADERLEN;
cmd->Header.CRC=bootloader_checksum((unsigned short *)cmd,cmd->Header.DataLen);
BootLoader_SendBytes(__recv_buf,cmd->Header.DataLen);
break;
case BOOTLOADER_CMD_WRITEFLASH: //写Flash
page=cmd->Header.Page;
addr=page*SPM_PAGESIZE;
boot_page_erase(addr); //擦除一个Flash页
boot_spm_busy_wait();
for(i = 0; i < SPM_PAGESIZE; i += 2) //数据填入Flash缓冲页
{
boot_page_fill(i, d | (d[i + 1] << 8));
}
boot_page_write(addr); //将缓冲页数据写入一个Flash页
boot_spm_busy_wait(); //等待页编程完成
cmd->Header.Cmd=BOOTLOADER_CMD_ACK;
cmd->Header.CRC=0;
cmd->Header.Page=BOOTLOADER_CMD_WRITEFLASH;
cmd->Header.DataLen=BOOTLOADER_HEADERLEN;
cmd->Header.CRC=bootloader_checksum((unsigned short *)cmd,cmd->Header.DataLen);
BootLoader_SendBytes(__recv_buf,cmd->Header.DataLen);
break;
case BOOTLOADER_CMD_EARSEFLASH: //擦除一页Flash
page=cmd->Header.Page;
addr=page*SPM_PAGESIZE;
boot_page_erase(addr); //擦除一个Flash页
boot_spm_busy_wait();
cmd->Header.Cmd=BOOTLOADER_CMD_ACK;
cmd->Header.CRC=0;
cmd->Header.Page=BOOTLOADER_CMD_EARSEFLASH;
cmd->Header.DataLen=BOOTLOADER_HEADERLEN;
cmd->Header.CRC=bootloader_checksum((unsigned short *)cmd,cmd->Header.DataLen);
BootLoader_SendBytes(__recv_buf,cmd->Header.DataLen);
break;
}
}
/**********************************************************************************************
**BootLoader_App:跳转到指定地址执行指令
***********************************************************************************************/
void BootLoader_App(uint16_t addr)
{
boot_rww_enable(); //允许用户程序区读写
(*((void(*)(void))addr))(); //跳转,这样比'jmp 0'节省空间
}
////////////////////////////////////////////////////////////////////////////////////////////////
#endif
2、main程序
/*
* Test.c
*
* Created: 2022/3/5 15:19:42
* Author : Administrator
*/
//#define F_CPU 8000000UL
#define F_CPU 7372800UL
#define BOOTLOADER_BAUDRATE 115200
#include <avr/io.h>
#include <Userlib/Atmel_BootLoader.h>
#include <util/delay.h>
int main(void)
{
/* Replace with your application code */
BootLoader_Init();
uint8_t data;
uint8_t state=0;
uint8_t i=0;
while (1)
{
data=BootLoader_RecvCmd(5000); //接收命令
switch(data)
{
case BOOTLOADER_RECVCMDEND: //接收到一个完整的命令
BootLoader_DoCMD();
break;
case BOOTLOADER_RECVUPDATE: //进入到升级模式
state=1;
break;
default:
if (i++<30) continue;
}
if (!state) //跳到用户程序执行
{
BootLoader_App(BOOTLOADER_PROGSTART);
}
}
}
3、编译后的BootLoader固件大小对于ATMEGA8芯片是900多个字节,对于ATMEGA328是1024个字节,正好1K,固件预留对M16和M32支持,其他需自己补充,MCU的BootLoader需用USBSAP程序事先写入MCU才可以使用USART串口下载功能,AVR芯片不像STM32自带BootLoader。
4、上位机,大飞环境编写。串口芯片使用的是CP2102,DTR引脚接MCU的RESET引脚时(仿正点原子开发板接法,不过是只接DTR控制重启),模式选择DTR低电平复位,下载程序和FlyMCU一样一键式下载并重启,如果DTR没有接RESET脚,需手动拉低RESET会进入下载模式。大致工作原理是MCU在开始工作时,大约有150ms时间等待握手信息,就是如果接收到"UUUU"这四个字符,MCU进入BootLoader模式,这时候通过上位机可以把用户区程序代码写入MCU,如果超过150ms时间未接收到握手信息,MCU进入用户指定的程序区执行。
读出的FLASH内容
|
|