一、项目介绍
本项目使用恩智浦的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的方式一致。
三、功能展示
<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=116170929741464&bvid=BV1f1PezWEro&cid=36451846334&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
四、总结
遇到的问题
1、最开始准备使用MCXA346的SPI外设驱动TFT显示屏,以为与MCXN947应该一样,问题不大。结果调试时除了问题,总也调不通。后来索性改用软件模拟的方式处理。
2、ADC处理部分,原本打算使用中断方式获取数据,但是因为这样做的话,因为驱动电机的步进已经使用了中断,要考虑中断嵌套的问题,处理逻辑会变得复杂,容易引起混乱。所以直接写在主程序的循环中了。
心得体会
在使用FRDM-MCXA346之前,用过FRDM-MCXN947,以为二者应该没有太大的区别。然而实际开发的时候,发现二者的开发包还是不完全一样的,具体体现在对GPIO口、外设的初始化处理上。要想完全掌握,需要用心再学习,不能简单照搬使用。