#关于STM32F051C8T6项目总结(四)

关于STM32F051C8T6某项目总述

3. 模块总述

  1. TM1640
  2. R25_F3950
  3. VS1838B
  4. AIP650
3. VS1838B
1. 功能说明

首先VS1838B接收头接收的是载波频率为38K的红外方波信号,遵守的是NEC协议。那么VS1838B可以用来做什么呢?市面上的大部分红外遥控器,都使用的是红外38K的NEC协议,也就是说VS1838B可以接收来自大部分遥控器的信号。所以要想给项目添加遥控功能,红外发射器和红外接收头便是最简便的配置。那么红外发射器指什么?当然指遥控器。遥控器可以定制,暂不赘述,所以先介绍VS1838B接收头。

2. 思路解析

首先红外发射器发射了一个38K红外信号,红外接收头在硬件层面接收到该38K红外信号,并产生相应的电信号。电信号即数字方波信号。那么该数字方波信号是一个特定的信号,试想一下为什么同样是38K红外载波遥控器为什么电视遥控器不能遥控空调呢?之所以叫特定,是因为遥控器不同按键产生的每个方波信号都是固定且不同的(同一遥控器),因此红外接收头接收到该方波信号传输给CPU就知道当前收到的信号是由哪个按键产生的。那么特定的方波信号到底有什么不同呢?可以这么理解,每个信号之所以不同是因为它们携带的“信息”不同。那么到底是什么信息呢?
这就要看NEC协议了。
在这里插入图片描述

图像来自此博客:(https://blog.csdn.net/STATEABC/article/details/131857097)

从上图可看出红外发射头发出的红外信号翻转后就是红外接收头接收的信号。浅浅分析一下此协议。NEC的协议格式是:
起始位(同步码(9ms低电平)+ 4.5ms高电平)+地址码+地址码反码+控制码+控制码反码
起始拉低9ms的电平之后,拉高4.5ms的电平,之后就是数据位。也就是说,在接收头那个引脚,CPU只要感受到电平被拉低9ms之后,然后有一段时间的高电平,就代表着我收到了一个红外信号。此后对该信号的数据解析,NEC的数据中地址码和控制码就是红外信号携带的信息,通过这个8字节(地址码)+8字节(控制码,一般以控制码作为唯一标识)数据对该红外信号唯一标识。
在这里插入图片描述

理一下思路:首先。我们在红外接收头out对应引脚接收到一个9ms的低电平和一个至少4.5ms的高电平那么就代表应该继续处理数据否则就丢弃该信号,然后在4.5ms的高电平之后,开始解析数据位(解析数据位的步骤是重点和难点)。解析完数据位,得到一个32字节的数据,该32字节数据构成如下:
地址码(4字节)+地址码反码(4字节)+控制码(4字节)+控制反码(4字节)
低字节先发送,意味着,我们先收到的是低字节,后收到的是高字节。

在这里插入图片描述
接下来分析:NEC协议的数据位结构,当我们接受完9ms的低电平和4.5ms的高电平时,继续读取电平信号会先读取到0.56ms的高电平,在0.56ms高电平之后会电平跳变此时是重点,我们需要记录低电平的持续时间如果低电平持续时间低于1125us-560us那么该数据位是0,如果低电平信号持续时间高于1125us-560us低于2250us-560us那么该数据位是1,然后又会继续读取到0.56ms的高电平,然后再记录低电平信号,如此循环往复。我们一共需要接收32位,接收后一般取控制码即可。然后用一个swich做分支处理。

3. 代码详解
   void Vs1838bInsInit(void)
   {
       GPIO_InitTypeDef GPIO_InitStructure;
       EXTI_InitTypeDef EXTI_InitStructure;
       NVIC_InitTypeDef NVIC_InitStructure;
       // 使能外部中断时钟
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
       RCC_AHBPeriphClockCmd(VS1838B_INPUT_CLK, ENABLE);
   
       // 配置 PD0 引脚为输入模式
       GPIO_InitStructure.GPIO_Pin  = VS1838B_INPUT_PIN;
       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
       GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
       GPIO_Init(VS1838B_INPUT_PORT, &GPIO_InitStructure);
       // 配置外部中断 EXTI8
       SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource8);
   
       EXTI_InitStructure.EXTI_Line    = EXTI_Line8;
       EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
       EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
       EXTI_InitStructure.EXTI_LineCmd = ENABLE;
       EXTI_Init(&EXTI_InitStructure);
       NVIC_EnableIRQ(EXTI4_15_IRQn);
       // 配置外部中断 EXTI8 的中断优先级
       NVIC_InitStructure.NVIC_IRQChannel         = EXTI4_15_IRQn;
       NVIC_InitStructure.NVIC_IRQChannelPriority =3;
       NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
       NVIC_Init(&NVIC_InitStructure);
   }

GPIO引脚(OUT连接引脚)可采用浮空不上拉或浮空上拉模式,项目采用的是浮空上拉模式。外部中断触发是上升沿和下降沿都触发

     // 配置 TIM2 为计时器
     void initTimer(void)
     {
         TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
     	NVIC_InitTypeDef NVIC_CFG;
         // 使能 TIM2 时钟
         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
     
         // 配置 TIM2 为计时器模式
         TIM_DeInit(TIM2);
         TIM_TimeBaseStructure.TIM_Period        = 0xffff;
         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
         TIM_TimeBaseStructure.TIM_Prescaler     = 47; // 分频系数,使 TIM2 计数器时钟为 1MHz
         TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
         TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
     
         // 清除计数器
         TIM_SetCounter(TIM2, 0);
         TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
     //	// 配置中断优先级
     //    NVIC_CFG.NVIC_IRQChannel         = TIM2_IRQn;
     //    NVIC_CFG.NVIC_IRQChannelPriority = 2;
     //    NVIC_CFG.NVIC_IRQChannelCmd      = ENABLE;
     //	  NVIC_Init(&NVIC_CFG);
         NVIC_EnableIRQ(TIM2_IRQn);
     		TIM_Cmd(TIM2,ENABLE);
     }

该定时器1TICK位1us。不使用其中断功能定时器只用来计时。

   
   // 外部中断 EXTI0 的中断处理程序
   void EXTI4_15_IRQHandler(void)
   {
       if (EXTI_GetITStatus(EXTI_Line8) != RESET) {
   
           switch (NEC_Trigger_cnt) {
               case 0:
                   START_COUNT;
                   NEC_Trigger_cnt++;
                   break;
               case 1:
                   STOP_COUNT;
                   if (GET_COUNT > 8000 && GET_COUNT < 10000) {
                       NEC_Trigger_cnt++;
                       START_COUNT;
                   } else {
   						 bit_count = 0;
   						 TIM2->CNT = 0;
   						 memset((void *)receive_code, 0, sizeof(receive_code));                        
   						 NEC_Trigger_cnt = 0;                
   				 }
                   break;
               case 2:
                   STOP_COUNT;
                   if (GET_COUNT > 3800 && GET_COUNT < 5500) {
                       NEC_Trigger_cnt++;
                       bit_count = 0;
                       TIM2->CNT = 0;
                       memset((void *)receive_code, 0, sizeof(receive_code));
                   } 
   								else {
   										bit_count = 0;
   										TIM2->CNT = 0;
   										memset((void *)receive_code, 0, sizeof(receive_code));                        
   										NEC_Trigger_cnt = 0;
                   }
                   break;
               default: {
                   if (NEC_TRIGGER)
                       START_COUNT;
                   else {
                       STOP_COUNT;
                       if (GET_COUNT > 420 && GET_COUNT < 680) {
                       } 
                       else if (GET_COUNT > 1450 && GET_COUNT < 1880)
                        {
                           receive_code[bit_count / 8] |= 1 << (bit_count % 8);      										
                       } else 
                       {
   					   bit_count = 0;
   					   TIM2->CNT = 0;
   					   memset((void *)receive_code, 0, sizeof(receive_code));                        
   						NEC_Trigger_cnt = 0;
                       }
                       bit_count++;
                       if (bit_count >= 31)
                        {
   						long_count++;
   						StateValue.NEC_KEY_STATE.value        = receive_code[2];
   				        if(receive_code[2] == START_NEC||receive_code[2] == STOP_NEC)
   						{
   						long_count = 0;
   						StateValue.NEC_KEY_STATE.isNeedUpdate = 1;
   						StateValue.NEC_KEY_STATE.isExcute     = 0;
   						}
   						vs1838b_time_cnt = 0;
   						bit_count   = 0;                       
                          NEC_Trigger_cnt = 0;
   						nec_time_cnt = 0;
   						vs1838b_time_cnt = 0;
   					   memset((void *)receive_code, 0,sizeof(receive_code));
                       }
                   }
               }
           }
   
           EXTI_ClearITPendingBit(EXTI_Line8); // clear interrupt flag
       }		 
   		 
   		if (EXTI_GetITStatus(EXTI_Line6) != RESET){
   			Reset_Error9 = true;
   		 EXTI_ClearITPendingBit(EXTI_Line6);
   		
   		}
   }

上面代码的大概思路是每次信号跳变会触发中断,每次中断会记录上次到本次信号跳变的时间,如果是9ms的低电平,NEC_Trigger_cnt++,代表下一次电平信号保持时长应该为4.5ms,依次类推,每次NEC_Trigger_cnt++都代表NEC信号解析到下一个电平信号跳变,如果不符合NEC协议信号的时长要求NEC_Trigger_cnt立即清0。
至此VS1838B总结完毕。

自行转载即可,转载请注明出处
  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32L151C8T6是一款32位的低功耗微控制器,而OLED是一种有机发光二极管显示技术。那么,STM32L151C8T6OLED例程是指在STM32L151C8T6微控制器上运行的用于控制OLED显示屏的示例程序。 该例程一般会提供以下功能: 1. 初始化:首先,例程会初始化STM32L151C8T6的相关硬件资源和寄存器,以确保能够正常驱动OLED显示屏。 2. 图像显示:例程会将一些预定义的图像或文字信息显示在OLED屏幕上,这些图像可能是预先在程序中定义好的,或者是从外部存储器中读取的。 3. 动画效果:该例程可能还会包含一些简单的动画效果,比如滚动、淡入淡出等,以增加显示效果的丰富性和吸引力。 4. 用户交互:为了让用户能够与OLED屏幕进行交互,该例程可能还会实现一些响应用户输入的功能,如按键触发、触摸屏控制等。 通过运行STM32L151C8T6OLED例程,我们可以了解如何使用STM32L151C8T6的库函数或底层驱动来控制OLED显示屏,以及如何设计和实现一些简单的图像和动画效果。这对于学习和开发基于STM32L151C8T6和OLED的嵌入式系统或物联网应用非常有帮助。 需要注意的是,每个具体的STM32L151C8T6OLED例程可能会有所不同,上述功能仅为一般而言。因此,在使用该例程之前,需要详细阅读相关文档和源代码,并根据具体需求进行适当的修改和调整。 ### 回答2: STM32L151C8T6 是一款低功耗的ARM Cortex-M3处理器,内置了丰富的外设和功能。而OLED (Organic Light Emitting Diode) 则是一种比传统LCD更加高亮度和低功耗的显示屏技术。 在STM32L151C8T6上,我们可以通过使用OLED显示屏来实现各种应用,比如显示文本、图形和动画等。为了简化开发过程,我们可以使用OLED例程来快速开始开发。 首先,我们需要连接STM32L151C8T6和OLED模块。在连接过程中,我们需要确保正确连接模块的供电电源和通信接口。一旦连接完成,我们就可以开始编写例程。 在OLED例程中,我们可以使用STM32标准外设库或者HAL库来驱动OLED显示屏。首先,我们需要进行初始化设置,包括设置GPIO引脚、I2C或SPI通信等。接下来,我们可以通过发送特定命令和数据来控制OLED显示屏的亮度、对比度、显示内容和颜色等。 在例程中,我们可以使用各种函数和指令来控制OLED的各个方面,比如写入文本、绘制图形和显示动画等。我们可以使用系统提供的API来设置显示坐标、字体、颜色等参数,进而实现各种复杂的显示效果。 例程的好处是它提供了一个基本的框架和示例代码,可以帮助开发者快速上手并理解如何使用STM32L151C8T6和OLED模块。同时,也可以根据实际需求进行修改和扩展。 总结起来,STM32L151C8T6可以通过使用OLED例程来实现各种功能,包括文本显示、图形绘制和动画展示等。使用例程可以简化开发过程,并提供基本的代码框架和示例,帮助开发者快速上手。 ### 回答3: STM32L151C8T6是一款低功耗微控制器,搭载Cortex-M3内核。本篇例程涉及到OLED显示屏的使用。 首先,我们需要按照提供的连接图,将OLED显示屏与STM32L151C8T6进行正确连接。通常情况下,OLED显示屏会通过I2C接口和STM32L151C8T6进行通信。 接着,我们需要在开发环境中创建一个新的STM32L151工程,并导入相应的头文件和库文件。确保在代码中包含了OLED显示屏的驱动程序。这些驱动程序可以从STM官方网站或其他开源项目中获得。 在主程序中,我们应该初始化OLED显示屏和I2C通信。初始化过程包括设置I2C总线速度、OLED显示屏初始化设置等。 接下来,我们可以开始在OLED上显示文本、图像等内容。通过调用相应的函数,我们可以将要显示的内容传递给OLED驱动程序并实现显示。例如,我们可以使用printf函数将一条字符串打印到OLED上。 最后,在程序结尾处,我们应该关闭I2C通信并释放相关的资源。 在编写程序之前,建议阅读并理解STM32L151C8T6和OLED显示屏的数据手册和技术规格,以了解其具体的功能和寄存器设置等方面的细节。 总结而言,STM32L151C8T6与OLED显示屏的例程涉及到硬件连接、初始化设置和数据传输等方面的内容。通过编写适当的代码,我们可以实现在OLED显示屏上显示文字、图像等功能。这有助于我们更好地了解和使用STM32L151C8T6微控制器和OLED显示屏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值