2017-05-28 20:39:01 u010315448 阅读数 292
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3411 人正在学习 去看看 朱有鹏



STM32系列单片机包含3种BOOT设置,对于不经常使用它的工程师,难免需要查阅相关BOOT设置的资料,但是大多数资料介绍拖沓冗长,今天小编总结提炼了一下,给出一张一目了然的BOOT设置表。





2020-01-11 15:08:46 u012577474 阅读数 46
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3411 人正在学习 去看看 朱有鹏

通过串口烧入hex到pic单片机(pic已经运行boot程序)

down_hex.py 源码

#coding:utf-8
import serial
import pic_hex_pars
import time
from threading import Timer
#串口定时接收
class PicSerial():
    def __init__(self):
        self.t = Timer(0.02, self.func)
        self.t.start()
        self.ser=serial.Serial("com1", 57600, timeout=0.5)
        self.rx_data=[0,0,0,0,0,0,0,0]

    def func(self):

        try:
            num = self.ser.inWaiting()
        except:
            t.ser.close()
            return None
        if num > 0:
            self.rx_data = self.ser.read(num)
            num = len(self.rx_data)
            # print(num)
            # print(self.rx_data[0],self.rx_data[1])
            # print("com2接收:",rx_data.decode('iso-8859-1'))
            # print(self.rx_data)
        self.t.cancel()
        self.t = Timer(0.02, self.func)
        self.t.start()
        
PROGRAM_DEVICE_CMD=[0xD0,0XF2,0X05,0X00,0X20,0X00,0X00,0X36,0X37,0X38]  ### 烧写程序命令,PROGRAM_DEVICE_CMD【00 20 00 00 表示起始地址0x00002000(左边为低字节)】
hex_data,start_addr=pic_hex_pars.hex_pars()  #读取代码数据
print("编程地址:", hex(start_addr))
print("转换后的代码行数:",len(hex_data))

# 从hex中获取代码起始地址
addr_l=start_addr%256
addr_h=start_addr//256
PROGRAM_DEVICE_CMD[3] = addr_l 
PROGRAM_DEVICE_CMD[4] = addr_h

pic_ser = PicSerial() #打开串口
pic_ser.ser.write(PROGRAM_DEVICE_CMD) #开始烧写程序命令,指定了烧写起始地址
time.sleep(1)#等待命令完成
for i in range(0, len(hex_data)):
    data=bytearray.fromhex(hex_data[i])
    print("第",i,"帧下载中:", hex_data[i],"\n\r")
    pic_ser.ser.write(data)  # 写8字节程序数据到单片机
    #等待一帧数据下载完成
    while  pic_ser.rx_data[0]!=54 or pic_ser.rx_data[1]!=54:
        0
    # print(t.rx_data[0], t.rx_data[1], t.rx_data[2])
    print("第",i,"帧下载完成!块填充计数监控值:", pic_ser.rx_data[2],"\n\r") #打印提示信息
    pic_ser.rx_data =[0,0,0,0,0,0,0,0]

print("hex发送完成")

pic_hex_pars.py 源码

#coding:utf-8
import binascii
import os
import linecache
#检查校应码
def calc_checksum( data):
    checksum = 0
    result=0
    # print("校应数据长度:",len(data))
    for i in range(0, len(data),2):
        checksum = ((int(data[i:i +2], 16))+checksum )
    # print(checksum)
    result=(256-checksum)&0xff
    # print(result)
    return result

def hex_pars(path="./led.hex"):
    DOWN_DATA_CMD = [0xD0, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF]  ###写ROM指令:【D0 F1 FF FF FF FF FF FF FF FF】, FF FF FF FF FF FF FF FF 表示要写入的数据数据(左边为低
    hex_result = [] #存储解析结果
    start_addr=0
    linenum=0
    data_line_num=0
    file_path = path
    fptr = open(file_path, "r")
    while True:
        line = fptr.readline().upper()  ###读取一行文本,并且小写字母转大写字母
        line = line.strip()  ##删除头尾空格和换行符
        if len(line) != 0:
                 # print(line)
                 length = int(line[1:3], 16)
                 addr =  int(line[3:7], 16)
                 rtype = int(line[7:9], 16)
                 checksum = calc_checksum(line[1:-2])
                 # print("校应和:", hex(checksum))
                 if checksum == int(line[-2:], 16):
                     # print("校验正确",linenum)
                     # 校验正确。
                     if rtype == 0x00 and addr > 0 and length > 0:
                         data_line_num+=1
                         #取得hex代码的起始地址
                         if  data_line_num ==1 :
                               start_addr=addr
                         data_len = length
                         send_times = (length // 8) if length % 8 == 0 else (length // 8 + 1)
                         # print("大循环次数", send_times)
                         #8字节为单位遍历一行数据
                         for j in range(0, send_times):
                             #遍历16进制字符串转字节数组,提取一行中的8字节数据
                             for i in range(0, 8):
                                 if data_len != 0:
                                     data_len = data_len - 1
                                     # print(i)
                                     DOWN_DATA_CMD[2 + i] = int(line[(9 + i * 2 + j * 16):(11 + i * 2 + j * 16)], 16)
                                     # 提取数据到二维数组
                             # 追加(一个编程指令)到二维数组
                             hex_result.append(bytearray(DOWN_DATA_CMD).hex())
                             # print("提取8字节数据:", bytearray(DOWN_DATA_CMD).hex())
                     else:
                         print("数据不符合要求,行号:", linenum)
                 else:
                     print("校应失败,行号:", linenum)
        else:
            block_num=len(hex_result)//8   # 完整块数量(64字节等于一个块)
            debris_num=len(hex_result)%8    # 剩余行数(一个行等于8字节,需要补齐到64字节)
            # print("hex转换完成,完整块数量:",block_num,"剩余行数:",debris_num)
            lack_line_number=8-debris_num  #计算需要补齐多少行
            #补齐缺少行
            for j in range(0, lack_line_number):
                hex_result.append(bytearray([0xD0, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]).hex())
            for j in range(0, len(hex_result)):
                print(hex_result[j])
            print("转换后的代码行数:", len(hex_result))
            break
        linenum += 1
    return hex_result,start_addr

运行示例(如下图):
在这里插入图片描述

一帧发送8字节数据。PIC_BOOT 需要接收64字节才写入ROM。

led.hex文件内容

:040000002CEF10F0E1
:10200000000E026E000E016E400E015C1F0E0258A3
:10201000D8B01200000E046E000E036E045007E1EB
:10202000280E035CD8B003D0034A042AF7D7014A2C
:10203000022AEAD7000E826E000E8B6E000E946E9E
:10204000010E8B6E00EC10F0000E8B6E00EC10F0A9
:0E205000F7D700F000F000F000011AEF10F0DA
:020000040020DA
:08000000FFFFFFFFFFFFFFFF00
:020000040030CA
:0E00000011087F7CFF0F81FF0FC00FE00F4043
:00000001FF

2019-05-18 16:47:44 tiantangmoke 阅读数 511
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3411 人正在学习 去看看 朱有鹏

工程项目上有时候需要为设备更新应用程序,如果不预留接口升级的话就需要拆除外壳,太麻烦了。一般会留下升级程序的接口,在需要升级设备的时候,给一个信号使程序进入BOOT区运行,然后进行在线升级。

我们一般把单片机flash空间划分为3块。
1、BOOT: 储存进行在线升级的驱动程序
2、APP : 储存应用程序
3、配置信息 : 储存需要保存的应用数据

以下是BOOT区以及APP区的配置方法。

1、设置BOOT程序的代码占用空间
根据BOOT功能来划分,这里我举个例子分配0x2000的空间给BOOT
在这里插入图片描述

2、BOOT区启动后要对APP区的代码进行校验
根据校验的结果来决定是停留在BOOT区等待,还是跳转APP程序运行。
可以把APP程序的校验和存在APP程序区的末尾,在烧录程序的时候烧录进去。在BOOT刚启动的时候,计算APP区代码的校验和进行对比。如果相同,就跳转到APP区,如果不同,就执行BOOT区接下来的程序进行远程升级。

3、APP区如何进入BOOT区
1、更改APP区校验信息
上面我们说到BOOT区启动的时候会校验APP区的代码来决定是否运行APP,所以APP区进入BOOT区之前,要破坏APP区的校验信息。在APP区进行操作,把校验信息改写,取反或者加1都是不错的选择。在BOOT区无法进行远程升级的时候,可以更改校验位回到APP区运行。

2、重启进入BOOT区
在更改APP区校验信息之后,需要重启单片机运行。
STM32的重启代码是:

NVIC_SystemReset();	

4、BOOT区在线升级

在确定了需要在线升级之后,就可以用预留的接口,来接收数据进行在线升级了。
我们将接收到的数据,写到APP的flash区内(不要覆盖了BOOT区和配置信息区),数据接收完后,还需要计算APP区内的校验信息并写入。然后重启BOOT区,这时校验信息正确,进入APP区。

5、BOOT区跳转到APP区流程
1、关闭一些可能会干扰到跳转的功能
STM32可以用这个来实现

EXTI_DeInit();
SYSCFG_DeInit();
RCC_DeInit();

2、检查栈顶地址是否合法.
APP_ROM_ADDR 为APP区的起始位置,比如0x08002000

 if (((*(uint32_t*)APP_ROM_ADDR) & 0x2FFE0000 ) == 0x20000000)

计算如果不为0x20000000,那就不进行跳转

3、初始化用户程序的堆栈指针

__set_MSP(*(__IO uint32_t*) APP_ROM_BASE_ADDR);

4、执行跳转
首先要获取跳转地址。
APP区的地址存在 APP_ROM_ADDR+4 (例如0x08002004)的位置,且有4个字节,
我们可以这样获取跳转地址以及执行跳转。

typedef void (*pFunction)(void);//定义了一个新类型,该类型是一个函数指针,
pFunction Jump_To_Application;  //定义程序地址指针
JumpAddress = *(__IO uint32_t*) (APP_ROM_ADDR + 4); 
Jump_To_Application = (pFunction)JumpAddress;
Jump_To_Application();

6、写APP程序所需注意的几个地方

程序并不是进入APP区就可以直接运行了,APP程序需要做一些处理才可以运行。

1、要划分好APP区的存放地址
比如我们设定BOOT区程序大小为0x2000,那么APP区程序就要从0X2000
在这里插入图片描述
2、需要重映射中断向量

__IO uint32_t VectorTable[48] __attribute__((at(0x20000000)));
void iRemapIrqVector(void)
{
    uint8_t i = 0;
    for(i = 0; i < 48; i++)
        VectorTable[i] = *(__IO uint32_t*)(APP_ROM_ADDR + (i<<2)); //中断向量是一个指针,每个占4个字节
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //这个一定要有
    SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); //设置为RAM启动模式
}

要分配RAM

在这里插入图片描述

2019-11-08 09:30:53 qq_36958104 阅读数 711
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3411 人正在学习 去看看 朱有鹏

 

 

 

如图所示,一般情况下如果我们想用用串口下载代码,则必须配置BOOT0为 1, BOOT1为 0,而如果想让 STM32 一按复位键就开始跑代码,则需要配置 BOOT0 为 0,BOOT1 随便设,请参考正点原子的一键下载电路,这个电路比较好,不用繁琐的切换boot电平。

原理图:

启动方式:

1,第一种启动方式是最常用的用户FLASH启动。默认启动方式。

2, 第二种启动方式是STM32内嵌的SRAM启动。该模式用于调试 第三种启动方式是系统存储器启动方式。STM32中自带的BootLoader就是在这种启动方式中,如果出现程序硬件错误的话可以切换BOOT0/1到该模式下重新烧写Flash即可恢复正常。

 

首先对BOOT0与BOOT1如何连接及连接方式介绍一下。

STM32 单片机的BOOT0 和BOOT1 应用原理
STM32 三种启动模式对应的存储介质均是芯片内置的,它们是:
1)主闪存存储器= 芯片内置的Flash。这也是正常工作模式
2)SRAM = 芯片内置的RAM 区,就是内存啦。
3)系统存储器= 芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说
的ISP 程序。这个区域的内容在芯片出厂后没有人能够修改或擦除,即它是一个ROM 区。
在每个STM32 的芯片上都有两个管脚BOOT0 和BOOT1,这两个管脚在芯片复位时的电平状态决定了芯片复

位后从哪个区域开始执行程序,见下表:

要注意的是,一般不使用内置SRAM 启动(BOOT1=1 BOOT0=1),因为SRAM 掉电后数据就丢失。多数情况下
SRAM 只是在调试时使用,也可以做其他一些用途。如做故障的局部诊断,写一段小程序加载到SRAM 中诊断板上的其他电
路,或用此方法读写板上的Flash 或EEPROM 等。还可以通过这种方法解除内部Flash 的读写保护,当然解除读写保护的同时
Flash 的内容也被自动清除,以防止恶意的软件拷贝。
一般BOOT0 和BOOT1 跳线都跳到0(地)。只是在ISP 下载的情况下,BOOT0=1,BOOT1=0 ,下载完成后,把
BOOT0 的跳线接回0,也即BOOT0=0,BOOT1=0 。

STM32F103 配置BOOT0 和BOOT1 直接接地还是经过电阻接地?
问:我的STM32F103RET6 挂了,原因是BOOT0 和BOOT1 直接接地导致的,很是奇怪,大家有没有遇到啊?
答:STM32 的硬件设计指导也是建议BOOT0、1 接电阻再接地的。
BOOT0 和BOOT1 分别通过10k 电阻接地,方便串口下载,下载器控制BOOT0 为高电平,下完后又将BOOT0
恢复为低,STM32 复位即可进入用户程序。平常上电直接进入用户程序。
boot0 接10k 电阻接地或者直接接地,都不会引起芯片损毁。

注意:如果JTAG 相关的引脚被用作普通IO,将无法下载(BOOT0 为低电平的情况下)。解决办法是用串口下载一次程序后再用JLINK 下载,或者将BOOT0 配置成高电平后下载。
 

2009-06-16 16:56:00 hearing1982 阅读数 672
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3411 人正在学习 去看看 朱有鹏

    因为片上ROM/FLASH不够,所以要这么一个boot code,可以让代码从外扩的ROM/FLASH启动.原来有它自己的代码,但是觉得不好,自己做了一个ISP,方便了很多.

    这里接触了NOR Flash的烧录,不过当时用的时候没有用CFI,后来看了U-Boot才知道代码如果写成那样会更通用,不过熟悉了Flash就可以了.

    当时遇到一个比较棘手的问题,就是速度,ROM/Flash的速度跟总线的配置是有关系的,总线可能可以比较高,但是ROM/Flash不支持,就不行.

    其它的,就是一般的单片机的程序了.

    稍微有点特殊的是高级的单片机(非普通51),一般I2C都是硬件做的,所以要仔细看看,因为I2C的标准也是比较细的,有些支持仲裁什么的,有些还是出错机制的处理等等,呵呵.细到实处不简单啊~~

 

    也是一笔经验啊.

没有更多推荐了,返回首页