毕业以来也算稍微有几年了,期间主要使用ARM架构的芯片进行开发或者对着无刷电机程序调参。类似辉芒微(FMD)这种芯片可以说是第一次使用,在这里将开发例程简单梳理总结一下,希望能帮到后续接触到的人。
        当然,由于项目本身虽然简单但是同样涉密,所以此次分享不涉及原理图及具体完整代码的分享,还请各位看客多多谅解。
        本次项目为基于FT62FCC的触摸按键检测芯片进行一款交流有刷电机产品的实现,具体芯片型号虽与FT62FCC略有不同但从使用上基本一致。在硬件实现上,具体的I/O使用如下表所示。
 

输出(0)/输入(1) 功能
1 PA0 0 COM1
2 PA1 1 OSC1
3 PA2 1 OSC2
4 PA3 0 COM2
5 PA4 1 TIMER_Key3
6 PA5 1 IR_IN
7 PA6 1 ON/OFF_Key1
8 PA7 1 GL
9 PD0 1 SPEED_Key2
10 PD1 0 SEG_C
11 PB0 0 SEG_D
12 PB1 0 SEG_E
13 PB2 1 Sleep_Key5
14 PB3 1 OSC_Key4
15 PD4 0 SEG_G
16 PB4 0 SEG_F
17 PB5 0 SEG_COM
18 PB6 0 SEG_A
19 PB7 0 SEG_B
20 PC0 0 TRIAC_L
21 GND
22 PC1 0 TRIAC_ML
23 PC2 0 TRIAC_M
24 VDD
25 PC5 0 TRIAC_OSC
26 PC6 0 TRIAC_H
27 PD5 1 C_Ref
28 PC7 0 TRIAC_MH

从引脚分配可以看出,I/O使用和功能实现主要分为五个方面:

  1. 触摸按键检测 - 通过FMD官方软件VisualTouchTL进行配置
  2. 过零信号采集 - 利用芯片PORTA的边沿中断功能实现
  3. 电机驱动 - 根据采集到的过零信号来控制可控硅
  4. IR信号接收 - 通过TIM2定时器中断检测IR端口电平变化实现解码
  5. 数码管与LED驱动 - 采用扫描方式实现多路LED共享驱动

一.触摸按键的调试
在项目的最初,我们需要对触摸按键电容的原理有所了解

基于该原理,我们可以知道触摸按键电容在具体的I/O端口上的变化并非简单的高低电平电平变化。在实际中,端口的变化为如下图所示(CH1黄线为触摸按键I/O信号)

此处我们可以借用FMD所提供的可视化软件进行配置。此处应当注意,若为中途接手或旧项目重新开发,应尽量使用与MCU出货日期相近版本的IDE和软件,否则可能出现ID校验失败导致烧录器无法烧录软件的情况。

下载安装并打开VisualTouchTL,具体的项目工程建立与使用可参考FMD官方视频
https://www.bilibili.com/video/BV1M84y1V7oj/?spm_id_from=333.1391.0.0&vd_source=e7de9cc04354bdc12789a3ad18ce1145
在成功选择芯片并建立工程后,我们根据需要对I/O进行配置得到如下界面。在当前界面中,由于触摸按键需求,CS和EMI为必选项。高灵敏为在源代码的基础上对滤波和采样进行了修改,后续可在代码文件touch.h中修改,可根据需求选择。低功耗为可选项,由于本次项目为AC供电项目,为开发便利,本次没有选择。

在这里,当我们配置完并调试按键状态为OK后,由于配置的先后顺序产生了Key1-Key5。此处的配置顺序尽量与开发者代码习惯相同,根据Key1-Key5的配置顺序后续对按键采集产生了回传数据0-5。
在生成代码后,通过代码我们可以知道

	while(1)
	{
		//清看门狗   
		CLRWDT();  
		//按键扫描
		TSC_Scan(); 
		if(TSC_DataProcessing()== 1) //返回1表示所有按键处理完成一次,请和TSC_Scan一起使用,否则影响扫描速度。
		{
			strongest = TSC_GetCsKey();
            single = TSC_GetLocalKey(); 				
            SleepProcess();			//睡眠处理函数需放置在if(TSC_DataProcessing()== 1)里面
         				
		}
                      
	}   

其中,由于函数中存在滤波算法的,请注意确保按键采集模块执行为实时的。

函数TSC_GetLocalKey更新了当前按键状态,回传值从Key1到Keyx依次对应了bit0到bitx。例如,当按键空闲状态下,该函数回传值为0;若Key1按下,bit0为1,回传值为1;若Key1和Key2同时按下,bit0和bit1为1,回传值为3,以此类推。
最强按键状态可以通过TSC_GetCsKey来进行更新,该函数回传值即为当前最强按键值。其中,在空闲状态下,该函数回传值为0;若Key1为最强按键,则回传值为1。依次类推,我们可以得到目前的按键状态。此外,在此处应注意,若有多个按键同时触发,则TSC_GetCsKey回传值为0。
因此,在函数处理上,如果我们要求不考虑多按键同时触发时,在采集按键时需要同时对TSC_GetLocalKey进行判断。在代码上可以做如下处理。

void RealKeyNum (void)
{
	//当且仅当按键空闲时更新按键
	if(LastNum == 0)
    {
		RelNum = strongest;
        LastNum = RelNum;
    }    
    else
    {	
		if(single == 0)
        {
			LastNum = single;
        }
    
    }

}	

while(1)
	{

		//按键扫描
		TSC_Scan(); 
		if(TSC_DataProcessing()== 1) //返回1表示所有按键处理完成一次,请和TSC_Scan一起使用,否则影响扫描速度。
		{
			strongest = TSC_GetCsKey(); //更新最强按键状态
            single = TSC_GetLocalKey(); //更新按键触发状态
            //睡眠处理函数需放置在if(TSC_DataProcessing()== 1)里面,若无休眠功能,可屏蔽 				
            SleepProcess();			
            RealKeyNum();           //此处最强按键保存在变量RelNum 
		}

        //在这之后根据RelNum进行进一步处理
        

	}   



另外,考虑到实际项目,我们在进行采集的状态分为裸PCB触发和整机状态触发两种状态,通过使用VisualTouchTL分别测量两种状态的按键值。
以同为高灵敏度为例,经过对比我们可得在裸PCB触发和整机状态触发有如下不同:

在多数情况下,若触发不灵,在确保硬件无异常的情况下,仅稍微上调有效阈值(如KeyX_ON)并下调无效阈值(如KeyX_OFF)即可扩大触发阈值范围。放大倍数尽可能维持高倍数来确保整机触发无异常。

二.过零信号的处理
由于项目使用PA7来采集过零信号,所以我们只能通过PORTA中断来进行信号采集。
此处需要注意的是,PORTA中断为I/O电平变化中断,即I/O从0-1或1-0均可产生中断。在此基础上,又由于所有PORTA共用标志位PAIE和PAIF,所以如果需要使用复数PORTA中断,在中断产生时需根据需要对具体I/O的电平进行采集来判断具体是哪一个PORTA产生中断。
在此基础上,为避免PORTA中断的误触,当交流项目使用多个PORTA中断时,我们应当确保I/O信号的平稳。即在AC接入时,使用隔离探头确保每个PORTA引脚浮空状态下无剧烈电平变化。

//MCU中断函数
void interrupt ISR(void) 
{
     //PORTA电平变化产生中断
	 if(PAIE && PAIF)
    {
		ReadAPin = PORTA; 	//读取PORTA数据
		PAIF = 0;  			//清PAIF标志位
		PAIE = 0;  			//暂先禁止PA0中断
		IOCA7 =0;  			//禁止PA0电平变化中断
        GLflag = 1;         //过零信号产生中断
    }	
}


//注意系统时钟、IO初始化中TRISA中PA7需要置1,WPUA中PAY置0
void PA7_Level_Change_INITIAL(void)
{
	ANSEL0 &= ~0x80;
	TRISA7 =1; 			     //SET PA0 INPUT
	ReadAPin = PORTA;	     //读取一下相应的端口寄存器
	PAIF =0;   			     //清PA INT中断标志位
    IOCA7 =1;  			     //使能PA7电平变化中断
	PAIE =1;   			     //使能PA INT中断
    GIE =1;    			 //使能全局中断
}

void main(void)
{
    while(1)
    {
        //过零采集
        PA7_Level_Change_INITIAL();

        //根据过零信号进行进一步控制
    }
}

三.可控硅控制
此处可讲的不多,根据过零信号"GLFLAG"来开关可控硅即可。注意实际控制中应当多次置高来确保信号的有效性,具体实现信号类似PWM波。
此处仅放几个实际波形图来示意。

                                              蓝线C3为I/O波形,粉线C2为AC交流电压

                                             蓝线C3为I/O波形,粉线C2为AC交流电压

四.IR信号驱动

在初始版本程序中,我尝试通过IR端口(PA5)的中断来启动TIM2定时器,利用定时器测量PA5引脚高低电平的持续时间以实现IR信号解码。但在实际测试中发现,当设备使用交流电源时,IR端口存在明显噪声干扰,导致无法准确测量PORTA的电平变化时间。因此最终改用TIM2定时中断的方式来实现IR信号解码。

                                       使用隔离探头可以观测到IR端口存在明显的杂波

                        当然,在IR和GND之间添加一枚合适的电容是可以增加信号质量的

由于项目的IR信号并非标准通讯协议,所以此处的IR信号有关代码仅做示意使用



void interrupt ISR(void) 
{
    //定时器2的中断处理**********************
	if(TMR2IE && TMR2IF)			//100us中断一次 = 5KHz
	{
		TMR2IF = 0;
        
        ir_decode_100us();         //IR数据采集 每100us判断一次IR电平
        
        Re_Irnum();                //IR按键处理
        
	}
}

//TIM1用作定时中断推进程序
//此处TIM2设置为每100us产生一次中断(16Mhz情况下)
void TIMER2_INITIAL (void) 
{    
    T2CON0  = 0B00000001; 			//T2预分频1:4,后分频1:1
    //BIT7: 0:无意义; 1:把PR2/P1xDTy缓冲值分别更新到PR2寄存器和P1xDTy_ACT
    //BIT6~BIT3: 定时器2输出后分频比选择 0000:1:1;0001:1:2;……1:16
    //BIT2:0:关闭定时器2;1:打开定时器2
    //BIT1~0:定时器2预分频选择 00:1;01:4;1x:16
    
	T2CON1  = 0B00000000;		   //T2时钟来自系统时钟,PWM1连续模式
	//BIT4: PWM模式选择 0:连续模式;1:单脉冲模式
    //BIT3: 0:PWM模式;1:蜂鸣器模式	
    //Timer2时钟源选择:000:指令时钟;001:系统时钟;010:HIRC的2倍频;100:HIRC;101:LIRC 110-LP 111-XT
    						
    TMR2H  	= 0;					//定时器2计数寄存器,定时值=200*(4*2/16M)=200*0.5us=100us
    TMR2L  	= 0;
		
	PR2H = 0;
	PR2L = 200;						//TIMER2周期寄存器

	 
	TMR2IF  = 0;					//清TMER2中断标志
	TMR2IE = 1;						//使能TMER2的中断(配置成timer定时器时不注释)
	TMR2ON  = 1;					//使能TMER2启动
	PEIE    = 1;    				//使能外设中断
	GIE     = 1;   					//使能全局中断
}

5.数码管与LED驱动
此处没有太多可以讲得,注意多COM驱动时需要先关闭多余COM的端口再进行驱动,否则用户体验上可能会看到残留灯光
以驱动数码管代码为例

// 数码管显示函数
void display_digit(uint num) {
    // 关闭所有COM端 (共阴数码管)
    COM1 = 1;
    COM2 = 1;
    COM3 = 1;
    
    // 数字0-9的段码映射表 (1=点亮, 0=熄灭)
    // 顺序: A,B,C,D,E,F,G
    static const uint digit_map[10] = {
        0b1111110, // 0: ABCDEF
        0b0110000, // 1: BC
        0b1101101, // 2: ABDEG
        0b1111001, // 3: ABCDG
        0b0110011, // 4: BCFG
        0b1011011, // 5: ACDFG
        0b1011111, // 6: ACDEFG
        0b1110000, // 7: ABC
        0b1111111, // 8: ABCDEFG
        0b1111011  // 9: ABCDFG
    };
    
    // 验证输入范围 (0-9)
    if(num > 9) return;
    
    // 获取当前数字的段码
    uint8_t seg_pattern = digit_map[num];
    
    // 设置各段状态 (从高位到低位: A→B→C→D→E→F→G)
    SEG_A = (seg_pattern & 0b1000000) ? 1 : 0;
    SEG_B = (seg_pattern & 0b0100000) ? 1 : 0;
    SEG_C = (seg_pattern & 0b0010000) ? 1 : 0;
    SEG_D = (seg_pattern & 0b0001000) ? 1 : 0;
    SEG_E = (seg_pattern & 0b0000100) ? 1 : 0;
    SEG_F = (seg_pattern & 0b0000010) ? 1 : 0;
    SEG_G = (seg_pattern & 0b0000001) ? 1 : 0;
    
    // 开启当前COM端 (COM3控制目标数码管)
    COM3 = 0;
}

以上便是基于辉茫微芯片FT62FCC的应用与解析。鉴于个人水平所限,文中若有任何疏漏或代码优化建议,诚邀各位读者批评指正。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐