【Funpack S5 #1】用FRDM-MCXA346开发板设计自动追光装置

一、项目介绍

本项目使用恩智浦的FRDM-MCXA346开发板及步进电机驱动板、硅光电池实现对最强光源的追踪。

1.1硬件介绍

本项目使用以下硬件构成:

·FRDM-MCXA346

这是恩智浦公司推出的低成本Freedom 开发板,基于高性能 MCX A346 微控制器(Arm Cortex-M33)。 该开发板提供丰富的板载资源、完整的 Arduino 兼容引脚排列、OpenSDA 调试器,以及多种扩展接口, 是学习嵌入式开发、快速原型设计和产品验证的理想平台。

·ULN2003驱动板

用于驱动步进电机。驱动板使用达林顿驱动集成块ULN2003构成,简单实用,输入端兼容单片机数字接口。可以用于低压、简单电机驱动的应用。

·步进电机

是两相五线步进电机,型号为28BYJ-48,步进角度为1.8度,工作电压5V。

·TFT显示屏

用于显示装置工作信息。本项目中使用的是1.8寸的TFT显示屏,SPI接口,分辨率为120*160,驱动核心是ST7735。

·太阳能电池

用于实时检测光强。实际上就是一小块太阳能电池板,是从电子玩具上拆下来的器件。

·干簧管

用于检测电机的归零位置

1.2功能概览

·装置上电

·驱动电机正反转,测试驱动是否有问题

·电机归位

·驱动电机转动,并在转动过程中获取位置的光强,记录最大值。

·完成180度旋转后,回转到光强最大位置处

1.3设计思路

实现功能预览的全部功能,实现步骤如下:

1)准备硬件和软件开发环境

2)连接太阳能电池、步进电机驱动板、TFT显示屏到主控板FRDM-MCXA346上,连接步进电机到步进电机驱动板。

3)编写代码,实现TFT显示屏正常显示;实现对步进电机正反转的驱动;使用MCXA346的ADC功能实现太阳能电池板的电压采集并显示。

4)完成上述功能后,将所有配件组装,做完整测试。

二、功能实现

2.1 装置构成

2.2软件流程图

2.3实现过程

1)TFT显示屏

为了能让TFT显示屏正常显示信息,需要对它进行必要的初始化。这个显示屏使用SPI接口,所以需要主控板FRDM-MCXA346提供必要的GPIO口资源。我按照以下方式设置FRDM-MCXA346开发板和TFT显示屏之间连接

FRDM-MCXA346 TFT显示屏

=======================================

P1_6

P1_7 RESET

P1_3 CS

P1_1 SCK

P1_2 DC

P1_0 SDA

3V3 VCC

GND GND

3V3 LED

=======================================

由于TFT显示屏只使用数据输入,不需要输出,所以J6中的标识为MISO的功能,改用为DC(命令、数据寄存器选择控制线)。

初始化的代码如下:

    // TFT显示屏使用的SPI口用到的GPIO
    //------------------------------------------------------------------------------------------------
    gpio_pin_config_t output_config = {
        .pinDirection = kGPIO_DigitalOutput,
        .outputLogic = 0U
    };
    GPIO_PinInit(GPIO1, PIN_SDO, &output_config); // SDA
    GPIO_PinInit(GPIO1, PIN_SCK, &output_config); // SCK
    GPIO_PinInit(GPIO1, PIN_CS, &output_config); // CS
    GPIO_PinInit(GPIO1, PIN_RS, &output_config); // DC
    GPIO_PinInit(GPIO1, PIN_RST, &output_config); // RESET
 
    const port_pin_config_t OUT = {/* Internal pull-up/down resistor is disabled */
            .pullSelect = kPORT_PullDisable,
            /* Low internal pull resistor value is selected. */
            .pullValueSelect = kPORT_LowPullResistor,
            /* Fast slew rate is configured */
            .slewRate = kPORT_FastSlewRate,
            /* Passive input filter is disabled */
            .passiveFilterEnable = kPORT_PassiveFilterDisable,
            /* Open drain output is disabled */
            .openDrainEnable = kPORT_OpenDrainDisable,
            /* Low drive strength is configured */
            .driveStrength = kPORT_LowDriveStrength,
            /* Normal drive strength is configured */
            .driveStrength1 = kPORT_NormalDriveStrength,
            /* Pin is configured as P3_18 */
            .mux = kPORT_MuxAlt0,
            /* Digital input enabled */
            .inputBuffer = kPORT_InputBufferEnable,
            /* Digital input is not inverted */
            .invertInput = kPORT_InputNormal,
            /* Pin Control Register fields [15:0] are not locked */
            .lockRegister = kPORT_UnlockRegister};
    // P1口和SPI有关的GPIO:P1_0,P1_1,P1_3
    PORT_SetPinConfig(PORT1, PIN_SDO, &OUT);
    PORT_SetPinConfig(PORT1, PIN_SCK, &OUT);
    PORT_SetPinConfig(PORT1, PIN_CS,  &OUT);
    PORT_SetPinConfig(PORT1, PIN_RS,  &OUT);
    PORT_SetPinConfig(PORT1, PIN_RST, &OUT);
    //-------------------------------------------------------------------------------------------------

显示信息使用Gui_DrawFont_GBK16函数。

void Gui_DrawFont_GBK16(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s)
形如:

Gui_DrawFont_GBK16(0,0,WHITE,BLACK, " EETREE ");
2)太阳能电池电压的检测

太阳能电池电压的检测处理,用到了MCXA346的ADC功能。本项目中使用J6的AN口,即P1_6口。根据数据手册,P1_6可以用作ADC输入,因此需要在初始化阶段追加以下设置:

   /* ADC0 peripheral is released from reset */
    RESET_ReleasePeripheralReset(kADC0_RST_SHIFT_RSTn);
 
    // ADC采集口P1_6 : ADC0_A22
    const port_pin_config_t port1_6_config = {/* Internal pull-up/down resistor is disabled */
                                                    .pullSelect = kPORT_PullDisable,
                                                    /* Low internal pull resistor value is selected. */
                                                    .pullValueSelect = kPORT_LowPullResistor,
                                                    /* Fast slew rate is configured */
                                                    .slewRate = kPORT_FastSlewRate,
                                                    /* Passive input filter is disabled */
                                                    .passiveFilterEnable = kPORT_PassiveFilterDisable,
                                                    /* Open drain output is disabled */
                                                    .openDrainEnable = kPORT_OpenDrainDisable,
                                                    /* Low drive strength is configured */
                                                    .driveStrength = kPORT_LowDriveStrength,
                                                    /* Normal drive strength is configured */
                                                    .driveStrength1 = kPORT_NormalDriveStrength,
                                                    /* Pin is configured as ADC1_A8 */
                                                    .mux = kPORT_MuxAlt0,
                                                    /* Digital input disabled; it is required for analog functions */
                                                    .inputBuffer = kPORT_InputBufferDisable,
                                                    /* Digital input is not inverted */
                                                    .invertInput = kPORT_InputNormal,
                                                    /* Pin Control Register fields [15:0] are not locked */
                                                    .lockRegister = kPORT_UnlockRegister};
    /* PORT1_10 (pin 3) is configured as ADC0_A22 */
PORT_SetPinConfig(PORT1, 6U, &port1_6_config);

并在主程序中做一些特殊处理,

//================================ADC Begin===================================
PRINTF("ADC init\r\n");
 
LPADC_GetDefaultConfig(&mLpadcConfigStruct);
/* Set to highest power level here, users need to properly match ADC clock and power level according
* to application requirements. For specific correspondence, please refer to the data sheet. */
 
mLpadcConfigStruct.powerLevelMode = kLPADC_PowerLevelAlt4;
 
mLpadcConfigStruct.enableAnalogPreliminary = true;
 
mLpadcConfigStruct.referenceVoltageSource = DEMO_LPADC_VREF_SOURCE;
 
 
mLpadcConfigStruct.conversionAverageMode = kLPADC_ConversionAverage128;
 
LPADC_Init(DEMO_LPADC_BASE, &mLpadcConfigStruct);
 
/* Request LPADC calibration. */
LPADC_DoOffsetCalibration(DEMO_LPADC_BASE); /* Request offset calibration, automatic update OFSTRIM register. */
 
/* Request auto calibration (including gain error calibration and linearity error calibration). */
LPADC_DoAutoCalibration(DEMO_LPADC_BASE);
 
/* Set conversion CMD configuration. */
LPADC_GetDefaultConvCommandConfig(&mLpadcCommandConfigStruct);
mLpadcCommandConfigStruct.channelNumber = DEMO_LPADC_USER_CHANNEL;
mLpadcCommandConfigStruct.conversionResolutionMode = kLPADC_ConversionResolutionHigh;
 
LPADC_SetConvCommandConfig(DEMO_LPADC_BASE, DEMO_LPADC_USER_CMDID, &mLpadcCommandConfigStruct);
 
/* Set trigger configuration. */
LPADC_GetDefaultConvTriggerConfig(&mLpadcTriggerConfigStruct);
mLpadcTriggerConfigStruct.targetCommandId = DEMO_LPADC_USER_CMDID;
mLpadcTriggerConfigStruct.enableHardwareTrigger = false;
LPADC_SetConvTriggerConfig(DEMO_LPADC_BASE, 0U, &mLpadcTriggerConfigStruct); /* Configurate the trigger0. */
 
PRINTF("ADC Full Range: %d\r\n", g_LpadcFullRange);
//================================ADC End===================================

采集ADC数据,使用以下方式处理:

       // 触发ADC
        LPADC_DoSoftwareTrigger(DEMO_LPADC_BASE, 1U); /* 1U is trigger0 mask. */
        // 等待ADC转换完成
        while (!LPADC_GetConvResult(DEMO_LPADC_BASE, &mLpadcResultConfigStruct)) {
        }
        g_adcval = ((mLpadcResultConfigStruct.convValue) >> g_LpadcResultShift);
        g_mapAdcVal = map(g_adcval, 0, 57700, 0, 100);

3)驱动步进电机

步进电机需要独立的四条控制线,占用四个GPIO口。初始化代码与驱动LED的方式一致。

4)电机归零位置控制

电机转动过程中,通过与转动轴一起运动的磁铁触发常开的干簧管闭合,产生位置归零信号。占用一个GPIO口。初始化代码与驱动LED的方式一致。

三、功能展示

视频效果演示

四、总结

遇到的问题

1、最开始准备使用MCXA346的SPI外设驱动TFT显示屏,以为与MCXN947应该一样,问题不大。结果调试时除了问题,总也调不通。后来索性改用软件模拟的方式处理。

2、ADC处理部分,原本打算使用中断方式获取数据,但是因为这样做的话,因为驱动电机的步进已经使用了中断,要考虑中断嵌套的问题,处理逻辑会变得复杂,容易引起混乱。所以直接写在主程序的循环中了。

心得体会

在使用FRDM-MCXA346之前,用过FRDM-MCXN947,以为二者应该没有太大的区别。然而实际开发的时候,发现二者的开发包还是不完全一样的,具体体现在对GPIO口、外设的初始化处理上。要想完全掌握,需要用心再学习,不能简单照搬使用。

相关帖子链接: Funpack5-1 - 用FRDM-MCXA346开发板设计自动追光装置