STM32利用串口下载是最便宜的一种下载方式,但是利用串口下载需要操作STM32的RST、Boot0两个引脚,在此需要单独加一个下载辅助控制器(STC15F104)。 要无线下载STM32程序需知道在有线情况下串口如何下载程序,简要来说下载完整过程简要如下:
1.将Boot0拉高,接着复位STM32
2.PC端串口会不断发送0x7F,与STM32握手(STM32自适应波特率),STM32收到7F后,会返回 0x79 0xF1
3.串口下载软件识别返回的字节会会接着进一步发送其他控制指令,就这样PC端与STM端不断收发即完成了下载程序。
4.在下载完后,将Boot0置低,接着RST复位STM32.
无线下载时只需注意到两次复位即Boot0电平变化即可,所以简单来说只需:辅助单片机STC监测串口中有无连续5个以上的 0x7F(也可以多几个),将Boot0拉高,立即复位STM32,接着STC等待串口中无数据后再将Boot0拉低,接着复位STM32即可。
无线串口利用ESP8266 的透传模式,即A端发什么,B端就收什么(A、B两者能互发),由于ESP8266断电后会自动关闭透传模式,故每次上电时需要STC单片机初始化下ESP8266为透传模式。无线下载机构如下(PC端也需上电初始化为透传模式):
STC主要电路:
PC端串口下载软件:
STC程序部分:
// STC15F104 程序 for STM32 Download
// STM32下载串口为偶校验
// 模拟串口设置为115200bps 偶校验
// ESP8266wifi为STA模式
// PC端8266为AP模式,需首次设置AT+CWSAP="STM32Download","Download01",7,3 掉电仍保存
//PC端的8266需AT+CIPSTART="UDP","192.168.4.2",40000,40000,0 设置目标的发送地址,不一定时192.168.4.2,需先看下对方IP,一般无第三者加入此地址不会变的
// PC端再发送: AT+CIPMODE=1 AT+CIPSEND 两个指令即完成PC端的设置
// 从站端,下载目标端程序
// 目标开发板Boot0需下拉,否则初始开机时程序非正常启动,且无法正常发送初始指令给8266模块
#include "reg51.h"
#include"intrins.h"
#define BAUD 0xFFA0 // 115200 @33.1776Mhz,下载时需注意勾选此频率
//0xFE80 // 9600bps @ 11.0592MHz
sfr AUXR = 0x8E; //辅助寄存器,设定T0工作速度,0--1/12,1--1T
sbit RXB = P3^0; //define UART TX/RX port
sbit TXB = P3^1;
sbit RST = P3^4; //复位 低电平有效
sbit Boot0 =P3^5; //启动选择,高 下载程序,低 正常运行程序
//sbit SW = P3^3; //开关,判断是否需初始化wifi
typedef bit BOOL;
typedef unsigned char BYTE;
typedef unsigned int WORD;
BYTE TBUF,RBUF;//发送/接收 寄存器
BYTE TDAT,RDAT;//发送/接收 数据
BYTE TCNT,RCNT;//发送/接收 计数器
BYTE TBIT,RBIT;//发送/接收 位数
BOOL TING,RING;//发送/接收 开始标志
BOOL TEND,REND; //发送/接收 结束标志
BYTE Parity; //计算校验位
void UART_INIT();
void UART_SENDBYTE(BYTE);
void UART_SENDSTRING( BYTE *str);
BYTE t, r;
BYTE buf[16];
void Delay1ms() //@33.1776Mhz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 17;
j = 150;
do
{
while (--j);
} while (--i);
}
void Delayms(unsigned char i)
{
while(i--)
{
Delay1ms();
}
}
void main()
{
BYTE Download=0;
BYTE Dend_CNT=0;//下载结束判断计数
TMOD = 0x00; //T0 16bit reaload Mode
AUXR = 0x80; //T0--1T
TL0 = BAUD;
TH0 = BAUD>>8;
TR0 = 1; //T0计时开始
ET0 = 1; //T0开中断
PT0 = 1; //T0高优先级
EA = 1;
RST=1;Boot0=0; //IO初始化
UART_INIT();
//while(1)
//{
// TEND = 0; TBUF = 0x7F; TING = 1; //发送0x7F
// TEND = 0; TBUF = 0x7E; TING = 1; //发送0x7F
// TEND = 0; TBUF = 0x7D; TING = 1; //发送0x7F
// TEND = 0; TBUF = 0x7C; TING = 1; //发送0x7F
// Delayms(50);
//if( REND)
//{ REND = 0;
// TEND = 0; TBUF = RBUF; TING = 1; //发送0x7F
// while(!TEND);}
//}
//AT+UART=115200,8,1,2,0 偶校验
//如果一组给定数据位中 1 的个数是奇数,那么偶校验位就置为 1,从而使得总的 1 的个数是偶数。如果给定一组数据位中 1 的个数是偶数,那么奇校验位就置为 1,使得总的 1 的个数是奇数。偶校验实际上是循环冗余校验的一个特例,通过多项式 x + 1 得到 1 位 CRC
//将8266建立透传:CIPMUX=0 单链接模式 CIPMODE=1 透传模式,8266设置透传后重启后为0,有时AT指令不能被识别,需发送“+++”终止透传
// CWMODE=2 CWSAP="STM32Download","Download01",7,3 网络建立只需初次设定即可,在此不再上电设置
//ATE0
//AT+CIPSTART="UDP","192.168.4.2",40000,40000,0
//AT+CIPSTART="UDP","192.168.4.1",40000,40000,0
//AT+CIPMODE=1
//AT+CIPSEND
Delayms(250); Delayms(250); Delayms(250); Delayms(250);
Delayms(250); Delayms(250); Delayms(250); Delayms(250);
Delayms(250); Delayms(250); Delayms(250); Delayms(250);
Delayms(250); Delayms(250); Delayms(250); Delayms(250);
Delayms(250); Delayms(250); Delayms(250); Delayms(250);
UART_SENDSTRING("AT+CIPSTART=\"UDP\",\"192.168.4.1\",40000,40000,0");//UART_SENDBYTE(0x0D);UART_SENDBYTE(0x0A);//换行符
Delayms(30);
UART_SENDSTRING("AT+CIPMODE=1");//UART_SENDBYTE(0x0D);UART_SENDBYTE(0x0A);//换行符
Delayms(30);
UART_SENDSTRING("AT+CIPSEND");//UART_SENDBYTE(0x0D);UART_SENDBYTE(0x0A);//换行符
while (1)
{
//检测串口发送连续的 0x7F 当有连续收到5个时则开启下载:置Boot0为1,延时10ms,100ms RST复位低电平
//下载完:等待100ms无收到数据则认为下载完毕,Boot0=0,RST复位MCU
if (REND)
{
REND = 0;
Dend_CNT=0;
if( RBUF == 0x7F && !Download ) {
r++;
if(r>5) //大于5个 0x7F 开启下载
{
Download=1;
Boot0=1;Delayms(10); //置Boot0为1
RST=0;Delayms(100);RST=1; //复位延时100ms
}
}
else r=0;
// buf[r++ & 0x0f] = RBUF;
}
else if( Download ) //无数据接收 则循环等待200ms,再无数据则判断下载完成
{
Delayms(10);
Dend_CNT++;
if(Dend_CNT >80) {Download=0; Dend_CNT=0;
Boot0=0;
RST=0;Delayms(100);RST=1; //复位延时100ms
}
}
/* if (TEND)
{
if (t != r)
{
TEND = 0;
if( buf[t++ & 0x0f] ==0x7F ) TBUF =0x91;
else TBUF = buf[(t-1) & 0x0f];
//TBUF = buf[t++ & 0x0f];
TING = 1;
}
}
*/
}
/* while(1)
{
if(TEND)
{
TEND = 0;
TBUF = 0xba;
TING = 1;
}
}*/
}
//-----------------------------------------
//Timer interrupt routine for UART
void tm0() interrupt 1 using 1
{
if (RING)
{
if (--RCNT == 0)
{
RCNT = 3; //reset send baudrate counter
if (--RBIT == 0)
{
RBUF = RDAT; //save the data to RBUF
RING = 0;
REND = 1;
}
else
{
if( RBIT != 1 )
{
RDAT >>= 1;
if(RXB)RDAT|=0x80;
}
else{//检查校验位
}
}
}
}
else if(!RXB)
{
RING = 1;
RCNT = 4;
RBIT = 10;
}
if (--TCNT == 0)
{
TCNT = 3; //reset send baudrate counter
if (TING) //judge whether sending
{
if (TBIT == 0)
{
TXB = 0; //send start bit
TDAT = TBUF; //load data from TBUF to TDAT
TBIT = 10; //initial send bit number (8 data bits + 1 stop bit+1 parity bit)
Parity = 0;
}
else
{
TDAT >>= 1; //shift data to CY
if (--TBIT == 0)
{
TXB = 1;
TING = 0; //stop send
TEND = 1; //set send completed flag
}
else
{
TXB = CY; //write CY to TX port
Parity += (CY&0xFF); //校验位
if( TBIT ==2 ) TDAT = Parity;
}
}
}
}
}
//--------------------------------------
//initial UART module variable
void UART_INIT()
{
TING = 0;
RING = 0;
TEND = 1;
REND = 0;
TCNT = 0;
RCNT = 0;
Parity = 0;
}
void UART_SENDBYTE(BYTE Ch)
{
while(!TEND); //等待发送完
TEND = 0; TBUF = Ch; TING = 1; //准备数据
}
void UART_SENDSTRING( BYTE *str)
{
while(*str !=0)
{UART_SENDBYTE(*str);
str++;}
UART_SENDBYTE(0x0D);UART_SENDBYTE(0x0A);
}