基于STM32L476+64M QSPI接口PSRAM(IPS6404L)开源分享(含源码)
基于STM32L4R9 的QuadSPI Flash 通讯速率不理想经验分享
STM32L4超低功耗功能概述
基于STM32L431RC Standby和RTC中断唤醒经验分享
基于STM32L431的睡眠模式经验分享
STM32L4R9 的 QuadSPI Flash 通讯速率不理想
STM32L4、STM32L4+和STM32G4系列 微控制器上的专利代码读取保护
STM32L433在STOP模式USART不能工作的解决办法
【实测教程】基于STM32L4系列的实测教程分享合集
STM32L4系列MCU的五种振荡器和使用说明
添加完宏定义后再给自己添加的xxx.c文件添加对应函数如下:
******************************驻网测试函数***************************; ^: Z x: w4 p, y5 j
void _BC28_SET_(void)
{
_DEBUGE_FLEG=0;
- t: p8 _- v5 }0 b/ w- p
//关闭操作标志位;
// _BC28_RESET_();
printf("AT+NRB\r\n ");" F( A2 g3 ~, {# O
HAL_Delay(2000);4 C! {$ y7 S/ v% n
_BC28_CMD_(CMD_ECHO, DCT_OK,300,4);# j3 V% g4 v' B5 n
_BC28_CMD_(CMD_CUFN_ASK, DCT_CUFN_ASK_1,300,4);
_BC28_CMD_(CMD_CUFN_SET, DCT_OK,300,4);% V5 ^8 O& R; k2 ~
_BC28_CMD_(CMD_EDRX_SET, DCT_OK,300,4); J) B- X9 C: g2 O+ j# e2 W) E
_BC28_CMD_(CMD_NCDP, DCT_OK,1000,4);; j6 T" E. `. Q" B' F
_BC28_CMD_(CMD_NCDP_ASK, DCT_OK,300,4);+ w1 ?+ ~: b4 W, M2 P
_BC28_CMD_(CMD_CGATT_SET, DCT_OK,300,2); C( z+ p; k2 P/ D
_BC28_SIGNALASK_();$ o7 w) i; v8 q* ` a8 D) W
if(_BC28_CMD_(CMD_CGATT_ASK, DCT_ADCGATT1,2000,4))$ U/ l9 n9 F5 w9 H# Q5 r6 }; Y
{! d8 M# d2 N1 I9 u- \; n9 F
HAL_GPIO_WritePin(STATE_LED_GPIO_Port, STATE_LED_Pin, GPIO_PIN_SET);
}2 r5 @$ H2 K! {# D8 ~6 i2 U+ L& H8 A
_BC28_CMD_(CMD_CGPADDR, DCT_OK,300,4);
_BC28_CMD_(CMD_NNMI_SET, DCT_OK,300,4);
_BC28_CMD_(CMD_NSMI_SET, DCT_OK,300,4);: @2 z& m) O, t3 p! e
_BC28_CMD_(CMD_NNMI_ASK, DCT_MNMI_1,300,4);
_BC28_CMD_(CMD_NSMI_ASK, DCT_NSMI_1,300,4);" X/ G5 _5 o( h0 o% U. Y) N( e0 w" E' c
HAL_Delay(200); V! Q( ^: q$ N+ R8 T! Z" X7 J) J
if(_BC28_CMD_1(CMD_send_TexT, DCT_RCVE,600,3,4))
{% P8 o+ N& G3 ~
_BC28_SET_();+ [$ J; t, L$ s5 [
}3 H0 T% Y6 }; U1 e+ ?9 O5 C
_BC28_CMD_(CMD_AT, DCT_OK,300,20);
HAL_GPIO_WritePin(STATE_LED_GPIO_Port,STATE_LED_Pin,GPIO_PIN_RESET);
_DEBUGE_FLEG=1;
}4 o# g5 O3 N9 r& R, t' l
这个测试函数的基本操作首先使用发送AT+NRB对模块进行重启,重启的过程大概需要2s左右,所以这里需要做相应的延时,防止后面命令发出去后被忽略,重启后使用char _BC28_CMD_(char* pstr_CMD,char* pstr_DCT,int delay_us,int MAXTRY)发送对应命令进行设置,从上图中可以看到基本都是最这个函数的调用,而这个函数实际是将串口的发送函数进行封装了,里面添加了延时,发送次数等参数的设置,熟悉C编程的朋友看到这里也就明白了,我们 实际也是使用printf函数进行命令的发送,这里涉及到对串口发送函数的重定向。发送函数的重定向可以使用直接用寄存器进行,memset()和strstr()函数需要包含string.h头文件,由于用到printf函数所以还需要xxx.h文件开头添加如下是代码:
xxx.c文件中添加如下代码
***************************xxx.c中命令发送函数**********************: W& @. }6 y% M: r% d4 s
char _BC28_CMD_1(char* pstr_CMD,char* pstr_DCT,int delay_us,int MAXTRY,int MAXWAT)
{$ z4 g' C. n& F; G0 |' E
char *strx;) v% g& i7 x: U( K
int i=0,j=0;
memset(RX_BUFFER_lp, NULL, 200);
strx=NULL;9 P+ h; b0 q& L. q7 Z
while(strx==NULL && j < MAXTRY)
{7 ?2 E* F" D6 {$ X5 v( l4 c( w
memset(RX_BUFFER_lp, NULL, 50);$ q# `+ d U+ A5 o0 u) v
HAL_Delay(50);
printf("%s",pstr_CMD);( c# i$ ~: g) Y1 e( e- a/ r; Q
j++;
i=0;
while(strx==NULL && i < MAXWAT)
{+ s4 j2 C( U+ q( B4 {8 c. {: p, ]
strx=strstr((const char*)RX_BUFFER_lp,(const char*)pstr_DCT);
HAL_Delay(delay_us);
i++;
}& k: V' I/ T# g$ z2 E- N
}
if(strx==NULL )7 l3 a" [8 _$ p! `8 h9 S
{& ~1 R! m! j: S" f/ `
return 0;
}
else; G: [/ X' \5 W. C1 e+ X
{
return 1;
}6 v: l, b" s( _% h
}
char _BC28_CMD_(char* pstr_CMD,char* pstr_DCT,int delay_us,int MAXTRY)& ~* }6 }9 k9 t1 n$ n. k
{
char *strx;
int i=0;
memset(RX_BUFFER_lp, NULL, 200);
strx=NULL;
HAL_Delay(50); |4 e, U$ z8 \- Q6 n$ l3 C) o
while(strx==NULL && i < MAXTRY)
{
memset(RX_BUFFER_lp, NULL, 200);
printf("%s",pstr_CMD); //???lpuart????????, l/ f. L( T! X
HAL_Delay(delay_us);' M$ n+ E1 m( u" ]5 l* o7 H
strx=strstr((const char*)RX_BUFFER_lp,(const char*)pstr_DCT);
i++;
}9 s4 L& ]( K) d3 R6 a
if(strx==NULL )
{
return 0;; P/ l7 x6 _* J
}" o; q' E+ h* k
else
{
return 1;
}
}' B5 V* v2 ~/ j f/ @& z
char _BC28_SIGNALASK_(void)
{* {& d- g- l: Q0 s* v" |
memset(RX_BUFFER_lp, NULL, 200);+ B# H# v) x1 i. E) @
char CSQ=99;
char *strx;
int i=0,j=0;% `. M; R0 \1 W
strx=NULL;1 R9 N2 o7 o- \% U5 k3 L( ^
HAL_Delay(50);
while(strx==NULL && i < 30 )4 L5 f# J' O2 M% I
{
printf("%s",CMD_CSQ); 1 f0 x2 M8 G* n
HAL_Delay(200);
strx=strstr((const char*)RX_BUFFER_lp,(const char*)DCT_ADCSQ);
i++;
if(strx)" I4 i4 z3 B2 e2 {& [( Z3 D
{$ C& K& j3 P F7 L; F7 e' o1 Z* v
CSQ=(strx[5]-0x30)*10+(strx[6]-0x30);4 Z0 w0 m6 n: L+ C. ^5 n
if(CSQ==99); r$ i. e2 z- ~% S. N+ v4 y" b* a
{+ i- Y6 h2 |( P# Q% U
memset(RX_BUFFER_lp, NULL, 50);# X2 d+ L% P! ^& l* `* l, V
while(CSQ==99 && j < 300)& W' }3 Z5 j- d& ^) N. C" M' X
{9 H% [0 o3 y, ^% y. K1 s
printf("%s",CMD_CSQ); ֵ( b) z; V+ ]! Y( G$ ^
HAL_Delay(400);
strx=strstr((const char*)RX_BUFFER_lp,(const char*)DCT_ADCSQ);9 e, c) y7 w% ?1 S9 T. r
CSQ=(strx[5]-0x30)*10+(strx[6]-0x30);4 S2 t# G0 y& J9 r
j++;" o+ q( c6 p( p) J9 G/ |+ R
}
}2 S. B* p8 m5 P, q) Q- n* P8 f
}& B. u9 Y2 h" z$ P/ |% b# e; E
}
if(CSQ==99)
{
return 0;' ?8 f1 J; l+ m
}
else
{
return CSQ;& x* Q& | |* @! z6 ?
}
}
char _BC28_SIGNALASK_(void)0 d( H2 H! Y1 {: I$ T- m0 t. e
{& B0 ]4 P: h- w# S* i4 ?7 x: s
memset(RX_BUFFER_lp, NULL, 200);
char CSQ=99;4 z& u" a8 I c, T2 E2 g
char *strx;
int i=0,j=0;$ d( m+ X& @. Y
strx=NULL;% U# J. t, @+ r R3 W9 i+ k
HAL_Delay(50);
while(strx==NULL && i < 30 ) P' b/ ]1 _' f( C
{
printf("%s",CMD_CSQ); //查看获取CSQ值3 s) A2 B: J, [8 D9 ]0 V$ H
HAL_Delay(200); ]" |- \, u: B) c1 E
strx=strstr((const char*)RX_BUFFER_lp,(const char*)DCT_ADCSQ);
i++;
if(strx)
{4 B5 v w1 A: z# ?, \/ J3 k
CSQ=(strx[5]-0x30)*10+(strx[6]-0x30);//信号转码0 U4 ?* `+ _( C! i' Z4 j# W; K- p
if(CSQ==99)1 c5 Q; z9 q8 [+ u" K
{3 }6 J2 }) I' m* R1 |0 k2 ^
memset(RX_BUFFER_lp, NULL, 50);
while(CSQ==99 && j < 300)8 M/ |+ O5 ?# L
{
printf("%s",CMD_CSQ); //查看获取CSQ值8 K! V3 c/ [3 ^, U$ l: f) n( f+ X
HAL_Delay(400);
strx=strstr((const char*)RX_BUFFER_lp,(const char*)DCT_ADCSQ);
CSQ=(strx[5]-0x30)*10+(strx[6]-0x30);//信号转码
j++;
}; A, n* ~( P. M- O# N: y& c, p
}
}
}
if(CSQ==99)
{
return 0;( m* l" `, j J! L7 O6 F
}, Z# r4 Y# V- }$ O0 W
else9 P* X3 u4 V1 W% s
{' S/ U% H$ g6 |8 C6 Z( S
return CSQ;
}
}
extern uint8_t RX_BUFFER_lp[200];0 d) I( a3 d7 }6 t
extern uint8_t RX_Conter_lp;* v1 Z: |: i- R5 r% U) Z3 w
extern __IO char _DEBUGE_FLEG;
在usart.c中添加如下代码
*****************************串口重定向函数**************************, V8 ] _9 w! J! F. c2 o
#include "stdio.h"6 l: e$ u" P+ o \& f3 z* a* A: B& z
int fputc(int ch,FILE *f)
{
while((LPUART1->ISR&0x40)==0);9 ] {% G& g8 `+ o- o
LPUART1->TDR=ch;
return ch;
}. \2 f" j# O3 U& W4 s5 s" E( [
*****************************宏定义变量******************************
#define Rcive_size_lp 2008 `! @( _5 w C0 R# Q7 `
#define Rcive_size_1 10
*************************需要包含的头文件代码************************& _+ M$ M* [: a2 ~, ~
#include "usart.h"
#include "main.h"
#include "string.h" K/ G( [" B& I5 @& [1 P
这里只是对函数示例,因为这个很常用,基本都清楚,但是考虑到初学者可能忘记这一步所以贴这里。3 w$ v2 t+ ~ s! P. j7 H* B+ ~/ |& H
stm32lxx_it.c文件中添加如下是代码
*************************外部宏定义重申明***************************; X( R _- P5 U8 F% A4 a6 ~6 T0 J
extern UART_HandleTypeDef hlpuart1;6 @, b5 F4 x# _% h/ U+ E
extern uint8_t RX_Conter_1;' [* `1 u" _4 a7 A0 G8 R! H
extern uint8_t RX_BUFFER_LPTMP[200];. ?$ ]4 [6 r, Q: X2 H3 Q
extern uint8_t RX_BUFFER_lp[200];( R+ W4 C$ k* N: h+ C' k
extern uint8_t RX_Conter_lp;8 I; Q$ x. L( x. R) H9 P, J
% Y$ h; r6 v) A: g0 l/ }& m" H6 p
extern uint8_t RX_BUFFER_1TMP[10];5 o) w; v" t+ {. D8 L
extern uint8_t RX_BUFFER_1[10];
extern uint8_t RX_Conter_1;1 f7 p: l- Z/ L8 O1 t1 w1 z% B* \
***********************LPUSAR中断服务函数***************************6 X8 e6 H9 ~1 E" f0 _7 M
void LPUART1_IRQHandler(void)( L2 H4 ~ Q* [& x! \4 i
{7 A1 Q0 M# F* O2 C: G
/* USER CODE BEGIN LPUART1_IRQn 0 */: _- p0 Y& S! w F
char *xstrx=NULL;" K4 p7 I; c2 Z' Y, r X+ D$ M
uint32_t tmp_flag = 0;1 i# z. D0 l/ U' W. m2 F
uint32_t temp;
/* USER CODE END LPUART1_IRQn 0 */5 x1 }% F- h, b: o# d8 O
HAL_UART_IRQHandler(&hlpuart1);
/* USER CODE BEGIN LPUART1_IRQn 1 */
tmp_flag = __HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE);% @+ o& x, }+ N* A1 E% V* D' b& A
if((tmp_flag != RESET))5 }* z0 b6 ?1 }: y5 V
{
__HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
HAL_UART_DMAStop(&hlpuart1);0 S3 S' {+ G) A% A0 d
temp = hdma_lpuart_rx.Instance->CNDTR;
RX_Conter_lp =200- temp;3 r/ g! J1 J8 u
}6 |0 @8 f" P* e/ @2 D) d$ Z
HAL_UART_Receive_DMA(&hlpuart1, RX_BUFFER_lp,200);: K' S4 F$ T: f% V, d
}
做完上述程序编写、编译并烧录成功后,板卡重启后便开始自行测试,不过在刚拿到板卡后建议先使用串口通信助手进行下测试,防止数据因板卡问题导致各位工程师做很多无用功,而且很多IP参数等相关信息都需要按照自己的Iot平台设置和修改,首先我们通过串口通信进行测试板卡是否能正常工作;根据官方给的命令手册可以查到命令AT为查看模块是否正常工作;在模块正常工作后,按以下步骤进行学习和测试(具体命令详情请查看官方命令设置手册,以下操作均在串口通信助手进行)。+ F3 \9 U; S7 m/ J7 z
1、 使用MCU重启按键重启MCU模块,然后发送AT+NRB进行重启模块,等待如下图信息出现;
; ~, x F6 A$ d
2、 发送命令AT+CFUN=1将模块设置为全功能模式,返回值为OK;
3、 发送AT+NCCID查询SIM卡ID,可视作查询是否有SIM插入;返回值为+NCCID:SIM卡ID;2 r s+ l3 Q+ r! K' C) B
4、 发送命令AT+CGATT=1将板卡设置到连接分组域,返回值为OK;
5、 发送命令AT+CSQ查询SIM卡信号强度,返回值为+CSQ:0~31,99,如果第一个参数为99,则说明没有信号或正在查询信号,第二个参数恒为99;9 J1 T2 W) ?" \# K6 s9 \0 q
6、 发送AT+CEREG=1命令,进行设备联网,返回值为OK,返回OK后发送AT+CEREG?查询是否联网成功;如果返回值为+CEREG:1,1,则联网成功。
(1) AT+CIMI读取sim卡信息:1 K; Y2 e9 C( ?6 i
(2) AT+CGATT?查看网络附着状态,如果没有网络信号:
(3) AT+CMEE=1开启ERROR报告;
(4) ATE1设置信息回显模式;
(5) ATI查看模块版本信息;
(6) AT查看模块是否正常工作;( j5 l4 Y- O0 H* }7 s3 l7 X
(7) AT+NRB重启板卡;
(8) AT+NBAND=?查看模块支持频段,电信为5,移动联通为8,入AT+NBAND=5;6 K1 V$ U/ O+ w
(9) AT+NSOCR创建socket,(用于TCP通信);) x( N% g1 o4 f: e$ D
(10) AT+NSOCO连接到服务器,服务器需要自己搭建(TCP通信);
(11) AT+NSOSD=1,3,021031发送一段信息(TCP通信);
(12) AT+SONMI=1,3读取一段信息(TCP通信);
(13) AT+NSOCL=1关闭socket(TCP通信);: `9 Z W$ j a; z( j2 }
(14) AT+NCDP=180.101.147.115,8883设置Iot平台IP和端口号(Iot平台);& j3 J/ Z! M9 _+ w' n
(15) AT+QREGSWT?查询模块注册模式(Iot平台);% y9 \) f( _- Y7 | T
(16) AT+QLWSREGIND=0启动寄存器注册到Iot平台(Iot平台);
(17) AT+QLWULDATA=3,AABB11发送con数据到Iot平台(Iot平台);: E. q& _8 k+ d5 j2 N V. w
(18) AT+QLWULDATASTATUS?查询发送con数据到Iot平台的状态,返回4则为成功(Iot平台);- `( i g y/ l% \: g* r! |
(19) AT+NMGS=3,AA11BB发送信息到Iot平台(Iot平台);* _1 } d- Z' x
(20) AT+NNMI=1设置新消息指示,即使能新消息接收信息自动显示(Iot平台);
(21) AT+NSMI=1设置发送信息指示(Iot平台);% g* p( r$ M3 n5 h7 q1 u# V
到这里我们的板卡联网就成功了,接下来就是将设备连接到自己开发的云平台了,我们这里只对华为云平台进行介绍。最开始做的当然是注册和实名认证,如果是个人学习使用建议使用个人认证,送的发布信息等足够个人用户使用。在认证完后再进行下一步操作。
认证完后,在左上角产品中找到Iot物联网,点击后在右侧子菜单左上角的物联网平台(云)栏找到设备接入并点击,然后就能看到图14界面,点击立即使用;
图14
进入后在左侧可以看到六个板块,这里点击最下面的开发中心;进入开发中心后,找到如图15板块,点击新建项目;" L4 ~$ W/ B' U/ l1 Q# G& o. w
9 }, g k& Y1 H" i. B0 @2 f
图15
项目名称自己随便填,符合华为云命名规则就好,所属行业我们选NB-Iot,因为我们的板卡是只支持窄带宽(NB)的,这里还要说明的是选择SIM卡的时候要注意选用NB的专用卡(即窄带宽专用卡,可以去http://www.360wulian.net/上面免费申请试用),否则是识别只能读取到对应的SIM卡ID,但是不能联网而且没有信号,如图16;
% g7 f! h1 m! P4 w
图16
项目建好后点击项目右侧进入如图 然后我们就进入了华为OceanConnect开发中心了如图17;
- `. z l$ r7 F0 E4 E
图17
然后点击左侧开发中心进行创建产品,可以看到如图18界面;点击自定义产品,后再弹窗口填写产品相关信息设备名字、型号、设备类型所属行业随便写应该是没问题的,主要是协议,协议按照模块支持进行选取就好,我这里选择的是CoAP,设备图片的话可以选个自己喜欢的图片传上去,嘿嘿;点击创建,这样你的产品就创建好了,如图19;; `* o; v. C$ B) u2 d$ V
图18: M$ ]/ U( D4 r+ K0 K# L
图19# \$ D; \' X! D+ F) J# y6 K6 p
额,这两张图有点密集了,大家凑合这看看就好;接下来进入的界面是云端测试的相关事项了,首先是Profile的定义,如图20,点击右侧新建服务;
. \* T' Z) I7 z0 Y% C! L0 A/ D4 p9 t( R
图204 w) F: g# D- {" e
然后依次设置服务名称和对应属性和命令如图21所示(图被压缩的有点丑);3 n% q6 W5 F& _& h8 d& k3 _
% W" O! A& j7 X% l& j. {+ ~7 a
图21
属性的设置就这样了,然后设置命令,第一个open命令我设置的值是只能发0和1,用于打开和关闭led,如图22( I# U% v) X" x E% Z' o- I2 y
图22% N1 C3 C! s. v1 W# W6 N
Profile的部分到这儿就结束了,翻到页头,点击图编解码插件开发,如图图23,点击新增消息在弹出窗口添加数据上报填写消息名后点击添加字段再弹出窗口填写对应信息;如图24。! l* i* G9 v. A/ w4 ]
图23
这里的话按照我们设备的属性,有两个状态,所以我们添加两个状态字段;然后添加的下发的命令,实际操作和上报差不多,就不演示了,最后需要做的就是部署插件了点击右上角蓝色的部署两个字就好了4 a7 |+ O1 g+ v4 f2 k( I& g3 j
图24
设置完后点击右侧设备模型并将属性下的各个模块拖到左侧白色区域就好了,不拖过来的话是不能够上报和下发命令的哦,然后点击右侧上方的蓝色部署两个字等待部署好就行,如图25。
图25 Z- F* v& L- D) g6 `6 _, d" P
然后我们跳过第三步,直接进行第四步在线调试。在进入界面后点击屏幕中间的新增测试设备在弹出窗口可以看到可以添加虚拟设备和真实谁被两个选项,我们选择添加真实设备,设备名称随便写,设备标识我们填写通过AT+CGSN=1命令调出的编号,点击创建就好,如图26:( \: b& c/ S2 S1 q" y' A" b$ X
( j+ t* b+ N! m4 \. G# B
图26
这样我们先回到云平台,左侧菜单栏点击对接信息,然后对模块进行联网,拷贝图26红色框内容;: t6 M- R; R3 N9 Y$ x! D- T
图27) }! p/ a H9 k" a# W/ Y
在串口通信助手发送AT+NCDP= <IP>,<端口>命令,返回OK后,点击左侧产品开发,在以下界面点击图28红色框区域(我们创建的设备)就可以看到我们的产品已经上线了;这样的情况就是我们的板卡已经练到云端了。如图28;
图28
% `* v ?; B& ~( W& r( `
图29
进入图29后点击右侧红色框区域,便可以调试我们的产品了。进入调试后就需要把我们的设备连接到云平台,并进行数据的上报和下发。如图30为调试界面。
. V' g6 C* [. Q, L* }
图30/ ?( W2 q s0 J1 C* `( {; [
现在我们要做的事是将板卡链接到云平台,这里需要上报信息的命令AT+NMGS=<长度>,<数据>以这个格式发送信息,返回OK后,在云平台右侧效力跟踪里就能看到跟踪信息并且在应用模拟器里看到我们上报的信息如图31和图32.! J- i* {* C/ i8 V
图31 图32- |/ q" c9 _% ~3 r, `8 M3 O; `3 E
这样我们的上报数据就正常了;如图33和图34为发送命令,图33红色发送数值为4,图34中收到的为04,这样下发命令就成功了。
图33# ]6 `/ o! e8 {- H, e$ J( _
% `6 W5 Z1 T3 Z2 \5 X# B7 s
图34