单片机

一、血压测量原理

血压的概念就是血液流经血管壁时的压力。由心脏出来的血液,需要有推力,才能绕行身体一周,心脏就是借着不停的收缩、放松,将血液推送前进。血压有两种,一是收缩压:是当心脏收缩将血液打到血管所测得的血压,另一是舒张压:是心脏在不收缩所测得的压力。当袖带的压力等于血压时,血液开始可以流通而产生所谓的袖带声,这时候表现为收缩压,从这一刻开始做记录,直到最后袖带声音消失的时候,记录此点即为舒张压。

二、电子血压计工作原理

系统框图如图1所示。系统由恒流源、压力传感器、放大电路、带通滤波、二次放大、血压脉冲触发、液晶驱动器、键盘语音电路和单片机组成。

基于MSP430F449D的电子血压计设计
图1 电子血压计原理图

单片机主要原理为:PWM输出控制气泵充气漏气调整袖带内气压;一路ADC采样袖带内气压直流分量以便取得收缩压和舒张压;一路ADC采样袖带内气压交流分量经分析计算后确定收缩压和舒张压的瞬态时间位置;接收血压脉冲信号触发ADC工作;将计算出的收缩压和舒张压结果输出至LCD显示并进行数值的语音提示。

三、硬件设计

1 MSP430FF449D单片机主控电路

本系统主控电路如图2所示,主要由MSP430F449芯片、JTAG接口电路、时钟发生电路、时钟输出电路、复位电路、PWM波输出电路、供电电路等组成。其中JTAG用于下载和调试程序,PWM波输出电路用于控制气泵。当测量血压时,先充气至200mmHg高,再慢慢以每秒约下降5mmHg的速度放气。实现自动测量血压。

基于MSP430F449D的电子血压计设计
图2 电子血压计主控电路

2 血压传感电路

如图3所示,本电路采用BP01型压力传感器和运放MAX4472。BP01型压力传感器是为检测血压而专门设计的,主要用于便携式电子血压计。它采用精密厚膜陶瓷芯片和尼龙塑料封装,具有高线性、低噪声和外界应力小的特点;采用内部标定和温度补偿方式,提高了测量精度、稳定性和重复性,在全量程范围内,精度为±1%、零点失调不大于±300μV。MAX4472是MAXIM公司的一款集成了四个运算放大器的低功耗放大芯片。本系统中内部集成运放A接恒流源,为压力传感器提供恒定的电流,运放B和运放C,运放D组成差分输入、单端输出放大电路,直接输入ADC0监视血压直流分量。

基于MSP430F449D的电子血压计设计
图3 血压传感电路

3 滤波和放大电路

如图4所示,电路由滤波和放大两部分组成。其中MAX267是MAXIM公司出产的一个集成滤波器,可以构成低通、带通、高通、等多种方式,使用灵活,性能远远优于采用集成运放组成的滤波电路。

基于MSP430F449D的电子血压计设计
图4 滤波和放大电路

MAX4471是MAXIM公司的一款低功耗的放大器。MAX9028是MAXIM公司的一个低功耗的比较器。滤波电路采用MAX267构成带通滤波器(允许0.8~38Hz的信号通过),滤掉信号中的直流成分和电源以及皮肤与袖带摩擦的高频噪声和工频干扰,然后经过MAX4471进行进一步放大,得到单片机匹配的电压信号,进入ADC2,监视血压的交流分量。同时该信号通过低功耗比较器MAX9028转换成脉冲信号,触发ADC1工作。

4 日历时钟和存储电路

如图5所示,由EEPROM24C256和日历时钟芯片PCF8563组成。24C256是一款低电压、串行接口,容量为256K的存储器,用于存储测量的血压值。PCF8563是PHILIPS公司推出的一款工业级内含I2C总线的具有极低功耗的多功能时钟/日历芯片。用于提供测量血压时的时间和日期,以便于以后进行查询使用。方便于使用者对自己一段时间的血压有个清晰的记忆。实用性强,克服了一些电子血压计的不足。

基于MSP430F449D的电子血压计设计
图5 日历时钟和存储电路原理图

5 键盘和显示电路

如图6所示,由键盘电路和液晶显示电路两部分组成。液晶显示电路采用ZJM12864BSBD,这是一款低功耗的点阵图形式LCD,显示格式为128点(列)×64点(行),具有多功能指令,很容易与MPU相连。其中键盘电路采用独立式按键,有7个按键,分别为测量、mmHg/kPa转换、记忆、设置、上翻、下翻、删除。可以进行日历时钟的设置,进行报警参数的设置,进行血压的测量值的存取和删除等功能。LCD可以显示收缩压,舒张压,当前的时间和日历;在查询状态时可以实现以往测量血压的日期、时间、测量值,同时可以通过软件编程实现历史数据的图形化显示(例如画出血压波动曲线),方便直观。

基于MSP430F449D的电子血压计设计
图6 键盘和显示电路

6 语音报压和报警电路

本电路如图7所示,主要由集成语音芯片ISD2560组成。ISD2560是Winbond公司生产的一款具有较强功能的语音录放芯片,是一种永久记忆型语音录放电路,录音时间为60s,可重复录放10万次。该芯片采用多电平直接模拟量存储专利技术,能够非常真实、自然地再现语音。通过事先录制好的声音,实现血压测量值的自动声音提示,如果血压高出正常血压的上下限值,还会发挥报警,提醒使用者就医。

基于MSP430F449D的电子血压计设计
图7 语音报血压,报警电路

四、结语

电子血压计具有小型化、低功耗、智能化程度高的优点,在使用上有便携和易操作的特点,从而呈现出家用化的趋势。本文给出了完整的携带式电子血压计硬件设计方案,并基于MSP430F449为控制核心辅以压力传感器和外围的模拟电路以及LCD 驱动芯片实现了电子血压计的设计。此设计用的芯片大部分都是低功耗的芯片,便于使用电池供电。同时设计实现了人性化,智能化的要求,就像一个家庭护士,对与高血压患者以及中老年人来说十分方便,可以转化为实际产品,故有较高的实用价值。

来源: 中电网

围观 4
12

对于传统流量检测系统而言,其多数选用的是电磁传感器,而电磁传感器易受外界磁场的影响而导致流量计量的不正确,MSP430单片机作为一种超低功耗的16位混合信号处理器,其在流量检测中的应用得到了越来越广泛的应用,因此,本文重点就基于MSP430单片机的流量检测仪的设计进行了研究。

1.以MSP430单片机为基础的流量检测仪的工作原理分析

考虑到流量检测仪低功耗等方面的特性,控制器选用的为MSP430F149,具体而言,此流量检测仪的工作原理如下:
当液体经过流量检测仪的过程中,检测仪内部的旋转磁盘进行转动,因而旋转磁盘上所设置的磁钢会触发传感器,并使其发出极为微弱的电信号,通过将此信号进行逐级放大和滤波之后,信号通过输出进入到检测仪的CPU中,CPU计数器对其进行输入,而后系统周期对脉冲个数进行读取,并借助于相应的软件对流量进行计算,后经处理形成所谓的差动信号,此差动信号以脉冲的形式传送至显示器中进行显示,这样其参数及流量信息可通过MSP430F149的I/O接口进行输入/输出。

2.基于MSP430单片机的流量检测仪的设计分析

2.1 电路的设计

流量检测仪的控制器采用的是TI公司所生产的MSP430149单片机,通过对流量检测仪的几个主要功能模块进行设计,系统的具体结构如图1所示。

流量检测中MSP430单片机的应用分析

1)温度检测模块,此模块主要包括了温度传感器与差动放大器,此模块通过温度传感器将信号传送至差送放大器中,信号经放大后输入至MSP430F149之中,经单片机的A/D转换口对信息及数据进行相应的处理及保存。本系统所采用的温度传感器其热电阻为PT100.

2)流量检测模块,此模块包括流量传感器与整形电路,当一定量的液体经过传感器时,传感器会产生脉冲,这样,通过所得脉冲数即可进行流量的计算。系统所采用的流量传感器为WG系列的韦根传感器,其原理如下:传感器中的合金材料具有磁性双稳态功能,这样受到外磁场激发后,其磁化方向会瞬间进行翻转,并在检测线圈中产生电信号,从而实现了磁电之间的转换。

3)通信模块,此模块主要包括了NRF-401无线通信电路以及RS485通信接口。对于RS485接口而言,其芯片采用的是TI公司生产的SN75LBC184,其使用的为单一电源VCC,3-5.5V电压范围之内均可正常进行工作,并能有效实现TTL到485间的转换。

对于无线串行接口电路而言,其采用的是NRF401无线通讯芯片,并应用了FSK调制解调技术,因此工作过程中的最高速率可达20kb/s.此外,还可对发射功率进行调整,发射过程中的最大功率为+10dBm.

4)对于液晶显示模块而言,其主要功能即对当前液体的流量进行显示。

2.2 软件的设计

2.2.1 模块的设计

对于流量检测仪而言,其设计过程某些程序的执行需要通过实际时间来进行调度。而此机制要想实现需要借助于MSP430单片机中所具有的TIMER-A及TIMER-B等实现。根据本系统的设计,需要定时器在32768Hz的条件下每秒进行一次中断,因此,需要对TIMER-B进行以下方面的设置:
- 将时钟源定义为ACLK;TBCTL=TBSSEL0+TBCLR
- 允许中断定时器;TBCCTL0=CCIE
- 将定时器的定时时间设为1s;TBCCR0=0X7FFF
- 在增计数模式进行定时器的工作;
- TBCTL1=MC0

这样,即可实现定时器1s中1次的中断服务程序,在此程序中进行相应定时器的设计,以便进行计时相关操作。系统采用了热电阻PT100.由于MSP430单片机自带ADC12模块,因此可将所采集的温度通过A/D进行转换。为了对温度及其他模拟量进行即时处理,系统采用了序列通道多次转换模式。转换完成后结果会存放于ADC12MEM之中。

对于系统的软件而言,其包括上层及下层模块两部分软件,其中,上层收到中心命令后可借助于射频无线通信方式对下层进行命令的发送,并进行计时。若下层无数据返回,一旦超时上层会重新进行命令的发送。若3次以上仍无数据返回,则将被认为下层工作出现异常,并向中心提交异常信号。由于MSP430单片机仅存在一个串口,而上。下层模块需2个串口,而第2个串口需要借助于定时器A所具有的捕获/比较功能来实现。

2.2.2 无线通信协议

系统的通信协议包括3层:一是物理层,主要是通过NRF401模块实现的;二是数据链路层,此层主要负责进行无线数据传输的提供;三是应用层,进行数据的发送时,需将此层所传送来的较长数据帧进行拆分,并进行包头与校验和,而后再重新进行打包并发送。

由于NRF401灵敏度极高,因此,若无数据的传输时,其数据的输出脚将会存在杂波的输出,此类杂波将会受到MCU串口的接收和处理。四个字节的0×CC加上一个字节的0×F0可保证数据帧到达之前双方之间通讯的同步实现。系统协议采用的是两个字节的帧头,即两个0×55,其加上起始和停止位之后,实际过程中发送的将为0101010101,因此可有效保证数据获得确认。此外,由于十个字节数据后为校验和,因此采用16位的crc校验可确保数据传输过程的准确性。此时接收方会对crc及校验和进行比较,若不同说明传输过程发生错误,此时接收方会讲错误帧编号进行记录,待所有数据发送结束后,可返回错误编号,并要求重新进行发送。

若所有数据均接收正常,则会确认发送正确。

3.结语

MSP430单片机由于其功耗超低等特点,因而在流量检测中得到很好的应用。

通过基于MSP430单片机的流量检测仪的设计,根本上解决了传统流量检测过程中精度问题以及检测仪受液体影响而寿命降低等多方面弊端,因而在流量检测方面具有十分良好的发展前景。

来源: 电子产品世界

浏览 1 次
14

1 前言

在进行 USB 开发的过程中,有多个客户反馈,USB 传输数据时出现卡顿现象。本文将针对这
一问题进行分析。

2 问题分析

这几个客户问题现象基本差不多,采用 STM32 作为 Device 设备,在与上位机或者 PC 端双向通讯一段时间后,从 Device 端到 Host 端的数据能够正常,而从 Host 端到 Device 端的数据异常,也就是说,STM32 在一段时间后不再能正常接收数据,但是,如果只是单向通信,就一直都是正常的。

这几个客户,有用 STM32F2 的,也有用 STM32F4 的,有用 CDC 类的,也有用作 HID 设备的,但都使用了 Cube 库。

下面就具体问题以其中一个客户使用 STM32F411 的 USB CDC 类的案例来分析问题,现象如
下 USB 通讯数据(CDC 类):

STM32单片机USB传输数据时出现卡顿现象

展开 Data Out 数据:

STM32单片机USB传输数据时出现卡顿现象

分析上图发现,并不是 Host 端没有向 Device 端发送 Data Out 数据,而是确实发送了,但被 Device
端 NAK 了。那么为什么会被 NAK 呢?

通过在调试下查看寄存器,我们发现当出现问题时,Data OUT 对应的端点 1 是处于关闭状态,那么为
什么端点 1 会关闭?查看 STM32 端的接收代码:

usbd_cdc_if.c:

static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
   /* USER CODE BEGIN 6 */
   //USBTask_ReceiveMsg(Buf, *Len); //UserRxBufferFS
   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
   USBD_CDC_ReceivePacket(&hUsbDeviceFS);
   return (USBD_OK);
   /* USER CODE END 6 */
}

如上代码,在 MCU 端接收到赖在 Host 端的数据后不做任何处理就立即接收下一次数据传输,问题是,这里对接收到的数据啥也没有做,居然还会出现 Data Out 端点关闭的问题,那么 OUT 端点到底是怎么关闭的呢?我们接下来看子函数:

CDC_Receive_FS() ->USBD_CDC_ReceivePacket() ->USBD_LL_PrepareReceive() -
>HAL_PCD_EP_Receive() ->USB_EPStartXfer()

最后在 USB_EPStartXfer 函数中有发现再次使能 OUT 端点的代码:

//…
 else /* OUT endpoint */
 {
    /* Program the transfer size and packet count as follows:
    * pktcnt = N
    * xfersize = N * maxpacket
    */
    USBx_OUTEP(ep->num)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_XFRSIZ);
    USBx_OUTEP(ep->num)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT);
    if (ep->xfer_len == 0U)
    {
       USBx_OUTEP(ep->num)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & ep->maxpacket);
       USBx_OUTEP(ep->num)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (1U xfer_len + ep->maxpacket -1U)/ ep->maxpacket;
       USBx_OUTEP(ep->num)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (pktcnt num)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & (ep->maxpacket *
      pktcnt));
    }
    if (dma == 1U)
    {
       USBx_OUTEP(ep->num)->DOEPDMA = (uint32_t)ep->xfer_buff;
    }

    if (ep->type == EP_TYPE_ISOC)
    {
       if ((USBx_DEVICE->DSTS & ( 1U num)->DOEPCTL |= USB_OTG_DOEPCTL_SODDFRM;
       }
       else
       {
          USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
       }
    }
     /* EP enable */
     USBx_OUTEP(ep->num)->DOEPCTL |= (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
 }

也就是说,在调用这个函数之前这个 OUT 端点原本就是关闭的?真的吗?这怎么跟我们理解的不一样?
不应该是 OUT 端点一旦打开就一直开着的吗?带着这些疑问,我们查看 STM32F411 的参考手册,终
于在 22.17.6 Operational model 一节中找到这么一幅图(由于 CDC 类数据传输采用的是 BULK 传输):

STM32单片机USB传输数据时出现卡顿现象

如上图,MCU 对 BULK 类型的 OUT 数据处理例程大体如下:

1> Host 端试图向一个端点发送 OUT token;
2> 当 Device 端的 USB 外设接收到这么一个 OUT token 后,如果 RXFIFO 空间足够,它将数据包存
储到 RXFIFO 中;
3> 在将数据包内容存储到 RXFIFO 后,USB 外设将产生一个 RXFLVL 中断(OTG_FS_GINTSTS);
4> 在接收到 USB 数据包的个数后(PKTCNT),USB 核将内部自动将这个 OUT 端点的 NAK 为置 1,以
阻止接收更多数据包;
5> 应用程序处理 RXFLVL 中断和从 RXFIFO 读取数据;
6> 当应用读取完所有数据(等于 XFRSIZ)后,USB 核将产生一个 XFRC 中断(OTG_FS_DOEPINTx);
7> 应用处理这个 OTG_FS_DOEPINTx 中断并通过 OTG_FS_DOEPINTx 的中断为 XFRC 来判断传输
完成;

从上面步骤中的第 4 步中可以看出,当 USB 核收到来自 Host 端的数据后会自动将 OUT 端点关闭,这
也就是为什么在接收函数中在接收下一次数据时要再次使能这个 OUT 端点的原因。因此我们大体可以
判断出在 OUT 数据传输的过程中,USB 核会禁止端点->打开端点->禁止端点…如此不断循环中;那么
问题到底出现在哪里呢?会不会在 USB 核自动关闭端点后就没有再次成功打开?带着这样的怀疑心态
逐句查看代码,最终在接收函数的子函数中发现这么一段代码:

HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t*pBuf, uint32_t len)
{
    USB_OTG_EPTypeDef *ep;

    ep = &hpcd->OUT_ep[ep_addr & 0x7FU];

    /*setup and start the Xfer */
    ep->xfer_buff = pBuf;
    ep->xfer_len = len;
    ep->xfer_count = 0U;
    ep->is_in = 0U;
    ep->num = ep_addr & 0x7FU;

    if (hpcd->Init.dma_enable == 1U)
    {
      ep->dma_addr = (uint32_t)pBuf;
    }

   __HAL_LOCK(hpcd);
  
    if ((ep_addr & 0x7FU) == 0U)
    {
       USB_EP0StartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
    }
    else
    {
       USB_EPStartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
    }
    __HAL_UNLOCK(hpcd); 
    return HAL_OK;
}

之所以会怀疑这里,这是客户提供了一个信息,单向通信的时候就不会有问题!这是因为在发送数据
时,发送函数的底层函数内也使用到了这个互斥锁:

CDC_Transmit_FS() -> USBD_CDC_TransmitPacket() -> USBD_LL_Transmit() ->
HAL_PCD_EP_Transmit() :
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t*pBuf, uint32_t len)
{
    USB_OTG_EPTypeDef *ep;

    ep = &hpcd->IN_ep[ep_addr & 0x7FU];
    /*setup and start the Xfer */
    ep->xfer_buff = pBuf;
    ep->xfer_len = len;
    ep->xfer_count = 0U;
    ep->is_in = 1U;
    ep->num = ep_addr & 0x7FU;

 if (hpcd->Init.dma_enable == 1U)
 {
    ep->dma_addr = (uint32_t)pBuf;
 }

 __HAL_LOCK(hpcd);

 if ((ep_addr & 0x7FU) == 0U)
 {
    USB_EP0StartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
 }
 else
 {
    USB_EPStartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
 }

 __HAL_UNLOCK(hpcd);
 return HAL_OK;
}

接收处理数据时,底层是通过接收中断回调上来的,但发送时,我们往往将发送放到 main 等用户函数
中。这两个是不一样的,一个在中断内,一个在中断外,优先级别是不一样的,优先级不一样就有可
能导致资源冲突;

我们进一步查看__HAL_LOCK()宏定义:

 #define __HAL_LOCK(__HANDLE__)                                          \
                       do{                                                                           \
                               if((__HANDLE__)->Lock == HAL_LOCKED)  \
                               {                                                                       \
                                   return HAL_BUSY;                                      \
                               }                                                                       \
                               else                                                                  \
                               {                                                                       \
                                   (__HANDLE__)->Lock = HAL_LOCKED;    \
                               }                                                                        \
                           }while (0U)

若__HAL_LOCK(hpcd);失败则直接返回 return HAL_BUSY 的。为了验证在接收过程中是否
__HAL_LOCK 失败,我们引进全局变量 Lock_Flag,在发送函数中若成功 LOCK 则设置
Lock_Flag=1,UNLOCK 后则复位为 0:

uint8_t Lock_Flag =0;
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t*pBuf, uint32_t len)
{
    USB_OTG_EPTypeDef *ep;

    ep = &hpcd->IN_ep[ep_addr & 0x7FU];

    /*setup and start the Xfer */
    ep->xfer_buff = pBuf;
    ep->xfer_len = len;
    ep->xfer_count = 0U;
    ep->is_in = 1U;
    ep->num = ep_addr & 0x7FU;

    if (hpcd->Init.dma_enable == 1U)
    {
       ep->dma_addr = (uint32_t)pBuf;
    }

    __HAL_LOCK(hpcd);
   Lock_Flag =1;

    if ((ep_addr & 0x7FU) == 0U)
    {
       USB_EP0StartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
    }
    else
    {
       USB_EPStartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
    }

    __HAL_UNLOCK(hpcd);
    Lock_Flag =0;
    return HAL_OK;
}

接下来在接收函数中对全局变量 Lock_Flag 值进行判断,若为 1 则锁死程序,因为在 Lock_Flag=1 时,
则表示发送函数中已经获取了锁没有释放,此时若再去获取则会导致失败从而返回 HAL_BUSY;这里通
过锁死代码以便判断这种情况:

HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t*pBuf, uint32_t len)
{
    USB_OTG_EPTypeDef *ep;

    ep = &hpcd->OUT_ep[ep_addr & 0x7FU];

    /*setup and start the Xfer */
    ep->xfer_buff = pBuf;
    ep->xfer_len = len;
    ep->xfer_count = 0U;
    ep->is_in = 0U;
    ep->num = ep_addr & 0x7FU;

    if (hpcd->Init.dma_enable == 1U)
    {
       ep->dma_addr = (uint32_t)pBuf;
    }

   if(Lock_Flag ==1)
   {
      while(1);
   }
    __HAL_LOCK(hpcd);

    if ((ep_addr & 0x7FU) == 0U)
    {
       USB_EP0StartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
    }
    else
    {
       USB_EPStartXfer(hpcd->Instance , ep, hpcd->Init.dma_enable);
    }
    __HAL_UNLOCK(hpcd);

    return HAL_OK;
}

通过调试,当出现问题时,程序果然被锁死在这个 while(1)了,这也证明了正是这个互斥锁所致。因此,
我们大体可以判断出现问题时流程大致如下:

1> 在 mian 函数中发送数据 CDC_Transmit_FS()
2> USBD_CDC_TransmitPacket()
3> USBD_LL_Transmit()
4> HAL_PCD_EP_Transmit()
5> __HAL_LOCK(hpcd); 此时成功获取互斥锁
6> 恰好此时有一个接收中断,由于 USB 中断具有优先级,跳转到接收中断内执行;同时,USB 核会
自动关闭 OUT 端点;
7> HAL_PCD_DataOutStageCallback()
8> USBD_CDC_DataOut()
9> CDC_Receive_FS()
10> USBD_CDC_ReceivePacket()
11> USBD_LL_PrepareReceive()
12> HAL_PCD_EP_Receive()
13> __HAL_LOCK(hpcd); 此时获取互斥锁失败导致返回,接收函数在 OUT 端点没有再次打开就已经提前结束,导致接收循环无以为继。

3 解决方案

知道了问题原因所在,接下来解决问题就相对来说比较容易的了。由于此问题是发送与接收处于不同优先等级导致资源冲突所致,那么我们可以将发送也放到与 USB 接收中断相同的中断等级中去,例如可以利用 USB 的 EOPF 中断,在开启 EOPF 中断后,在此中断内发送数据,这样发送与接收中断就处于相同等级了,EOPF 每 1ms 触发一次,速度完全可以。当然开启一个相同优先级的定时器来做发送数据也是可以,只不过定时器间隔得控制好。

此外,其实此问题是出现在 Cube 库的低版本中,例如 CubeF4 V1.5.0 和 CubeF2 V1.3.0 中都存在,但是在最新本的 CubeF4 V1.16.0,CubeF2 V1.6.0 版本中此问题得到了解决;此问题虽然后来发现是版本太旧所致,但从多个客户反馈此问题来看,此问题依然不失为一个很好的参考和教训。

来源: 21ic.com

围观 16
28

1、引言

随着电子工业的发展,电子元器件急剧增加,电子元器件的适用范围也逐渐广泛起来,在应用中我们常常要测定电容的大小[1]。因此,一种简单、实用的电容测试工具在实际中具有一定的实用价值。一般元件参数的数字化测量是把被测参数转换成频率后再进行测量[2],本设计采用555为核心的振荡电路,将被测电容值转化为频率,并利用AT89S51处理器测量出频率,再通过该频率值计算出电容参数值。

2、系统的原理框图

系统主要采用了555定时器构成的RC振荡电路和单片机技术。设计思路:被测电容C通过RC振荡转换成频率信号f,送入单片机测频,对该频率进行运算处理求出被测电容的值,并送显示器显示。系统框图如图1所示,其主要由测量电路和控制电路两部分组成。当接入被测电容后,由555定时器构成RC振荡器产生方波信号,把此信号通过接口传到AT89C51单片机I/O口上,对此方波信号进行测频,通过软件编程,计算出得到被测电容值,由LCD1602液晶显示。

基于555定时器的电容测试仪设计
图1 系统框图

3、硬件设计

3.1 555振荡电路的设计

由555芯片构成的多谐振荡电路如图2,CX为被测电容,接通电源后,CX被充电,A点电压UA上升。当UA上升到时,触发器被复位,同时555芯片内部放电三极管导通,此时U0为低电平。CX通过R2和放电三极管放电,使UA下降。当UA下降到时,触发器又被置位,U0翻转为高电平[3]。CX放电所需的时间为:


基于555定时器的电容测试仪设计
基于555定时器的电容测试仪设计
图2 555构成的RC振荡电路

基于555定时器的电容测试仪设计

由上式可知,当电路设计完成后,振荡器输出f随CX的变化而改变。改变R1、R2的值即可改变系统量程。系统量程分为四档:(1)R1+2R2=470KΩ时,测1.0nF-10.0nF的电容值。(2)R1+2R2=47KΩ时,测10.0nF~100.0nF的电容(3)R1+2R2=4.7KΩ时,测100.0nF~1000.0nF的电容。(4)R1+2R2=470Ω时,测1.0μF~10.0μF的电容。图3为R1+2R2=470KΩ时,测量电容为2μF振荡输出输出波形。

基于555定时器的电容测试仪设计
图3 振荡电路输出的频率信号

3.2 信号处理及显示电路

信号处理电路部分采用单片机AT89S51作为系统的主控制器。AT89S51单片机的最小系统由时钟电路、复位电路、外加电源及单片机构成[4],其硬件电路如图4所示。555振荡电路输出的是脉冲波,接到AT89S51处理器的输入引脚P3.5,通过AT89S51内部定时/计时器T0、T1及相应的程序设计,构成一个数字式频率测量系统,测出频率后按(5)式运算处理后得到被测电容值。

基于555定时器的电容测试仪设计
图4 单片机控制显示模块

显示模块LCD1602液晶第1、2脚接驱动电源;第三脚VL为液晶的对比度调节,通过在VCC和GND之间接一个10K多圈可调电阻,中间抽头接VL,可实现液晶对比度的调节;液晶的控制线RS、R/W、E分别接单片机的P2.5、P2.6、P2.7;D0~D7为LCD1602液晶模块的8位双向数据口,分别与STC89C52RC单片机的P1.0~P1.7相连,用于传输数据。接在单片机的P0口;BL+、BL-为液晶背光电源[5][6]。

4、系统软件设计

基于555定时器的电容测试仪设计
图5 主程序流程图

系统软件环境以Keil4.0为仿真平台,使用C语言编程编写了运行程序;包括主程序模块、显示模块和电容测试模块。软件设计主要包括三个方面:一是初始化系统;二是按键检测;三是数据采集、数据处理并进行显示。程序采用模块化的结构,这样便于调试和修改,易编程和易读性好,也程序结构清楚[7]。系统程序流程如图6所示,首先对P3.5口脉冲信号频率的测量,再通过(5)式算出所测的电容值,由LCD1602显示出来。

5、系统的测试

基于555定时器的电容测试仪设计
表1 电容测试数据

6、结束语

设计的电容测试仪硬件采用555定时器作为信号采集模块、AT89S51单片机作为信号处理器模块,软件采用Keil4.0为仿真平台,使用C语言编程编写了运行程序。其具有性能稳定、精度高、操作简单、功耗低等优点。经测试表明:其可以测试1.0nF-10.0uF范围的电容,误差小于0.5%。误差产生主要原因与电路元件参数、测试环境、测试方法等因素有关。

参考文献:

[1] 刘军,李智.基于单片机的高精度电容电感测量仪[J].研究与开发,2007,26(6):48-51.
[2] 谢冬莹,芦庆,蒋超.基于单片机实现测量电容方法研究[J].仪表技术,2009,(11):42-44.
[3] 陈有卿,叶桂娟.555时基电路原理设计与应用[M].北京:电子工业出版社,2007.
[4] 刘宇.微型化数字式电容测微仪[D].天津大学,2007.
[5] 张怀强,何为民.电阻电容在线测试及LCD显示[J].今日电子,2008,(7):41-44.
[6] 兰羽,卢庆林.仪表放大器在激光外差玻璃测厚系统中的应用[J].国外电子测量技术,2012,31(3):79-82.
[7] 张培仁.基于C语言编程MCS-51单片机原理与应用[M].北京:清华大学出版社,2003.

来源: 捷配电子市场网

围观 7
326

在8位单片机中没有16位数的操作指令,所有的int型数据都要通过两个字节分开操作,使用的方法不用,生成的代码也不相同,当然效率也不一样,通过指针对16位数进行操作可以得到高效的代码。

比如通过串行口接收数据,或者从串行的EEPROM中读取的数据,或者从大于8位的A/D读取的数据,由于8位单片机的数据线是8位的,高于8位的数据都要分成两个字节分别读取,然后写入到RAM中去再进行计算,或者把16位的int型数据从RAM中读出再分别把高低字节存到EEPROM或者送到D/A,或者通过串行口发送出去,方法有很多种,下面用多种方法进行实现该操作,这里只演示写入到16位的情况,读取的情况非常相似,不赘述。

(1)使用联合 (union)

typedef union{unsigned int i;unsigned char c[2];}u_int;unsigned char dH = 0x11, dL=0x22;unsigned int d;u_int ud;ud.c[0] = dH;ud.c[1] = dL;d = ud.i;此时d = 0x1122;

(2) 使用移位指令

数据定义与前面相同d = ((unsigned int)dH) 或者d = dH;d d |= dL; // or: d = d | dL; 后者编译的代码可能不是最简的

(3)使用指针

unsigned char *cptr;cptr = (unsigned char*)(&d);cptr[0] = dH;cptr[1] = dL;

(4)强制指针类型转换

*((unsigned char*)(&d)) = dH;*((unsigned char*)(&d)+1) = dL;或((unsigned char*)(&d))[0] = dH;((unsigned char*)(&d))[1] = dL;

这两种方式看似相同但由Keil编译出的代码是不用的,前都有一次加法运算,而后者没有,后者生成的代码更简洁,这种方式与用联合成生的代码是完全一样的。

在这几种方法中第(1)与第(4)的第二种生成的代码是最乘洁的,是推荐使用的,从软件工程的角度出发,推荐使用方法(1),这样没有强制类型转换,没有用到指针,更不容易出错。从书写的代码来讲,第(4)的第二种方法是最好的,代码简洁而且效率最高,但语法有点儿复杂。

转自: 单片机精讲吴鉴鹰

围观 7
380

1.引言

数字信号处理器是一种适合于实现各种数字信号处理运算的微处理器,具有下列主要结构特点:
(1)采用改进型哈佛(Harvard)结构,具有独立的程序总线和数据总线,可同时访问指令和数据空间,允许实际在程序存储器和数据存储器之间进行传输;
(2)支持流水线处理,处理器对每条指令的操作分为取指、译码、执行等几个阶段,在某一时刻同时对若干条指令进行不同阶段的处理;
(3)片内含有专门的硬件乘法器,使乘法可以在单周期内完成;
(4)特殊的指令结构和寻址方式,满足数字信号处理FFT、卷积等运算要求;
(5)快速的指令周期,能够在每秒钟内处理数以千万次乃至数亿次定点或浮点运算;
(6)大多设置了单独的DMA总线及其控制器,可以在基本不影响数字信号处理速度的情况下进行高速的并行数据传送。

由一片DSP加上存储器、模/数转换单元和外设接口就可以构成一个完整的控制系统,但这种方案要达到高速实时控制是不可行的。因为一个实时控制系统一般需要完成数据采集、模/数转换、分析计算、数/模转换、实时过程控制以及显示等任务,单靠一片DSP来完成这些工作势必会大大延长系统对控制对象的控制周期,从而影响整个系统的性能。所以我们添加一个CPU,负责数据采集、模/数转换、过程控制以及人机接口等任务,使DSP专注于系统控制算法的实现,充分利用它的高速数据处理能力。从性能价格比的角度出发,这个CPU采用8位的51系列。这时,两个CPU之间的数据共享就成了一个重要的问题。

采用双口RAM(简称DRAM)是解决CPU之间的数据共享的有效办法。与串行通信相比,采用双口RAM不仅数据传输速度高,而且抗干扰性能好。在笔者实验室研制的电力有源滤波器中,选用了TI公司的第三代DSP芯片TMS320C32和51系列单片机89C52作为控制系统的CPU。两个CPU之间通过双口RAM CY7C133完成数据交换。但在实际使用过程中遇到了89C52 与双口RAM总线宽度不匹配的问题,需要进行接口电路的设计。

2.双口RAM CY7C133的内部结构和功能

CY7C133是CYPRESS公司研制的高速2K×16CMOS双端口静态RAM,具有两套相互独立、完全对称的地址总线、数据总线和控制总线,采用68脚 PLCC封装形式,最大访问时间可以为25/35/55 ns。采用主从模式可以方便地将数据总线扩展成32位或更宽。各引脚的功能如表1所示,内部功能框图如图1所示。

DSP与单片机的一种高速通信实现方法

  
CY7C133允许两个CPU同时读取任何存储单元(包括同时读同一地址单元),但不允许同时写或一读一写同一地址单元,否则就会发生错误。双口RAM中引入了仲裁逻辑(忙逻辑)电路来解决这个问题:当左右两端口同时写入或一读一写同一地址单元时,先稳定的地址端口通过仲裁逻辑电路优先读写,同时内部电路使另一个端口的信号有效,并在内部禁止对方访问,直到本端口操作结束。BUSY信号可以作为中断源指明本次操作非法。在主从模式中,主芯片的信号接上拉电阻作为输出,从芯片的信号作为写禁止输入。

3.DSP、单片机与双口RAM之间的接口电路

89C52的地址总线宽度为16位,数据总线为8位;TMS320C32的数据总线宽度为32位,地址总线宽度为24位。而CY7C133的数据总线宽度为16位,地址总线宽度为11位,所以TMS320C32与双口RAM的接口并无特别之处,但是89C52与双口RAM之间的接口电路中就需要对89C52进行总线扩展了。具体做法是利用锁存器74HC373的锁存功能,通过对其使能信号的控制,进行分时读写,实现数据总线的扩展,即利用锁存器作为虚拟总线。具体的读写过程、读写信号及锁存器使能信号的产生将在下面详细说明。DSP、单片机与双口RAM之间的接口电路如图2所示。

DSP与单片机的一种高速通信实现方法

  
TMS320C32分配给双口RAM的地址空间为0x800000h~0x8007FFh。通过三八译码器74HC138对A20~A23和STRB进行译码,给出双口RAM的片选信号CER。89C52分配给双口RAM的地址空间为0x1000h~0x1FFFh。通过二四译码器74HC139对A13~A15进行译码产生双口RAM的片选信号CEL。双口RAM每边都有两个读/写控制信号,分别控制高位字节和低位字节的读/写,在使用时可以根据需要分别对数据的高位和低位进行写入操作。在图2所示接口电路中,两边的两个读/写控制信号分别被连接在一起,也就是说此时双口RAM的读写都是同时读写16位数据。

图2中双口RAM CY7C133的读写信号以及锁存器74HC373的使能信号的产生如图3所示。其中,WR是89C52的写控制信号,RD是89C52的读控制信号,A0是89C52的地址最低位,A15是地址最高位,R/W是TMS320C32的读写控制信号,BUSYL接89C52的P1口的一个引脚(具体可根据系统实际情形自行选择,图中未画出),BUSYR接TMS320C32的READY信号。

DSP与单片机的一种高速通信实现方法

  
下面讨论一下89C52对双口RAM的读写过程。当89C52对双口RAM进行读数据时,由图3可知此时A0应为低电平,不妨假设地址为0x1000h,则存储在双口RAM中该地址处的16位数据同时被读出,由于高8位数据线与89C52的8位数据线直接相连,所以高8位数据被立即读入89C52中。同时,根据图3中各信号的相互逻辑关系不难判断,U3的使能信号LE有效(高电平),OE无效(低电平),因而低8位数据被送入U3 中锁存起来。接着89C52再进行一次读操作,这时地址变为0x1001h,由于A0变成高电平,双口RAM的读使能信号变成无效电平,所以此次读操作对双口RAM不产生影响。再来看U3的使能信号LE和OE的变化情况,显然LE变成了无效电平,而OE变成了有效电平,上次被锁存的数据(即双口RAM的低8位数据)被送入89C52。当89C52对双口RAM进行写入操作时,注意此时A0应为高电平,不妨假设地址为0x100Ch,同样可根据图3判断U2的使能信号LE和OE均为有效电平,因而数据被同时写入双口RAM中(即此时双口RAM的高8位数据和低8位相同);接着89C52再进行一次写操作,此时地址变为0x100Dh,由于A0变成低电平,U2的片选为无效电平,U2被封锁,数据写入双口RAM的高8位。从上面的分析可知,利用最低地址位A0的不同电平,89C52通过两次连续的读或写操作,成功地实现了对双口RAM中数据的读或写,只不过是读入时是先读入高8位,后读入低8位;而写入则是先写入低8位,后写入高8位。

4.软件实现方案

双口RAM必须采用一定的机制来协调左右两边CPU对它的读写操作,否则会出现读写数据的错误。通常可以用中断、硬件、令牌和软件这四种方式来协调双方,本文采用的是软件方式。从上面的分析中我们可以得知,在接口电路中实际上已经利用89C52的最低地址位A0把双口RAM的存储空间分为奇、偶地址两个空间。其中,奇地址空间专供89C52写,偶地址空间专供89C52读。那么我们只需对TMS320C32的软件作相应处理即可,也就是说,TMS320C32对双口RAM的奇地址空间只读,对偶地址空间只写。这样就避免了TMS320C32和89C52对双口RAM同一地址单元的写入操作。另外,在对双口RAM进行访问之前,CPU首先对本端的BUSY信号进行查询,只有本端/BUSY信号无效时才进行读写操作,进一步保证了数据读写的可靠性。

5.结束语

通过双口RAM实现双CPU之间的数据通信,极大地提高了数据传输速度和可靠性,满足了控制系统的实时、高速的控制要求。本文所设计的89C52与双口RAM之间的接口电路简单实用,成功解决了它们总线匹配的问题,对其他类似需要总线扩展的系统也有一定的参考价值。

来源: 捷配电子市场

浏览 1 次
358

前言

STM32 提供了灵活的固件加载模式,其中大部分型号支持 DFU 加载。并且在电脑端,提供了配套的演示软件 DfuSe。

包含可视化版 DfuSeDemo.exe 和命令版 DfuSeCommand.exe。本文主要介绍 DfuSeCommand.exe 的使用。

前期步骤

1. 在电脑上安装 DfuSe(可通过“相关工具 & 链接”小节中,提供的链接获取安装包)。

2. STM32 硬件正确配置,使其能够满足进入 System Bootloader 模式。更多详细介绍请参考《AN2606 STM32 microcontroller system memory boot mode》。如果应用中,利用 IAP 实现 DFU 升级,同样可以利用 DfuSe 工具。

3. 利用 USB 数据线连接电脑和 STM32 的全速 USB 接口,等待驱动自动安装完成。如果驱动无法正确安装,可指定驱动路径,重新安装驱动。驱动位于 DfuSe 安装目录\ Bin\Driver。

4. 打开 DfuSe 安装目录下的 DfuSeCommand.exe。

5. 结合下节,使用 DfuSeCommand。

在使用 DfuSeCommand 前需要完成所列步骤,以便提供运行所需的硬件和软件环境。更多关于 DfuSe 以及固件转换等介绍,可以参考《UM0412 Getting started with DfuSe USB device firmware upgrade STMicroelectronics extension》。

DfuSeCommand 命令使用介绍

DfuSecommand 提供了几个命令,分别用于实现帮助、DFU 设备连接、加载、上传功能。
为演示需要,执行上节列出的步骤,并连接两个 DFU 设备(由 STM32 的 System Bootloader 实现 DFU 功能)到电脑。

输入红框中命令,执行帮助命令,列出了DfuSecommand.exe 支持的全部命令。
注:执行命令中对应的位置信息需要与 DfuSe 安装位置一致。

STM32单片机DfuSeCommand的使用

上传命令。命令实现:连接设备 0,上传固件并保存。

上传命令。命令实现:连接设备 1,上传固件并保存。

加载命令。命令实现:连接设备 0,加载固件到设备 0.

加载命令。命令实现:连接设备 1,加载固件到设备 1.

加载命令。命令实现:连接设备 0 (默认),加载固件到设备0.

小结

本文所列出的 DfuSeCommand 命令使用,并未涵盖全部参数情况。使用者可以参考列出部分的命令实
现,并结合 Help 下列出的全部命令参数,执行需要的命令。

DfuSeCommand.exe 相对于可视化版 DfuSeDemo.exe 功能更加精简,使用者如果只是为了验证 DFU
功能,建议使用可视化版 DfuSeDemo.exe,操作方便,并且具有更多的功能。

来源: 21ic.com/stm32/

围观 7
213

页面

订阅 RSS - 单片机