返回

蓝桥杯嵌入式学习笔记

目录

起手式

1.RCC-High Speed Clock (HSE)->CrystaI/Ceramic Resonator
2.配置频率
图1
3.进入工程,点击魔术棒。Debug-Use:CMSIS-DAPDebugger
4.点击旁边的Setting-FlashDownload:勾选Reset and Run

点亮LED灯

图1
我们要配置PC8-15,PD2的引脚,将他们设置为GPIO-OUTPUT.同时由图知PC8-15为高电平时灯不亮,PD2为使能引脚,高电平是控制灯亮。
所以我们默认设置PD2为低电平,PC8-15为高电平
我们要用到的函数:

1
2
3
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

编写一个控制第几个灯开关的函数

原理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#define GPIO_PIN_0   ((uint16_t)0x0001) 
#define GPIO_PIN_1   ((uint16_t)0x0002) 
#define GPIO_PIN_2   ((uint16_t)0x0004) 
#define GPIO_PIN_3   ((uint16_t)0x0008) 
#define GPIO_PIN_4   ((uint16_t)0x0010) 
#define GPIO_PIN_5   ((uint16_t)0x0020) 
#define GPIO_PIN_6   ((uint16_t)0x0040) 
#define GPIO_PIN_7   ((uint16_t)0x0080) 
#define GPIO_PIN_8   ((uint16_t)0x0100) 
#define GPIO_PIN_9   ((uint16_t)0x0200) 
#define GPIO_PIN_10  ((uint16_t)0x0400) 
#define GPIO_PIN_11  ((uint16_t)0x0800) 
#define GPIO_PIN_12  ((uint16_t)0x1000) 
#define GPIO_PIN_13  ((uint16_t)0x2000) 
#define GPIO_PIN_14  ((uint16_t)0x4000) 
#define GPIO_PIN_15  ((uint16_t)0x8000) 
#define GPIO_PIN_All ((uint16_t)0xFFFF) 

实际上是左移关系(0x0200«1=0x0400)

但要注意
GPIO_PIN_3 ((uint16_t)0x0008)与GPIO_PIN_4 ((uint16_t)0x0010)和
GPIO_PIN_11 ((uint16_t)0x0800)与GPIO_PIN_12 ((uint16_t)0x1000)
并不满足这个小妙招

示例代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void led_show(uint8_t index,int opeator){
  HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
  if(opeator==1){
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(index-1),GPIO_PIN_RESET);
  }else{
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8<<(index-1),GPIO_PIN_SET);
  }
  HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

按键操作

原理

图1

设置PB0-2,PA0为GPIO-INPUT,PULL-UP(上拉是高电平,按下时低电平)
示例代码:

 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
uint8_t btn1;
uint8_t btn1last;
uint8_t btn2;
uint8_t btn2last;
uint8_t btn3;
uint8_t btn3last;
uint8_t btn4;
uint8_t btn4last;
void readbtnpin(){
    btn1=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
    btn2=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
    btn3=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
    btn4=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
  if(btn1==0&&btn1last==1){
      led_show(1,1);
    }else if(btn2==0&&btn2last==1){
      led_show(2,1);
    }else if(btn3==0&&btn3last==1){
      led_show(3,1);
    }else if(btn4==0&&btn4last==1){
      led_show(4,1);
    }
  btn1last=btn1;
  btn2last=btn2;
  btn3last=btn3;
  btn4last=btn4;
}

示例2:

 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
u8 key_scan()           //按键按下判断
{
  if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET)
    return 1;
  else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==GPIO_PIN_RESET)
    return 2;
  else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==GPIO_PIN_RESET)
    return 3;
  else if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
    return 4;
  return 0;//按键置空时用这个
}


  key_value = key_scan();
  key_down = key_value&(key_value^key_old);
  //置空(0x00)&(0x00^0x11);异或后维持原样(0x11)
  key_up =~key_value&(key_value^key_old);
  key_old = key_value;
  switch(key_down)//开始key_down的判断
  {
    case 1:
        break;
    case 2:
      break;
    case 3:
      break;
    case 4:
      break;
  }
  //开始key_up的特判
  //长按b3,松开运行函数
  if(key_up == 3)//在这里加长按的代码(需加个按下的flag)

按键防抖

1
2
//检测上次按下的时间>=50ms  加在有关key的函数最前面  
if(uwTick-KEY_uwTick<50)return;

LCD 操作相关

配置lcd相关函数

1.将资料包中的lcd.h与lcd.c,font.h移入工程,并链接。

解决与LED串线的问题

打开lcd.c,在

1
2
3
4
5
6
7
8
void LCD_Init(void)
void LCD_Clear(u16 Color)
void LCD_DisplayStringLine(u8 Line, u8 *ptr)//用到啥啥就要加

首行加:
uint16_t temp =GPIOC->ODR;
结尾加:
GPIOC->ODR = temp;

基础操作

常用函数:

1
2
3
4
5
6
void LCD_Init(void);
void LCD_SetTextColor(vu16 Color);
void LCD_SetBackColor(vu16 Color);
void LCD_Clear(u16 Color);
void LCD_DisplayStringLine(u8 Line, u8 *ptr);
void LCD_DrawLine(u8 Xpos, u16 Ypos, u16 Length, u8 Direction);

起手式: 加在USER CODE BEGIN 2下面:

1
2
3
4
  LCD_Init();
  LCD_Clear(Black);
  LCD_SetTextColor(White);
  LCD_SetBackColor(Black);
1
2
3
      char str[20];
      sprintf(str,"   helloWorld:%d   ",a);
      LCD_DisplayStringLine(Line2,(uint8_t *)str);

需要注意的地方

比赛时line0表示第一列。

PWM输出

图1

配置

图1

占空比设置

$$ Rate=\frac{CCR}{ARR+1} $$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
HAL_TIM_PWM_Start(&htim2,TIM_CHANNER_1);//使能
TIM2->CCR2=50;//2=CH2
CCR//输出比较寄存器

//如何设置ARR值
HAL_TIM_GetAutoreload(&htim2);
HAL_TIM_SetAutoreload(&htim2,arr);

//opttion2
__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 300)
用来改变CRR的值的函数
$$ f=\frac{f_{sys}}{(ARR+1)(PSC+1)} $$

pwm捕获

图1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNER_1);//中断使能
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//时钟中断回调(CCR=80-1)
{
  if(htim->Instance==TIM2)
  {
  u32 counter = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//捕获到上升沿,CCR=CNT;
  //所以说count=CCR
  TIM2->CNT=0;
  freqA = 1000000/counter;

  }
}

输入捕获

1
2
3
4
5
6
7
8
9
HAL_TIM_IC_Start_IT(&htim16, TIM_CHANNEL_1);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
  if(htim->Instance == TIM16)
    {
      tim16_capture_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
      TIM16->CNT = 0;
      tim16_freq = SystemCoreClock / (80 * capture_value);
    }
}

ADC测量

图1 图1

1
2
3
4
5
double get_vol(ADC_HandleTypeDef *hadc){//我们可以包装一个函数方便我们使用
  HAL_ADC_Start(hadc);
  uint32_t adc_value=HAL_ADC_GetValue(hadc);
  return 3.3*adc_value/4096;
}

串口通信

图1

1
2
3
4
5
6
7
uint8_t status[8]={0};
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){//重写回调
  if(huart->Instance == USART2){
    HAL_UART_Transmit(huart, &data,sizeof(data),100);//100是timeout
    HAL_UART_Receive_IT(huart, &data,sizeof(data));
  }
}

注意点

在while上面加上初始化

1
HAL_UART_Receive_IT(&huart2,&data,sizeof(data));

dma+usart

Normal 模式

 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
HAL_UART_Receive_DMA(&huart3, recvStr, RX_BUF_SIZE);

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART3) {
        rxComplete = 1; // 标记接收完成
    }
}

while (1) {
    if (rxComplete) {
        rxComplete = 0;

        // 实际接收到的字节数 = RX_BUF_SIZE(因为是 Normal 模式,收满才触发)
        // 如果你希望接收任意长度(如遇到 '\n' 停止),应改用 IDLE 中断

        HAL_UART_Transmit(&huart3, recvStr, RX_BUF_SIZE, HAL_MAX_DELAY);

        // 清空缓冲区(可选)
        memset(recvStr, 0, sizeof(recvStr));

        // 重新启动下一次接收
        HAL_UART_Receive_DMA(&huart3, recvStr, RX_BUF_SIZE);
    }
    HAL_Delay(10); // 非阻塞轮询
}

Circular 模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
HAL_UART_Receive_DMA(&huart3, rxBuffer, RX_BUF_SIZE);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); // 使能空闲中断

void HAL_UART_IDLE_Callback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART3) {
        // 清除 IDLE 标志
        __HAL_UART_CLEAR_IDLEFLAG(&huart3);

        // 获取已接收字节数
        uint16_t dataSize = RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(huart3.hdmarx);

        // 回传数据
        HAL_UART_Transmit(&huart3, rxBuffer, dataSize, HAL_MAX_DELAY);
    }
}

减速函数

通过加TIM并判断CNT>15实现

$$ f=\frac{f_{sys}}{(ARR+1)(PSC+1)} $$

eeprom读写

图1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void eeprom_write(uint8_t addr,uint8_t dat){
  I2CStart();
  I2CSendByte(0xa0);//从机地址
  I2CWaitAck();
  I2CSendByte(addr);//从机内部地址
  I2CWaitAck();
  I2CSendByte(dat);
  I2CWaitAck();
  I2CStop();
}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
uint8_t eeprom_read(uint8_t addr){
  I2CStart();
  I2CSendByte(0xa0);
  I2CWaitAck();
  I2CSendByte(addr);
  I2CWaitAck();
  I2CStop();

  I2CStart();
  I2CSendByte(0xa1);
  I2CWaitAck();
  uint8_t dat =I2CReceiveByte();
  I2CSendNotAck();
  I2CStop();
  return dat;
}

rtc时钟

图1

1
2
3
4
5
6
7
RTC_TimeTypeDef sTime ={0};
RTC_DateTypeDef sDate={0};
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc){
  ...
}
  HAL_RTC_GetDate(&hrtc,&sDate,RTC_FORMAT_BIN);
  HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);//rtc句柄,RTC_DateTypeDef,用于

ex

显示图片

 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
void LCD_showPic(const uint8_t* pic_arr, uint8_t pic_x, uint8_t pic_y) {
  LCD_Init();
  LCD_Clear(Black);
  
  LCD_SetCursor((240-pic_y)/2,(320-pic_x)/2); 
  LCD_WriteRAM_Prepare();
  
  for(int i=0;i<(pic_x*pic_y*2);i+=2){
    if(i%(pic_x*2)==0){
      for(uint8_t flesh=0;flesh<(320-pic_x);flesh++)
        LCD_WriteRAM((uint16_t)0x00000);
    }
    LCD_WriteRAM(pic_arr[i+1]<<8|pic_arr[i]);
  }
}

void LCD_showVPic(const uint8_t* pic_arr, uint8_t pic_x, uint8_t pic_y) {
  LCD_Init();
  LCD_Clear(Black);
  uint16_t start_x=(240-pic_x)/2,start_y=0;
  LCD_SetCursor(start_x,start_y); 
  LCD_WriteRAM_Prepare();
  
  for(uint16_t y=0;y<pic_x;y++){
    for(uint16_t x=0;x<(320-pic_y)/2;x++){
      LCD_WriteRAM(0x000000);
    }//填充左半边
    for(uint16_t x=0;x<pic_y;x++){
      uint16_t offset =(pic_x*x+y) * 2;
      LCD_WriteRAM(pic_arr[offset+1]<<8|pic_arr[offset]);
    }
    for(uint16_t x=0;x<(320-pic_y)/2;x++){
      LCD_WriteRAM(0x000000);
    }//填充右半边
  }
}

CAN总线

物理层

CAN通讯网络是一种遵循ISO11898标准的高速、短距离“闭环网络”, 它的总线最大长度为40m,通信速度最高为1Mbps,总线的两端各要求有一个“120欧”的电阻。 “CAN 总线接线图”

SS段 (SYNC SEG)

SS 译为同步段,若通讯节点检测到总线上信号的跳变沿被包含在SS段的范围之内,则表示节点与总线的时序是同步的, 当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。SS段的大小固定为1Tq。

PTS段 (PROP SEG)

PTS 译为传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS段的大小可以为1~8Tq。

PBS1段 (PHASE SEG1)(增加),

PBS1 译为相位缓冲段,主要用来补偿边沿阶段的误差,它的时间长度在重新同步 的时候可以加长 。PBS1段的初始大小可以为1~8Tq。

PBS2段 (PHASE SEG2)(减少)

PBS2 这是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短 。PBS2段的初始大小可以为2~8Tq。

硬同步(发生在SS段, 只是当存在帧起始信号时)

重新同步(影响PBS1,PBS2长度)

差分信号

“CAN 总线接线图”

起始段,仲裁段、控制段、数据段、CRC段、ACK段,帧截止 ""

优先级

ID(在仲裁段)数值越小,消息优先级越高。 RTR位 (Remote Transmission Request Bit),译作远程传输请求位,它是用于区分数据帧和遥控帧的, 当它为显性电平时表示数据帧,隐性电平时表示遥控帧。
IDE位 (Identifier ExtensionBit),译作标识符扩展位,它是用于区分标准格式与扩展格式, 当它为显性电平时表示标准格式,隐性电平时表示扩展格式。
SRR位 (Substitute Remote Request Bit),只存在于扩展格式,它用于替代标准格式中的RTR位。 由于扩展帧中的SRR位为隐性位,RTR在数据帧为显性位,所以在两个ID相同的标准格式报文与扩展格式报文中,标准格式的优先级较高。

pic

code

  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
static void CAN_GPIO_Config(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;

   /* 使能引脚时钟 */
   CAN_TX_GPIO_CLK_ENABLE();
   CAN_RX_GPIO_CLK_ENABLE();

   /* 配置CAN发送引脚 */
   GPIO_InitStructure.Pin = CAN_TX_PIN;
   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
   GPIO_InitStructure.Speed = GPIO_SPEED_FAST;
   GPIO_InitStructure.Pull  = GPIO_PULLUP;
   GPIO_InitStructure.Alternate =  GPIO_AF9_CAN1;
   HAL_GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);

   /* 配置CAN接收引脚 */
   GPIO_InitStructure.Pin = CAN_RX_PIN ;
   HAL_GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);
}

static void CAN_Mode_Config(void)
{

   /************************CAN通信参数设置****************************/
   /* 使能CAN时钟 */
   CAN_CLK_ENABLE();

   Can_Handle.Instance = CANx;
   Can_Handle.pTxMsg = &TxMessage;
   Can_Handle.pRxMsg = &RxMessage;
   /* CAN单元初始化 */
   Can_Handle.Init.TTCM=DISABLE;  //MCR-TTCM  关闭时间触发通信模式使能
   Can_Handle.Init.ABOM=ENABLE;   //MCR-ABOM  自动离线管理
   Can_Handle.Init.AWUM=ENABLE;   //MCR-AWUM  使用自动唤醒模式
   Can_Handle.Init.NART=DISABLE;//MCR-NART  禁止报文自动重传DISABLE-自动重传
   Can_Handle.Init.RFLM=DISABLE; //MCR-RFLM  接收FIFO 锁定模式DISABLE-溢出时新报文会覆盖原有报文
   Can_Handle.Init.TXFP=DISABLE; //MCR-TXFP 发送FIFO优先级DISABLE-优先级取决于报文标示符
   Can_Handle.Init.Mode = CAN_MODE_NORMAL;  //正常工作模式
   Can_Handle.Init.SJW=CAN_SJW_1TQ;   //BTR-SJW 重新同步跳跃宽度 2个时间单元

   /* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) 波特率即为时钟周期tq*(1+3+6)  */
   Can_Handle.Init.BS1=CAN_BS1_5TQ;     //BTR-TS1 时间段1 占用了6个时间单元
   Can_Handle.Init.BS2=CAN_BS2_3TQ;     //BTR-TS1 时间段2 占用了3个时间单元

/* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB 1 = 45 MHz) */
   Can_Handle.Init.Prescaler =6; //BTR-BRP 波特率分频器定义了时间单元的时间长度 45/(1+5+3)/5=1 Mbps
   HAL_CAN_Init(&Can_Handle);
}


static void CAN_Filter_Config(void)
{
   CAN_FilterConfTypeDef  CAN_FilterInitStructure;

   /*CAN筛选器初始化*/
   CAN_FilterInitStructure.FilterNumber=0;           //筛选器组0
   CAN_FilterInitStructure.FilterMode=CAN_FILTERMODE_IDMASK; //工作在掩码模式
   CAN_FilterInitStructure.FilterScale=CAN_FILTERSCALE_32BIT;//筛选器位宽为单个32位。
   /*
   使能筛选器,按照标志的内容进行比对筛选,扩展ID不是如下的就抛弃掉,是的话,会存
   入FIFO0。 */

   CAN_FilterInitStructure.FilterIdHigh= ((((uint32_t)0x1314<<3)|
   CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;   //要筛选的ID高位
   CAN_FilterInitStructure.FilterIdLow= (((uint32_t)0x1314<<3)|
   CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要筛选的ID低位
   CAN_FilterInitStructure.FilterMaskIdHigh= 0xFFFF;//筛选器高16位每位必须匹配
   CAN_FilterInitStructure.FilterMaskIdLow= 0xFFFF;//筛选器低16位每位必须匹配
   CAN_FilterInitStructure.FilterFIFOAssignment=CAN_FILTER_FIFO0;//筛选器被关联到FIFO0
   CAN_FilterInitStructure.FilterActivation=ENABLE;      //使能筛选器
   HAL_CAN_ConfigFilter(&Can_Handle,&CAN_FilterInitStructure);
}

static void CAN_NVIC_Config(void)
{
   /* 配置抢占优先级的分组 */
   HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_1);
   /*中断设置,抢占优先级0,子优先级为0*/
   HAL_NVIC_SetPriority(CAN_RX_IRQ, 0 ,0);
   HAL_NVIC_EnableIRQ(CAN_RX_IRQ);
}

void CAN_SetMsg(void)
{
   uint8_t ubCounter = 0;
   Can_Handle.pTxMsg->StdId=0x00;
   Can_Handle.pTxMsg->ExtId=0x1314;           //使用的扩展ID
   Can_Handle.pTxMsg->IDE=CAN_ID_EXT;          //扩展模式
   Can_Handle.pTxMsg->RTR=CAN_RTR_DATA;         //发送的是数据
   Can_Handle.pTxMsg->DLC=8;              //数据长度为8字节

   /*设置要发送的数据0-7*/
   for (ubCounter = 0; ubCounter < 8; ubCounter++) {
      Can_Handle.pTxMsg->Data[ubCounter] = ubCounter;
   }
}

void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)
{
   /* 比较ID是否为0x1314 */
   if ((hcan->pRxMsg->ExtId==0x1314) && (hcan->pRxMsg->IDE==CAN_ID_EXT)
   && (hcan->pRxMsg->DLC==8) ) {    flag = 1; //接收成功
   } else {
      flag = 0; //接收失败
   }
   /* 准备中断接收 */
   HAL_CAN_Receive_IT(&Can_Handle, CAN_FIFO0);
}

__IO uint32_t flag = 0;    //用于标志是否接收到数据,在中断函数中赋值
/**
* @brief  主函数
* @param  无
* @retval 无
*/
int main(void)
{
   /* 配置系统时钟为216 MHz */
   SystemClock_Config();
   /* 初始化LED */
   LED_GPIO_Config();
   /* 初始化调试串口,一般为串口1 */
   UARTx_Config();
   /*初始化can,在中断接收CAN数据包*/
   CAN_Config();

   printf("\r\n 欢迎使用野火  STM32 开发板。\r\n");
   printf("\r\n 野火 CAN通讯实验例程\r\n");

   printf("\r\n 实验步骤:\r\n");

   printf("\r\n 1.使用导线连接好两个CAN讯设备\r\n");
   printf("\r\n 2.使用跳线帽连接好:5v --- C/4-5V \r\n");
   printf("\r\n 3.按下开发板的KEY1键,会使用CAN向外发送0-7的数据包,包的扩展ID为0x1314 \r\n");
   printf("\r\n 4.若开发板的CAN接收到扩展ID为0x1314的数据包,会把数据以打印到串口。 \r\n");
   printf("\r\n 5.本例中的can波特率为1MBps,为stm32的can最高速率。 \r\n");
   while (1) {
      /*按一次按键发送一次数据*/
      if (  Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON) {
            LED_BLUE;
            /* 装载一帧数据 */
            CAN_SetMsg();
            /* 开始发送数据 */
            HAL_CAN_Transmit_IT(&Can_Handle);
            HAL_Delay(100);
            LED_RGBOFF;
      }
      if (flag==1) {
            /*发送成功*/
            LED_GREEN;
            printf("\r\nCAN接收到数据:\r\n");
            CAN_DEBUG_ARRAY(Can_Handle.pRxMsg->Data,8);
            flag=0;
            HAL_Delay(100);
            LED_RGBOFF;
      }
   }
}
Licensed under CC BY-NC-SA 4.0