蓝桥杯嵌入式CT117E硬件开发平台经验分享12 | 第十届蓝桥杯国赛题

电压、PWM采集、温度18B20读取的实现

毫不夸张的说,第十届蓝桥杯嵌入式国赛赛题出的是最有水平的一届,难度也是最高的一届赛题(第十一届国赛赛题由于只是考了脉冲捕获和脉冲输出,之前有介绍过,本文就是最后一篇过于蓝桥杯国赛赛题的分析)

第十届赛题如下:

蓝桥杯嵌入式CT117E硬件开发平台经验分享12 | 第十届蓝桥杯国赛题_第1张图片
蓝桥杯嵌入式CT117E硬件开发平台经验分享12 | 第十届蓝桥杯国赛题_第2张图片
蓝桥杯嵌入式CT117E硬件开发平台经验分享12 | 第十届蓝桥杯国赛题_第3张图片

本届赛题最难的地方不是数据的采集,而是要让数码管和串口同时工作,要知道,串口和数码管是通用IO口的,虽然可以分时复用,但两条数码管的时钟线都在串口IO上,所以即便分时复用,也会导致在串口发送和接收的过程中数码管显示乱码。

—>相应的本文的解决方法为:在串口使能的时候,只允许其中之一有效,即:串口需要发送数据时要关闭串口接收功能,同理,当串口接收时串口的发送端是无效的。–>原因:由于数码管是74HC595驱动的(可以适当了解下此芯片),它工作与时钟线的上升沿,即RCLK(R:存储寄存器读取):上升沿 移位寄存器进入存储寄存器;下降沿 数据保持不变;SCK(S:送入595):上升沿 数据寄存器数据移位。Q0–>Q1–>Q2–>Q3–>…–>Q7;下降沿 移位寄存器数据不变。也就是说要想让595工作,则需要有时钟线的上升沿,而且是两个时钟线的上升沿。那么本文方法就是当启用串口功能时只让一个IO功能有效,则不会同时改变两个IO口连接的时钟线状态,即完成了相当于锁存的功能。实现方法为:

//端口共用问题,思路:两个时钟线RCK,SCK同一时间只能有一个被占用,
//另一个被赋值为低电平,以防产生上升沿导致错误数据发送到数码管显示,
//且调用函数顺序有要求:做到先关闭串口2的使能,然后关闭无用GPIO,再打开所需GPIO,然后调用串口发送
//调用完串口发送后及时关闭发送GPIO,再打开串口接收GPIO  ; 下面的函数调用顺序不要随意变动!!!
  USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
  USART_Cmd(USART2, DISABLE);
  SEG_Init();  
  SEG_Display();  

  USART2_GPIO_RXDisable(); 
  USART2_GPIO_TXEnable(); 
  USART_Cmd(USART2, ENABLE);
  printf("\r\n\n USART2 AND 7SEG Share GPIO....\r\n");
  USART2_GPIO_TXDisable();
  USART2_GPIO_RXEnable();
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);  

调用的代码:

串口2

#include "usart2.h"
#include <stdio.h>
//reference:     ..\2.STM32固件库v3.5\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART\Interrupt
//TXD2 - PA2       RXD2 - PA3
static void USART2_NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  /* Enable the TIM4 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVICPriority_Structure.Usart2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);
}
static void USART2_RCC_Configuration(void)
{   
  /* Enable GPIO clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  /* Enable USARTy Clock */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);  //在使用串口复用功能的时候,要开启相应的功能时钟USART ,且此时钟源在APB1上!!!  
}
static void USART2_GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure USARTy Rx as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Configure USARTy Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART2_GPIO_TXEnable(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure USARTy Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //    GPIO_Mode_Out_PP
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART2_GPIO_TXDisable(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure USARTy Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //    GPIO_Mode_Out_PP
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
void USART2_GPIO_RXEnable(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure USARTy Rx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART2_GPIO_RXDisable(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure USARTy Rx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //    GPIO_Mode_Out_PP
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  GPIO_ResetBits(GPIOA,GPIO_Pin_3);
}
static void USART2_Configuration(void)
{
  USART_InitTypeDef USART_InitStructure;
/* USARTy and USARTz configuration ------------------------------------------------------*/
  /* USARTy and USARTz configured as follow:
        - BaudRate = 9600 baud  
        - Word Length = 8 Bits
        - One Stop Bit
        - No parity
        - Hardware flow control disabled (RTS and CTS signals)
        - Receive and transmit enabled
  */
  USART_InitStructure.USART_BaudRate = 9600;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
   /* Configure USARTy */
  USART_Init(USART2, &USART_InitStructure);
  /* Enable USARTy Receive and Transmit interrupts */
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
//  USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
  /* Enable the USARTy */
  USART_Cmd(USART2, ENABLE);
}
void USART2_Init(void)
{
   USART2_NVIC_Configuration();
   USART2_RCC_Configuration();
   USART2_GPIO_Configuration();
   USART2_Configuration();
}
//reference:     ..\2.STM32固件库v3.5\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART\Printf
/***************************************************************************
USART_FLAG_TC:
当发送移位寄存器中的1字节数据已经通过TX脚一位一位的移出去后,该标志位就会被置1,
从而引发该事件的中断。所以,其实USART_FLAG_TC就是用来标志“发送移位寄存器中的数据有没有全部发送出去”这件事的。

对于USART_FLAG_TC来说,没必要每次当发送移位寄存器中的数据发送完成后都发生中断,而应该是整个串口数据帧全部发送完毕,
包括最后一个字节也发送出去之后才应该开中断,这代表的就是一个数据帧发送完成事件了。

USART_FLAG_TXE:
当发送数据寄存器中的数据已经取完了,该标志位就会被置1,从而引发该事件的中断。
所以,其实USART_FLAG_TXE就是用来标志一个事件的,通过它的值可以知道该事件有没有发生(即发送数据寄存器中的数据有没有被取走)。

USART_FLAG_TXE来说,只是说明数据寄存器中的数据已经被发送移位寄存器取走了(但发送移位寄存器中可能还没有启动发送过程),
通过中断就可以提醒CPU可以往数据寄存器中填充数据了,发送移位寄存器中的数据往外发送的过程其实还是比较耗时的,
相对于C语言代码执行时间来说,这个过程的耗时完全算得上是一个宏观的数据,所以每次发送数据寄存器中的数据被发送移位寄存器取走后,
都应该产生中断来提醒CPU对该寄存器更新数据;
****************************************************************************/
int fputc(int ch, FILE *f)        
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART */
  USART_SendData(USART2, (uint8_t) ch);

  /* Loop until the end of transmission */
  while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)   //注意是TC  !!!
  {}

  return ch;
}
STRUCT_USART2 USART2_Structure;
void USART2_Interrupt(void)
{
  u8 USART2_Rec;
  if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
  {
     USART_ClearITPendingBit(USART2,USART_IT_RXNE);
     USART2_Rec = USART_ReceiveData(USART2);
    /* Read one byte from the receive data register */
    if(USART2_Rec == 'S')
    {
      USART2_Structure.RxCounter = 0;
      USART2_Structure.IS_Start = TRUE;
    }
    if(USART2_Structure.IS_Start == TRUE)
      USART2_Structure.RxBuffer[USART2_Structure.RxCounter++] = USART_ReceiveData(USART2);
    if(USART2_Rec == '\r')
    {
      USART2_Structure.IS_Start = FALSE;
      USART2_Structure.IS_Finish = TRUE;
    }
    if(USART2_Structure.RxCounter > USART2_BUFFMAX)
    {
       USART2_Structure.RxCounter = 0;
       USART2_Structure.IS_Start = FALSE;
       USART2_Structure.IS_Finish = TRUE;
    }   
  }  
}

数码管驱动:

#ifndef __7seg_h
#define __7seg_h

#include "stm32f10x.h"

#define SER_H GPIO_SetBits(GPIOA,GPIO_Pin_1)
#define SER_L GPIO_ResetBits(GPIOA,GPIO_Pin_1)

#define RCK_H GPIO_SetBits(GPIOA,GPIO_Pin_2)
#define RCK_L GPIO_ResetBits(GPIOA,GPIO_Pin_2)

#define SCK_H GPIO_SetBits(GPIOA,GPIO_Pin_3)
#define SCK_L GPIO_ResetBits(GPIOA,GPIO_Pin_3)

#define  Seg_Code_A     10
#define  Seg_Code_C     12
#define  Seg_Code_O     0
#define  Seg_Code_Off   16 
#define  Seg_Code_spot  17 

typedef struct {
   uint8_t bit3;
   uint8_t bit2;
   uint8_t bit1;
}STRUCT_7SEG;
extern STRUCT_7SEG SEG_Structure;

void SEG_Init(void);
void SEG_Display(void);

#endif 




#include "7seg.h"
/*typedef const uint8_t uc8;  !< Read Only */
uc8 Seg7[] = { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,   // 0 - 9 
				 0x77,0x7c, 0x39,0x4f,0x79,0x78,       // A - F   
				 0x00,                                //灭,    共阴 
         0x3f|0x80,0x06|0x80,0x5b|0x80,0x4f|0x80,0x66|0x80,0x6d|0x80,0x7d|0x80,0x07|0x80,0x7f|0x80,0x6f|0x80};  //0. - 9.   :0-9带点显示
void SEG_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* GPIOx clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);
   
  RCK_L;
  SCK_L;
}
STRUCT_7SEG SEG_Structure;
void SEG_Display(void)   //left 1  2  3
{
  u8 i = 0;
  u8 SEG_DisBuff = Seg7[Seg_Code_Off];
  SEG_DisBuff = Seg7[SEG_Structure.bit3];
  for(i = 0;i < 8; i++)
  {
    if(SEG_DisBuff & 0x80)  
      SER_H;
    else
      SER_L;
    SCK_H;
    SEG_DisBuff = SEG_DisBuff << 1;
    SCK_L;
  }
  SEG_DisBuff = Seg7[SEG_Structure.bit2];
  for(i = 0;i < 8; i++)
  {
    if(SEG_DisBuff & 0x80)  
      SER_H;
    else
      SER_L;
    SCK_H;
    SEG_DisBuff = SEG_DisBuff << 1;
    SCK_L;
  }
  SEG_DisBuff = Seg7[SEG_Structure.bit1];
  for(i = 0;i < 8; i++)
  {
    if(SEG_DisBuff & 0x80)  
      SER_H;
    else
      SER_L;
    SCK_H;
    SEG_DisBuff = SEG_DisBuff << 1;
    SCK_L;
  }
  RCK_H;
  SEG_DisBuff = Seg7[Seg_Code_Off];
  RCK_L;
}




你可能感兴趣的:(单片机嵌入式,嵌入式,单片机,stm32,串口通信)