0%

NUCLEO板子对外部stm32进行编程下载

环境

  1. NUCLEO-F401RE板子
  2. stm32f103rb核心板(源地)
  3. 软件环境见STM32-Dev-Mac

步骤

  1. 硬件连线,拔掉NUCLEO板子上的CN2两个跳线帽,然后连接CN4到编程的目标板(stm32f103rb核心板),电源不连单独供电,连接图如下

    NUCLEO 从上(SWD)到下(CN4) 目标板 描述
    VDD_TARGET 1 vdd电源
    SWCLK 2 SWDCLK 时钟
    GND 3 GND
    SWDIO 4 SWDIO 数据输入/输出
    NRST 5 目标MCU的RESET
    SWO 6 保留

    stm32

  2. 打开STM32CubeMX软件,新建个工程,这里直接选STM32F103RBTx的NUCLEO套版,这里直接在这个套版上基础修改

  3. 设置led输出引脚:在pinout引脚设置界面去掉PA5,修改PC13为GPIO_Output输出,并打上LED用户标签

    QQ20180722-105903

  4. 修改时钟引脚设置,由于自己的板子用的高速时钟(HSE)和低速时钟(LSE)都用的外部时钟,所以这里切换时钟模式都为外部(Crystal)

    QQ20180722-105957

  5. 修改时钟配置,修改晶振频率和自己板子一致,并且换成外部,然后设置倍数,如果倍数设置高于频率,会提示红报错,选择可用最高倍数即可,该板子只能选X9

    QQ20180722-110120

  6. 最后生成makerfile类型工程

  7. 修改Makefile文件,C_SOURCES去重,BINPATH设置路径/usr/local/bin/

  8. main.c添加led流水灯代码,引脚的名称注意用刚刚设置的标签名字

    1
    2
    3
    4
    5
    6
    7
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
    HAL_Delay(500);
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
    HAL_Delay(1000);
  9. 编译make下载st-flash write ./build/<项目名>.bin 0x8000000

使用CLion进行项目编译运行

  1. build->OpenOCD Support->board config file:st_nucleo_f103rb.cfg
  2. 编译运行手动按reset

注意:使用openocd时,NRST一定要连!

但是:没有线引出来无法连接,所以运行时立马手动按reset键也可以成功。

参考

STM32Cube工具学习笔记(一)Cube配置

Nucleo-64板载ST-LINK/ V2-1调试器 之对外界编程

在Windows上配置Eclipse中配置STM32的开发调试环境

缓存分布式锁

官方分布式锁redisson—–文档

基于redis缓存分布式锁

  • redis宕机(已锁/未锁)
  • 其中一个线程服务宕机(已锁)
redis SETNX 命令详解

SETNX key value

key 的值设为 value ,当且仅当 key 不存在。

若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

jedis.setnx(key,value); key 锁id ,value 过期时间

参考

分布式锁的三种实现的对比

时区问题

alpine镜像默认时区是UTC,执行命令date可以进行查看时区,默认返回Mon Jul 16 03:43:52 UTC 2018,因此在查看日志,以及java代码里使用new date()时获取的时间时UTC格式的。

原因

参考:Setting the timezone

解决apline时区问题只需安装tzdata然后设置下就可以了,在alpine执行

添加字体的化安装ttf-dejavu解决

1
2
java.lang.NullPointerException: null
at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)
1
2
3
4
5
6
date #查看时区UTC
apk update
apk add tzdata ttf-dejavu
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo "Asia/Shanghai" > /etc/timezone
date #执行命令date可以进行查看时区

解决方式一(采用)

镜像大小:

  • java:8-jre-alpine 107.9M
  • java:8-jre-alpine-cst 109.2M

封装docker镜像一层,这里操作的基础镜像是用java:8-jre-alpine进行封装

1
2
3
4
5
# 生成镜像name:java:8-jre-alpine-cst
FROM java:8-jre-alpine
RUN apk add --no-cache tzdata ttf-dejavu \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone

下次直接就直接使用自己的镜像java:8-jre-alpine-cst

参考:Linux下修改时区

解决方式二

可以直接封装在java:8-jre-alpine镜像里,这种需要知道java:8-jre-alpine镜像的dockerfile于构建所需的环境包,这种方式构建镜像理论上应该比方式一小。

解决方式三

在构建java应用程序时构建进行时区设置,这种方式存在,每次打包都要构建安装执行tzdata,在网差的情况下,这种构建就很慢了

1
2
3
4
5
6
7
8
9
10
11
12
#基础镜像选择alpine 小巧安全流行方便
FROM java:8-jre-alpine
#复制固定路径下打包好的jar包(target/*.jar)并重命名到容器跟目录(/app.jar),或ADD
COPY target/service.jar app.jar
COPY target/lib lib
RUN apk add --no-cache tzdata \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
#健康检查 -s 静默模式,不下载文件
#HEALTHCHECK CMD wget -s http://127.0.0.1:14030/actuator/health || exit 1
#启动容器执行的命令 java -jar app.jar ,如果加其他参数加 ,"-参数",
CMD ["java","-jar","app.jar"]

debian 系统

1
2
3
#TZ=Asia/Shanghai
#命令行设置方法
ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone
dockerfile 设置
1
2
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

安装环境准备

直接使用win10的wsl沙盒Ubuntu系统,自带python3.5

安装

1
2
apt install python3-pip
pip3 install rsa

注意事项

IndentationError: unexpected indent 检查缩进是否一致,空格和Tab符号注意区分

实战

通过cookie爬百度数据

  1. 登陆百度,通过浏览器设置-内容管理-cookie,找到百度的BDUSS的内容复制

    62oGQA.gif

  2. 编写脚本login.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import requests
    #需要爬数据的url
    url = 'http://i.baidu.com/'
    #浏览器访问网站的cookie信息
    cookie = {"BDUSS":"----------------------------------------------------AAAAAAAAAAA----------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA--"}
    #requests请求,获取登录网站页面的内容
    html = requests.get(url,cookies=cookie).content
    #print(html)
    #把内容保存为文件
    with open("baidu.html", 'wb') as f:
    f.write(html)
    f.close()
  3. 在Ubuntu bash执行python3 login.py,会生成一个文件baidu.html在当前目录,打开如果能看到个人信息就证明获取成功

爬百度翻页数据

上面已经登陆成功了,下面直接用cookie进行爬数据会被重定向,还需要添加请求头,以及翻页参数

62o9aT.gif

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
#需要爬数据的url
url = 'https://jingyan.baidu.com/user/nucpage/content'
#浏览器访问网站的cookie信息
cookie = {"BDUSS":"-----QAAAAAAAAAAAEAAA--1QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA---"}

#提供你所使用的浏览器类型、操作系统及版本、CPU 类型、浏览器渲染引擎、浏览器语言、浏览器插件等信息的标识
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"
# 从那个连接来的
referer="https://jingyan.baidu.com/user/nucpage/content"
# 设置请求头
headers = {
"User-Agent": user_agent,
"Referer": referer
}
# url参数
# https://jingyan.baidu.com/user/nucpage/content?tab=exp&expType=published&pn=20
params = {
'tab': 'exp',
'expType': 'published',
'pn': '30'
}

#requests请求,获取登录网站页面的内容
html = requests.get(url,cookies=cookie,headers=headers).content

#print(html)
#把内容保存为文件
with open("baidu.html", 'wb') as f:
f.write(html)
f.close()

最终版爬百度经验的个人经验数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import requests
#正则
import re
#需要爬数据的url
url = 'https://jingyan.baidu.com/user/nucpage/content'
#浏览器访问网站的cookie信息
cookie = {"BDUSS":"--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA-"}

#提供你所使用的浏览器类型、操作系统及版本、CPU 类型、浏览器渲染引擎、浏览器语言、浏览器插件等信息的标识
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"
# 从那个连接来的
referer="https://jingyan.baidu.com/user/nucpage/content"
# 设置请求头
headers = {
"User-Agent": user_agent,
"Referer": referer
}


#requests请求,获取发布数量
published = requests.get(url,cookies=cookie,headers=headers).content
#<li><a class="on" href="/user/nucpage/content">已发布 (505)</a></li>
reg=r'<li><a class="on" href="/user/nucpage/content">已发布 \((.*?)\)</a></li>'
publishedNum=re.search(reg,published.decode(),re.I|re.M|re.S).group(1)
#group(0) 匹配的串,group(1) 匹配的串中第一个括号
print(publishedNum)
#算页数,实际篇数-1
pages=int((int(publishedNum)-1)/20)+1
print(pages)
#把内容保存为文件,'w'是写,'wb'是写入byte
with open("jingyan.md", 'w') as f:
for page in range(0,pages):
pn=page*20
print(pn)

# url参数
# https://jingyan.baidu.com/user/nucpage/content?tab=exp&expType=published&pn=20
params = {
'tab': 'exp',
'expType': 'published',
'pn': pn
}

#requests请求,获取登录网站页面的内容
html = requests.get(url,cookies=cookie,headers=headers,params=params).content

#过滤
reg=r'<a class="f14" target="_blank" title=(.*?)>'

#re.I 使匹配对大小写不敏感
#re.M 多行匹配,影响 ^ 和 $
#re.S 使 . 匹配包括换行在内的所有字符
#这个是查找此字符串中所有符合条件的内容并返回一个列表
list=re.findall(reg,html.decode(),re.I|re.M|re.S)
#写入文件并替换为markdown格式
for item in list:
item=item.replace('" href="','](https://jingyan.baidu.com')
item=item.replace('.html"','.html)')
item=item.replace('"','[')
f.write("%s\n" % item)
f.close()

参考:

Python:网页的抓取、过滤和保存

问题:

问题1: 文件损坏
1
2
3
4
//该格式会转码导致部分数据丢失
ftp.setFileType(FTP.ASCII_FILE_TYPE);
//该格式不会丢失数据
ftp.setFileType(FTP.BINARY_FILE_TYPE);
问题2:上传失败

发现在虚拟机运行代码时发现通过FTP上传文件总是失败返回500 Illegal PORT command

查资料所得发现FTP工作协议分:参考FTP时显示500 Illegal PORT command的解决

  • 主动模式:服务器向客户端敲门,然后客户端开门(端口)
  • 被动模式:客户端向服务器敲门,然后服务器开门(端口)

从上面分析就知道,主动模式肯定不行的,虚拟机里的服务开放端口,然后告诉服务器,但是服务器并不能访问虚拟机的端口,他只能访问虚拟机宿主机的端口,因此导致了本地可以运行,但是放到虚拟机却上传失败了

解决

然后在代码里添加ftp.enterLocalPassiveMode();设置为被动模式,记得上传下载都要设置。

不设置默认为主动模式enterLocalActiveMode()

附代码

pom.xml添加ftp依赖

1
2
3
4
5
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.5</version>
</dependency>

ftp工具类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
* ftp上传下载工具类
*/
public class FtpUtil {
/**
* Description: 向FTP服务器上传文件
*
* @param host FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param basePath FTP服务器基础目录
* @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
*/
public static boolean uploadFile(String host, int port, String username, String password, String basePath,
String filePath, String filename, InputStream input) throws IOException {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);// 连接FTP服务器
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
ftp.enterLocalPassiveMode(); //设置被动模式
//切换到上传目录
if (!ftp.changeWorkingDirectory(basePath + filePath)) {
//如果目录不存在创建目录
String[] dirs = filePath.split("/");
String tempPath = basePath;
for (String dir : dirs) {
if (null == dir || "".equals(dir)) continue;
tempPath += "/" + dir;
if (!ftp.changeWorkingDirectory(tempPath)) {
if (!ftp.makeDirectory(tempPath)) {
return result;
} else {
ftp.changeWorkingDirectory(tempPath);
}
}
}
}
//设置上传文件的类型为二进制类型
ftp.setFileType(FTP.BINARY_FILE_TYPE);

//上传文件
if (!ftp.storeFile(filename, input)) {
return result;
}
input.close();
ftp.logout();
result = true;
} catch (IOException e) {
// e.printStackTrace();
throw e;
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}

/**
* Description: 从FTP服务器下载文件
*
* @param host FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param remotePath FTP服务器上的相对路径
* @param fileName 要下载的文件名
* @param localPath 下载后保存到本地的路径
* @return
*/
public static boolean downloadFile(String host, int port, String username, String password, String remotePath,
String fileName, String localPath) {
boolean result = false;
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return result;
}
ftp.enterLocalPassiveMode(); //设置被动模式
ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
File localFile = new File(localPath + "/" + ff.getName());
OutputStream is = new FileOutputStream(localFile);
ftp.retrieveFile(ff.getName(), is);
is.close();
}
}

ftp.logout();
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}

/**
* 删除FTP上指定路径的文件
*
* @param host
* @param port
* @param username
* @param password
* @param ftpDirAndFileName
* @return
*/
public static boolean deleteFile(String host, int port, String username, String password, String ftpDirAndFileName) {
FTPClient ftp = new FTPClient();
try {
int reply;
ftp.connect(host, port);
// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
ftp.login(username, password);// 登录
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return false;
}
boolean hasDelete = ftp.deleteFile(ftpDirAndFileName);

ftp.logout();
return hasDelete;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}

return false;
}
}
问题3:docker部署,无权限问题

原因:由于使用ftp登陆时默认跟目录/其实时用户ftp home目录,因此设置home目录因该是挂载卷的子目录,不然同级回提示没权限,如果新配置记得删除passwd的文件或删除,不然配置修改无效

正确的配置文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ftpd-server:
restart: on-failure
image: stilliard/pure-ftpd:hardened
volumes:
- /dockerdata/v-manager-test-ygl/ftpdata:/home/ftpusers
- /dockerdata/v-manager-test-ygl/ftpconfig:/etc/pure-ftpd/passwd
ports:
- "14821:21"
- "30000-30009:30000-30009"
environment:
PUBLICHOST: "192.168.1.230"
FTP_USER_NAME: "ftpuser"
FTP_USER_PASS: "ftpuser"
FTP_USER_HOME: "/home/ftpusers/ftpuser"

问题4:ftp 被动模式依赖iptable服务

java读取sqlit db文件

首先在pom.xml引入依赖包

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.23.1</version>
</dependency>

读取数据db

1
2
3
4
5
6
7
8
9
@Before
public void loadDb() {
try {
connection = DriverManager.getConnection("jdbc:sqlite:C:\\Users\\Administrator\\Desktop\\data.db");
System.out.println("连接成功");
} catch (SQLException e) {
e.printStackTrace();
}
}

读取完关闭数据库

1
2
3
4
5
6
7
8
@After
public void after() {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

单行读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Map queryMap(String sql) throws SQLException {
HashMap row = null;
System.out.print("执行:" + sql);
ResultSet rs = connection.createStatement().executeQuery(sql);
if (rs.next()) {
ResultSetMetaData md = rs.getMetaData();
int columns = md.getColumnCount();
row = new HashMap(columns);
for (int i = 1; i <= columns; ++i) {
if (i > 1) System.out.print(", ");
row.put(md.getColumnName(i), rs.getObject(i));
System.out.print(rs.getObject(i) + " " + md.getColumnName(i));
}
}
rs.close();
return row;
}

多行读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public List queryList(String sql) throws SQLException {
System.out.print("执行:" + sql);
ResultSet rs = connection.createStatement().executeQuery(sql);
ResultSetMetaData md = rs.getMetaData();
int columns = md.getColumnCount();
ArrayList list = new ArrayList(50);
while (rs.next()) {
HashMap row = new HashMap(columns);
for (int i = 1; i <= columns; ++i) {
if (i > 1) System.out.print(", ");
row.put(md.getColumnName(i), rs.getObject(i));
System.out.print(rs.getObject(i) + " " + md.getColumnName(i));
}
list.add(row);
}
rs.close();
return list;
}

注意事项

  1. ResultSet连续查询会覆盖之前的,无论是不是新的ResultSet都是共享一个,因此读完数据保存到map马上关闭

  2. connection.createStatement()这个对象也不能共享全局,会出现数据库未关闭的错误

  3. 问题,该驱动目前好像不支持alpine系统,因此用docker打包式,基础镜像用alpine会加载不了驱动

    1
    2
    [WARN]: Failed to load native library:sqlite-3.15.1-6f7bc1af-1dba-4675-84c7-aaf90017dff0-libsqlitejdbc.so. osinfo: Linux/x86_64
    [WARN]: java.lang.UnsatisfiedLinkError: /tmp/sqlite-3.15.1-6f7bc1af-1dba-4675-84c7-aaf90017dff0-libsqlitejdbc.so: Error relocating /tmp/sqlite-3.15.1-6f7bc1af-1dba-4675-84c7-aaf90017dff0-libsqlitejdbc.so: __isnan: symbol not found

    解决:替换基础镜像,这里用java:8-jre他的基础系统是debian

    参考: Failed to load native library:sqlite-3.15.1

    解决:居然是版本问题,换成3.8.11.2可以读取

测试

利用docker镜像测试jdbc驱动包

准备测试代码SQLiteJDBC.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.sql.*;

public class SQLiteJDBC
{
public static void main( String args[] )
{
Connection c = null;
try {
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:test.db");
} catch ( Exception e ) {
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
System.exit(0);
}
System.out.println("Opened database successfully");
}
}

然后准备好db测试数据库文件、SQLiteJDBC.java测试代码文件、sqlit-jdbc驱动包:sqlite-jdbc-3.8.11.2.jar 这几个文件都放在同一个目录,然后切合到该目录执行以下命令

1
2
3
4
#编译生成class文件
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp java:8-jdk-alpine javac SQLiteJDBC.java
#运行class文件
docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp java:8-jdk-alpine java -classpath ".:sqlite-jdbc-3.8.11.2.jar" SQLiteJDBC

docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp <java镜像名字> <java命令>该命令意思是当前挂载当前路径到myapp目录,--rm 是一次性,用完即毁

安卓文件离线断点下载

GitHub:lingochamp/FileDownloader

添加依赖implementation 'com.liulishuo.filedownloader:library:1.7.4'

Application里添加初始化

1
2
3
4
5
6
7
8
FileDownloader.setupOnApplicationOnCreate(this)
.connectionCreator(new FileDownloadUrlConnection
.Creator(
new FileDownloadUrlConnection.Configuration()
.connectTimeout(15_000) // set connection timeout.
.readTimeout(15_000) // set read timeout.
)
).commit();

单次弹窗下载进度条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("是否需要下载离线地图?");
builder.setMessage(f.getAbsolutePath() + " 没有找到离线地图文件");
builder.setCancelable(true);
final ProgressDialog dialog22 = new ProgressDialog(this);
dialog22.setTitle("正在下载离线地图");
dialog22.setCancelable(false);
builder.setPositiveButton("下载(文件>500M)", new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, int which) {
FileDownloader.getImpl().create(Config.KJMAPDOWNLOAD)
.setAutoRetryTimes(100)
.setPath(f.getAbsolutePath(),true)
.setListener(new FileDownloadLargeFileListener() {
@Override
protected void pending(BaseDownloadTask task, long soFarBytes, long totalBytes) {
dialog22.show();
}
@Override
protected void progress(BaseDownloadTask task, long soFarBytes, long totalBytes) {
dialog22.setMessage( "重试"+task.getRetryingTimes()+"次,下载速度:" + task.getSpeed() + "kb/s,百分比:" + soFarBytes / 1048576 + "/" + totalBytes / 1048576 + "M");
}
@Override
protected void completed(BaseDownloadTask task) {
Toast.makeText(LineActivity.this, "下载成功,请重新打开该页面", Toast.LENGTH_LONG).show();
String oldFileUrl=task.getPath()+"/"+task.getFilename();
Log.i("oldFileUrl",oldFileUrl);
File oldName=new File(oldFileUrl);
File newName=new File(f.getAbsolutePath()+"/"+Config.KJMAPDOWNLOADFILENAME);
if (oldName.renameTo(newName)){
Log.i("LineActivity","重命名成功");
}else {
Log.e("LineActivity","重命名失败");
}
dialog22.dismiss();
}
@Override
protected void paused(BaseDownloadTask task, long soFarBytes, long totalBytes) {
}
@Override
protected void error(BaseDownloadTask task, Throwable e) {
Toast.makeText(LineActivity.this, "下载失败", Toast.LENGTH_LONG).show();
dialog22.dismiss();
}
@Override
protected void warn(BaseDownloadTask task) {
}
}).start();
dialog.dismiss();
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();

注意事项

  1. getAbsolutePath()获取文件路径,末尾没有/ 如果获取文件地址需要手动拼接/
  2. .setPath(f.getAbsolutePath(),true)这个必须设置,后面需要指定该路径是文件还是目录,如果要设置文件名,需要设置为false,然后路径拼接文件名,如果是路径模式,下载完成后需要手动重命名,否则没有文件后缀
  3. 该下载会自动接着上次未完成的下载,需要重新下载,可以去下载目录删除未完成的文件即可
  4. 文件大小可以能大于1g用FileDownloadLargeFileListener

maven基础

``clean install -DskipTests=true` 清理打包

clean install -DskipTests=true -pl app -am 清理 打包安装 跳过测试 单模块(app)

tomcat7:run 用内置tomcat运行jar

1
2
#解决maven仓库明明有包,但是idea下载不下来,可以手动执行命令进行下载,执行前先清理本地仓库目录
mvn dependency:get -DremoteRepositories=https://repo1.maven.org/maven2 -DgroupId=org.java-websocket -DartifactId=Java-WebSocket -Dversion=1.3.8
注意

clean再运行时清理会报错,所以需要先当掉

常见问题

  1. 在一个带父子项目层级的项目的子项目里面引入一个第三方依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.influxdb</groupId>
    <artifactId>influxdb-client-java</artifactId>
    <version>6.7.0</version>
    </dependency>

    第三方依赖里面又依赖了com.squareup.okhttp3,依赖里面的版本的dependencyManagement管理的是<dependency.okhttp3.version>4.10.0</dependency.okhttp3.version>,但是实际就变成了3.8.1,导致okhttp一些方法提示不存在。

    原因:未知

    解决:在父项目的pom.xml里面增加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <properties>
    ....
    <dependency.okhttp3.version>4.10.0</dependency.okhttp3.version>
    </properties>
    ....
    <dependencyManagement>
    <dependencies>
    ......
    <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>${dependency.okhttp3.version}</version>
    </dependency>

    <dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>logging-interceptor</artifactId>
    <version>${dependency.okhttp3.version}</version>
    </dependency>

    </dependencies>
    </dependencyManagement>

stm32开发环境搭建(mac)

环境工具

  • CLion
  • STM32CubeMX 记录见:mac系统安装STM32CubeMX
  • Segger 下载 Ozone - The J-Link Debugger
  • 安装编译器:arm-gcc-none-eabi-gcc brew cask install gcc-arm-embedded
  • 安装stlinkbrew install stlink
  • 安装openocd 执行brew install openocd

运行测试第一个跑马灯

环境准备

  • mac os系统

  • 开发板NUCLEO-F401RE 其中mcu型号stm32f401RETx

  • 安装项目初始化软件STM32CubeMX 步骤见mac系统安装STM32CubeMX

  • 安装编译器arm-gcc-none-eabi-gcc 执行brew cask install gcc-arm-embedded

  • 安装stlink下载器brew install stlink

  • 开发板NUCLEO-F401RE跳线如图

    62fMm8.png

项目搭建步骤:

  1. 初始化项目工程见STM32CubeMX使用之初始化项目

  2. 进入生成的工作目录执行make

  3. 提示错误/bin/sh: /arm-none-eabi-gcc: No such file or directory解决,修改Makefile

    1
    2
    # BINPATH是指arm-none-eabi-gcc的路径,可以通过执行which arm-none-eabi-gcc得到路径
    BINPATH = /usr/local/bin/
  4. 提示错误

    1
    2
    build/main.o: In function `main':
    /Users/xuanleung/IdeaProjects/f401demo/Src/main.c:75: multiple definition of `main'

    解决修改Makefile,删除里面重复的,#标注为有重复,删除即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    C_SOURCES =  \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c \
    #Src/stm32f4xx_it.c \
    Src/stm32f4xx_it.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c \
    /Src/system_stm32f4xx.c \
    #Src/stm32f4xx_hal_msp.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c \
    #Src/main.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c \
    Src/stm32f4xx_hal_msp.c \
    Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c \
    Src/main.c
  5. 再重新执行make,得到编译成功的文件了

    1
    2
    3
    4
    5
    /usr/local/bin//arm-none-eabi-size build/f401demo.elf
    text data bss dec hex filename
    5460 20 1636 7116 1bcc build/f401demo.elf
    /usr/local/bin//arm-none-eabi-objcopy -O ihex build/f401demo.elf build/f401demo.hex
    /usr/local/bin//arm-none-eabi-objcopy -O binary -S build/f401demo.elf build/f401demo.bin
  6. 下载bin文件到开发板NUCLEO-F401RE ,执行st-flash write ./build/xxxxx.bin 0x8000000,如果下载失败重启单片机和检查跳线帽

  7. 修改src下面的main.c添加跑马灯代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    while (1)
    {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
    HAL_Delay(500);
    HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
    HAL_Delay(500);
    }
  8. 重新编译make和下载st-flash write ./build/f401demo.bin 0x8000000

  9. 下载完成后ld2就会闪烁

使用CLion进行项目编译运行

操作记录:mac下stm32CubeMX+CLion+openocd开发环境

  1. 使用STM32CubeMX重新初始化项目,选择SW4STM32

问题

debug提示如下错误:

1
Cannot load symbol file: "/Users/xuanleung/IdeaProjects/f401re_demo/cmake-build-debug/f401re_demo.elf": not in executable format: File format not recognized

解决:取消勾选

62fsh9.png

参考

macOS 下用 Clion和OpenOCD开发 STM32(st-link和STM32CubeMX)

CLion for embedded development

CLion for Embedded Development Part II

http://blog.meekdai.com/MacOS-Eclipse-ARM-GCC-STM32.html

STM32 Nucle o-64 boards文档

Nucleo-64板载ST-LINK/ V2-1调试器 之对外界编程