你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

蓝牙温度采集器(基于CubeMX开发)  

[复制链接]
我是东哥 发布时间:2015-1-21 15:13
1. 方案介绍* z- N4 t/ t4 @  L# L
系统功能就是一个可以通过手机蓝牙实时查看远端采集器实时温度的系统。系统由采集器和Android手机端组成。采集器由STM32F072-Nucleo、蓝牙转串口模块(信弛达BM-S02模块)、温度传感器(MCP9808)组成。Android手机端负责和蓝牙模块通信,获取温度数据,并显示。
4 t3 S" i. k8 d2. 主要功能& a( b, t1 j+ O) `+ R$ x
系统重点除了完成一个特定的功能外,另一个目的在于研究CubeMX开发平台和RTOS,完成一系列通用的软件模块。已经完成了一些比较有用的模块,可供网友参考、指正。开发环境基于STM32 CubeMX+FreeRTOS(实际上是CMSIS OS的接口),所有的API调用都是基于CubeMX生成的Hal层API。完成的软件模块包括:; D) t( s$ l/ ?" @# q5 v+ f' q% ^
     基于uart2和FreeROTS-cli的命令终端,支持上下翻页和命令自动补全);$ `- m2 H) ?- _! q5 n0 {/ ~
     基于DMA的串口驱动,适合和需要大吞吐的wifi或蓝牙模块通信的场合;, l3 l( U* f. U. M
     以及一个简单的MCP9808的驱动(CubeMX hal的i2c接口先赞一个)。) e# }6 c& F/ {" n% I
3. 实施方案现场实施
, S! U* a: c7 C4 n% u IMG_20150121_120330.jpg # g) [  L* X. G" W% _) H0 v
蓝牙部分原理(出自官方文档)6 A! [6 E% `, V5 M4 N( v; v
bt.jpg
# O$ i/ v$ }# Q0 R0 oBRTS、BCTS、GND接地,UART_TX、UART_RX接Nucleo的UART1_TX(PA9)和UART1_RX(PA10),VCC接3.3V。MCP9808原理图! z) }# S$ b/ G2 o( B! r( B+ g. \
mcp9808.jpg ( v* y- o4 \, Z" Z) E! k
MCP9808地址可编程,上下拉都接了,实际贴片时会根据需要决定是否贴上电阻,这样可以在一个i2c总线上挂多个MCP9808。8 G$ D4 r6 n/ r7 D' ?
4. 软件实现
% C+ z) Z$ R7 z, u" S# A0 T/ qprintf到串口
1 y$ A& b& R% W# i3 h% Y  }重载fputc函数8 _- f: t' f/ Q) z: f# G
#define PRINT_BUF_SIZE 27 \7 c& F8 J% s; Z8 Y
static int read_bc = 0;
1 w1 ]" L$ Z& Jstatic int __serial_send(char *buf, uint16_t size). _& J# E9 G2 v7 v& C0 s
{
  v: |, |( |$ n+ z7 Z    HAL_UART_Transmit(&huart2, (uint8_t *)buf, size, 100);# `) j) w3 \( f6 Q# n! Q
    return size;* y# X( q" z% u0 H( P
}6 f) q0 @1 r. q5 n" ^7 F
int fputc(int ch, FILE *f)
) C5 [- ~( }' e5 ]& g( ]5 w6 o{- `9 U- F) `  p* {( f4 B; m
    /* Place your implementation of fputc here */
4 u. x! c6 K* o* t    /* e.g. write a character to the USART */
0 C6 ^; ?5 g3 r7 c    char buf[PRINT_BUF_SIZE];
  ~. _2 [8 H% [3 i9 R2 _' p5 }* E    buf[0] = ch;
% x2 q1 K1 i7 C6 @% g, a( s( g7 U7 S0 E0 U. |0 F
    if (ch == '\r') {
; v$ D; d9 g. }9 H3 h        read_bc = 1;( E, e" P! l* O. z3 |# \
    } else if (ch == '\n') {' S1 Q: C5 z4 C; ]6 x* L
        if (!read_bc) {
* M; {7 X: e0 x$ G$ c            buf[1] = '\r';
# A. E* Z, J, @$ X            __serial_send(buf, 2);1 l( x6 @  H, O' ]% v# J- d
            return ch;' O7 @8 t$ R6 J' L: E4 ~7 H
        }9 k' J% x0 Y$ `: V
        read_bc = 0;
1 P+ N* r& f; _$ [3 D* g1 o& }1 ?, d    } else if (read_bc) {
, t6 H1 q: A# t/ _        read_bc = 0;
! \+ k, ~7 ?$ [6 N    }5 {7 |( ?( j/ }9 X9 W0 a; O' _
" s: Y7 T; x0 \( N( u0 n
    __serial_send(buf, 1);
5 J3 |3 F) A! O    return ch;
, X4 Q) g* J* O0 Y! {}
! ~( i! g* L5 }& j
% G7 z- K2 R0 l* q% ~" j
- }+ f' N8 b% B; t5 Q$ v, m命令终端& ^: ^: j/ }; A7 \3 R9 G
2 U' h7 C1 o1 ?2 Q& J. D2 Y
) K% X+ S4 m3 \. h
基于uart2和FreeRTOS-Cli实现,由于Nucleo的虚拟串口直接和uart2是相通的,因此不需要再另接串口线。( F5 r/ Y8 ~/ R4 }9 R9 k4 J' N
总体流程; C) g0 G& C. k2 q
shell_process.jpg # H% a7 i+ B4 l% @
代码逻辑并不复杂,要讲有点麻烦,会把所有代码发出来,参考shell.c代码。8 n7 M4 V0 s$ S. r9 H8 B' M% m
为了命令补全,在FreeRTOS-Cli中加了一个函数,其余FreeRTOS-Cli代码未做修改。- K( I% W) E# ?, {1 N
实际效果如图所示: clipboard.png 9 H# Y) A. L7 ?! F: H  @1 `9 l/ |
显示不对齐是由于FreeRTOS的vTaskList函数实现导致,不影响调试。
- Q) D* F' V( @5 @$ F; r1 t1 eUART DMA传输
+ H3 ?! \' L7 Q主要用于大流量的数据通信,比如:wifi、蓝牙模块和MCU之间的通信。
# B& d9 a2 \# i- x2 ~网上有很多关于串口DMA的讨论,主要集中在UART DMA收上面,因为DMA接受需要固定长度的字节数,而这个限制对大部分应用是不可能做到的。所以,有人讨论了用有通信格式的数据包的方式,这个其实也不能完全满足所有需要,比如:这里用的蓝牙模块有的时候会打印一些系统信息,有的时候是远端传过来的数据,不太好区分。
! b" [* i* h' }( J9 O% K这里的DMA传输无需格式,无需固定长度,实现方式参考了这个帖子http://blog.csdn.net/jdh99/article/details/8444474,现在STM32F103上调通,后来移到Nucleo平台,主要困难在于CubeMX的API比较生疏,研究完具体实现才能明白怎么做。核心在于先启动DMA RX传输,然后在IT_IDLE中断中处理接受的数据包(不完整的DMA RX接收,但一次通信已结束)。9 l& B$ A6 @, J. h/ g+ a7 [  Z2 [
初始化) X6 {3 i) J2 o1 o9 X# G4 C
typedef struct {
* p% b6 h' a, A: O+ `6 k" q4 e2 P% [    uint8_t *rxbuf;/ E% ~# a6 f( d$ u. z  I
    uint8_t *txbuf;
+ e7 j4 @3 o  m+ O$ a9 ^    uint16_t rxbuf_size;
/ y; M! W( b; g$ ?2 ]7 S    uint16_t txbuf_size;1 q% }6 p; p* A; {- _; v
    uint32_t flag;
) p' D( ]2 _8 g( K3 N5 [: R; o# K8 B. B, \* a
    osSemaphoreId rx_semp_id, tx_semp_id;
: B+ H7 f8 a- K" @( b    UART_HandleTypeDef *UART_hd;
, O; j. E# X. ]# ^    //DMA_HandleTypeDef *DMARx_hd, *DMATx_hd;
* Q0 U1 B- m1 V. Y) j) g* x    serial_rx_callback rx;4 F5 E& T2 }& ~) S6 \
    int rxlen;
1 x3 C1 a1 H  y: {} uart_dma_info_t;' ]7 W, e, `2 m% Z2 F

& o0 U8 V0 x! H* Xint uart_dma_init(UART_HandleTypeDef *UART_hd,
( h$ s) i; p4 C+ T                serial_rx_callback rx, uint16_t rxbuf_size,
4 g+ Z3 V& Z6 a                uint16_t txbuf_size)
7 Q1 \; s+ W9 F! C{3 I- j. R2 S, ^( X( _
    int id;9 f# ]1 A: U! [- }
    uart_dma_info_t *info;
. R0 a4 x5 w/ E: e% x) B: x+ n    osSemaphoreDef(uart_rx_sep);
4 v* l/ F1 Q# H% r/ m: N    osSemaphoreDef(uart_tx_sep);
' p* w' ?: n6 W    osThreadDef(uart_rx_dma, uartRxTask, osPriorityHigh, 0, configMINIMAL_STACK_SIZE+100);: o2 o) @- ]* g& {
. T' N5 K' N& [2 I6 e( B
    assert_param(UART_hd != NULL);
5 {1 Q& R: s7 d, E( x1 C- G) v/ |) T  n/ Q
    id = __get_uart_id(UART_hd->Instance);3 o( G- f2 h7 d- X1 j6 |
    assert_param(id >= 0 && id < MAX_UART_NUM);$ [+ ?7 p. v9 c1 P2 j, ~& \9 z
2 [& }& y8 Z, h2 q5 A
    info = &uart_info[id];3 c: w1 e- r" l# p/ m

/ s/ m. m. C9 K+ p/ j3 [3 B% X    info->rxbuf_size = rxbuf_size;. Y) ~3 Z) ~% R3 I; d/ j
    info->txbuf_size = txbuf_size;$ }% E9 p# S3 N2 A; K
    info->UART_hd = UART_hd;$ l0 w* N$ l; N: [" g
    info->rx = rx;
5 b7 b; a6 s5 S1 Q3 b! }( m
5 ?3 u( S$ J6 j) w  ]4 d    if (rxbuf_size > 0) {, y  D- N8 h0 `* Y
        assert_param(info->UART_hd->hdmarx);
# Z& M9 u, U0 Q& \% D        info->rxbuf = pvPortMalloc(rxbuf_size);
7 @1 n0 }5 o6 Z        assert_param(info->rxbuf);# C6 _% t1 j. m% V/ o# A1 a
        osThreadCreate (osThread(uart_rx_dma), info);   
3 t/ d' ]# z& }- F        info->rx_semp_id = osSemaphoreCreate(osSemaphore(uart_rx_sep), 1);
, M  `) l% {4 _* {% X4 v9 o4 G4 W! r        assert_param(info->rx_semp_id);6 A1 ?, O0 ?. T5 e& g3 `9 b, v+ f9 O
        osSemaphoreWait(info->rx_semp_id, osWaitForever);//skip the first time
6 H. K& e( ^9 W  A9 j9 I
. g0 q# {* u" r9 t9 X1 K, R  h) _$ o4 D. [9 E% h' y2 ^7 B& J
        __HAL_UART_DISABLE(info->UART_hd);+ w- _5 n( z0 \, Y2 o8 i, A
        info->UART_hd->Instance->CR3 |= UART_DMA_RX_ENABLE;
9 L9 V) f8 C+ t3 |        __HAL_UART_ENABLE(info->UART_hd);
6 G2 Q! {- L& S& N7 }
& q: J/ T$ [0 t7 r& l  Y        HAL_DMA_Start(info->UART_hd->hdmarx, (uint32_t)&info->UART_hd->Instance->RDR,
' x. ]7 }( I# q6 ]: J                    (uint32_t)info->rxbuf, info->rxbuf_size);  . _9 d* r4 Q3 R+ C
    }1 H2 u, G% D; ^0 s
    if (txbuf_size > 0) {) V" i* |! r! e' x! ^* l  y
        assert_param(info->UART_hd->hdmatx);
6 N0 l9 o) u3 K6 }( v) F        info->txbuf = pvPortMalloc(txbuf_size);  [* F, M  b# d# `
        assert_param(info->txbuf);
- b, ~- R+ O; E$ R9 V  f4 R        info->tx_semp_id = osSemaphoreCreate(osSemaphore(uart_tx_sep), 1);
: N2 {, X; c1 X2 B        assert_param(info->tx_semp_id);
, V  p( L+ ]6 t  i$ u        osSemaphoreWait(info->tx_semp_id, osWaitForever);// skip the first wait. E! a  h* N( b+ `8 _  E

$ R9 S" f$ t9 s+ a9 p7 K0 X        info->UART_hd->hdmatx->XferCpltCallback = uart_dma_send_finsh_cb;
" u5 A% @( X9 O        info->UART_hd->hdmatx->XferErrorCallback = uart_dma_send_err_cb;6 v; G" ?- f: T) x1 M. p' b" X
        info->UART_hd->Instance->CR3 |= UART_DMA_TX_ENABLE;0 q; W7 ?/ I, B8 Z2 v; r! N
    }" D  k: t  D" y& ]/ b' n; y/ B

. O+ D, e# H0 x* m$ C    return id;4 t9 A0 @4 a) [
}
. K# |" t- c" H9 b9 j. D% A
' c, s! v) {- \9 t: J& z: R+ `中断处理8 a$ n1 l  B  k( y+ V% G+ B2 c( N0 U; i. l
void USART1_IRQHandler(void)
) i; r, Q5 J' _+ t5 m; W{& ^* k) r1 S1 I* y
  /* USER CODE BEGIN USART1_IRQn 0 */4 S" H8 N. Y# P. y5 U# w
7 ?5 z; |+ _1 ]# y
  /* USER CODE END USART1_IRQn 0 */8 C2 u$ M4 q) m. W
  HAL_UART_IRQHandler(&huart1);
. R; `+ d6 |" K1 B( |* S  /* USER CODE BEGIN USART1_IRQn 1 */9 i) e  E0 B1 x6 e
   if (__HAL_UART_GET_IT(&huart1, UART_IT_IDLE) != RESET) {( r# E" S* s+ X3 b7 e& J* G
        uart_dma_recv_isr(uart1_fd);
8 ?# J; l3 M0 P* F3 w5 I  |, G    }1 V; g  O7 s0 w

9 J4 J( L: x0 X+ z; u  /* USER CODE END USART1_IRQn 1 */4 s" i/ l, [+ }0 o
}
$ x2 y- S4 }+ }6 r, z0 X! |) v8 T8 g5 d4 x6 Y% \+ I
void uart_dma_recv_isr(int fd)5 p, Z6 f5 p* _5 I6 X9 `
{
* v4 S) K  R8 J; B    uart_dma_info_t *info;
5 S$ J! ?- y9 J6 N7 {    uint32_t temp = 0;/ F6 P, ?# E; @: S: Y
8 V* p4 W0 u( w5 [! A* G6 v
    assert_param(fd >= 0 && fd < MAX_UART_NUM);
6 W6 K! w1 g3 H# f$ n: y1 V% u" U8 i8 q+ H, o) M
    info = &uart_info[fd];  O1 C. W% q7 T8 W, q! r( `
    assert_param(info->UART_hd->hdmarx);0 u& W  c! d3 X& R7 N

3 b, k% m. ^2 J    __HAL_UART_CLEAR_IT(info->UART_hd, UART_CLEAR_IDLEF);
' N: b* C. m) U/ C$ T    SET_BIT(info->UART_hd->hdmarx->ErrorCode, HAL_DMA_ERROR_NONE);: ^/ X! z, B# B

/ V* l' G, l0 r: s  Y7 v    /* Change the DMA state */9 l* T8 r' b4 m/ H6 M
    info->UART_hd->hdmarx->State = HAL_DMA_STATE_READY;    % P: a- i( A; d( ^9 v& i/ Y
    /* Process Unlocked */! C% V/ m' t) }  S8 P5 |/ l' d
    __HAL_UNLOCK(info->UART_hd->hdmarx);
" m" B6 u5 J/ K  f* W
, l. U( h% N6 C& f4 @4 ~5 M+ G    __HAL_DMA_DISABLE(info->UART_hd->hdmarx);$ `; f: r3 X: D- P$ ^7 d, Q- }
( z* ]( |8 U3 N7 l( p1 E1 ?
    temp = info->rxbuf_size - info->UART_hd->hdmarx->Instance->CNDTR;
, N  r: Z- c9 Y! o1 Z    if (temp > 0) {
. H. e% {! Y5 r" ~. E9 Q* \. y( s        info->rxlen = temp;, Z; ~8 F" p( H) [1 j  d6 a
        osSemaphoreRelease(info->rx_semp_id);
& _/ D* K7 d$ S% c2 N6 X    }
- n0 m9 c  Z! H8 q
2 E, N( ^  B1 `    HAL_DMA_Start(info->UART_hd->hdmarx, (uint32_t)&info->UART_hd->Instance->RDR,
) }( S# v  Q, a! o; m( K) v2 G  y                    (uint32_t)info->rxbuf, info->rxbuf_size);
0 I# R$ ^, x$ O+ \' Z}
5 `/ [& z  J* v4 _& `
% n5 I7 ]; u8 I8 W1 O3 s0 r接收处理线程
/ ]3 Y% M. T. B/ y" E% t$ Ystatic void uartRxTask(void const *args)4 [' h- D: J; S; Q$ @- E
{( H6 u! w+ q. m- C
    uart_dma_info_t *info = (uart_dma_info_t *)args;% i) l; ]/ O- q) g$ c# J1 f
& F) m/ o6 s0 i' j0 G# |
    while (1) {+ o! u" E+ R# t- X% T9 |
        osSemaphoreWait(info->rx_semp_id, osWaitForever);
7 a+ \1 W- M: J7 A        if (info->rx && info->rxlen > 0) {- `, d5 k/ F* L. r5 j
            info->rx(info->rxbuf, info->rxlen);. T, M+ V( e# R4 z/ T1 ?
        }
  }) Q  u* Q* k3 g    }
$ @+ I1 [8 ?: v& h; b}- X+ B1 }0 H/ h
0 ?* A( Y2 d( ?; |) v! @
7 r" Y; H' y6 {. Z; o
具体实现看附件把,贴代码也不太容易说清楚。3 s- P5 B2 `6 e$ X: B6 |3 ?
& b0 F% G8 `9 h% c  w4 R2 k
MCP9808驱动
1 c% \7 N! M$ M; k做了一些封装,但基本上只要调用HAL_I2C_Mem_Read就可以搞定了。
0 z! o2 c6 v8 U9 K5 ^& d! x# `( m. ?9 I# q) a
MCU全部的代码见附件4 w' ?+ L4 M1 S% W! ]  N$ m0 e6 ]
f0fmw.zip (6.88 MB, 下载次数: 2620)
收藏 3 评论23 发布时间:2015-1-21 15:13

举报

23个回答
我是东哥 回答时间:2015-1-21 19:05:37
harvardx 发表于 2015-1-21 18:46! U* U& I) A% t" p4 f
不错的东东 .app能写好就ok 单片机方面没有难点 ,毕竟是透传,4年前我做过. 距离不理想 .改成wifi透传了 ...

8 V5 k% k5 N' \$ \( ~嗯,其实这个东西主要是前端和后端难搞,前端指探头部分,如果就是用i2c芯片读读,确实没什么难度,但要是精度高,并且探头要非常小,这个难度一下子就大起来了,像这类的要求在实验室设备上要求比较多。如果是民用,重点还是app要搞好,单片机这里比较简单。0 Q: ~' C( C7 a8 Z
这次的东西其实硬件和功能都比较简单,重点还是研究基于STM32CubeMX的开发,现在毕竟文档和案例都比较少,弄个这个给大家参考参考。
帅帅丽刃 回答时间:2017-3-31 00:01:08
你好,请问您对基于stm32的lora点对点通信并采集stm32内部传感器数据的实现有了解吗,楼主,可以加一下您QQ或者微信吗,谢谢
星辰一方 回答时间:2015-1-26 21:06:40
支持分享!看到你图片还以为楼主准备用iPhone蓝牙呢,至今没找见有没有iPhone能用的app和蓝牙模块通讯……
沐紫 回答时间:2015-1-21 15:31:42
抢沙发啦~~~~
kqh1120 回答时间:2015-1-21 16:21:52
还有板凳呢
harvardx 回答时间:2015-1-21 18:46:25
不错的东东 .app能写好就ok 单片机方面没有难点 ,毕竟是透传,4年前我做过. 距离不理想 .改成wifi透传了
harvardx 回答时间:2015-1-21 19:12:32
不错 还是要支持. 哈哈.
我是酱油哥 回答时间:2015-1-22 08:40:33
顶一下   
埃斯提爱慕 回答时间:2015-1-22 08:42:11
提示: 作者被禁止或删除 内容自动屏蔽
党国特派员 回答时间:2015-1-22 11:09:18
1.png
天天晓宇 回答时间:2015-1-22 13:24:44
好东西!
1407W 回答时间:2015-1-22 16:19:28
有雨水检测模块就更好了 现在急需雨水检测模块的原理图和代码
lkl0305 回答时间:2015-1-22 19:19:57
多谢分享
lkl0305 回答时间:2015-1-22 19:21:04
多谢分享
wyxy163@126.com 回答时间:2015-1-25 14:42:14
提示: 作者被禁止或删除 内容自动屏蔽
我是酱油哥 回答时间:2015-1-26 20:50:45
顶一下   
12下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版