【Digikey得捷好物畅享】ADI MAX78000FTHR# 开发板以软件模拟方式点亮 I2C接口OLED

一、项目介绍
使用MAX78000开发板以软件模拟I2C方式驱动OLED。
1.1、准备开发环境
以MAX78000为核心的开发板,除了FTHR_RevA模式的,还有一款EvKit_V1模式的,因此在MaximSDK中导入从网上下载得到的SDK例程时,注意要修改必要的设置,才能保证示例工程正常运行。我没有仔细验证到底改哪几个是有效的,只是单纯地把所有“EvKit_V1”关键字改为“FTHR_RevA”。设计以下几个文件:
1)工程目录.settings\org.eclipse.cdt.core.prefs文件
environment/project/cdt.managedbuild.toolchain.gnu.cross.base.1028364529/BOARD/value=EvKit_V1
2)工程目录.vscode\settings.json文件
“board”:“EvKit_V1”,
3)工程目录\Makefile文件
BOARD ?= EvKit_V1
4)工程目录\project.mk文件
#BOARD=FTHR_RevA
去掉注释符号“#”即可,或者新追加一行“BOARD=FTHR_RevA”
我是在GPIO例程的基础上进行修改的。
1.2、硬件构成

名称 型号 作用
显示部件 0.96寸OLED 显示调试信息
主控板 MAX78000FTHR# 加载驱动、控制显示内容

二、功能的实现
2.1、电气连接
选择接口线少的四线OLED,电气连接如下:

2.2、设计思路(含后期追加的电机控制部分)
使用AIN3(P2.3)和AIN4(P2.4)模拟I2C接口的SCL和SDA,同时,使用板载的SW1和SW2按钮,以GPIO中断的方式,处理后面驱动步进电机时的控制转向和是否转动的动作。并通过OLED和板载的三个颜色的LED显示处理结果。

对象 作用
SW1 切换电机的顺时针转动或者逆时针转动。预定给TMX2209的DIR控制用。
SW2 通过标志变量。切换0/1状态,控制定时器3是否输出占空比为50%的PWM波。预定给TMX2209的STEP控制用。
红色LED 显示PWM波的输出状态。
绿色LED 步进电机顺时针转动状态下的指示灯。
蓝色LED 步进电机逆时针转动状态下的指示灯。
P2.7 切换步进电机的顺时针转动或者逆时针转动的实际输出控制。接TMX2209的DIR。
P2.6 输出控制步进电机步进用的脉冲输出。接TMX2209的STEP。

目前测试还没有连接TMC2209。仅仅是变成测试。
2.3、程序代码
主程序如下:

/**
 * @file        main.c
 * @brief       GPIO Example
 * @details
 */

/***** Includes *****/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "board.h"
#include "mxc_device.h"
#include "mxc_delay.h"
#include "nvic_table.h"
#include "i2c.h"
#include "tmr.h"

#include "oled.h"
#include "tmc2209.h"

extern MOTOR_DIR_Type 	motor_dir;			// 马达方向, P0.17
extern MOTOR_RUN_Type 	motor_run;			// 马达启停,

/***** SW1中断函数 *****/
uint32_t speed = 2;
void sw1_isr(void* cbdata) {
//    //mxc_gpio_cfg_t* cfg = cbdata;
//    //MXC_GPIO_OutToggle(cfg->port, cfg->mask);
	setDirToggle();
	//setSpeed(++speed);
}

/***** SW2中断函数 *****/
void sw2_isr(void* cbdata) {
	setRunToggle();
}

void setInputKey(void) {
	// 马达转动方向控制按钮
	mxc_gpio_cfg_t sw1_pin  = {MXC_GPIO0, MXC_GPIO_PIN_2,  MXC_GPIO_FUNC_IN, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIOH};

	// 启停定时器3控制按钮,使马达运行转动、停止
	mxc_gpio_cfg_t sw2_pin  = {MXC_GPIO1, MXC_GPIO_PIN_7,  MXC_GPIO_FUNC_IN, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIOH};

	// 设置中断:P0.2- SW1
	sw1_pin.pad   = MXC_GPIO_PAD_PULL_UP;
	sw1_pin.func  = MXC_GPIO_FUNC_IN;
	sw1_pin.vssel = MXC_GPIO_VSSEL_VDDIOH;

    MXC_GPIO_Config(&sw1_pin);
    //MXC_GPIO_RegisterCallback(&sw1_pin, sw1_isr, &red_led_pin);
    MXC_GPIO_RegisterCallback(&sw1_pin, sw1_isr, &sw1_pin);
    MXC_GPIO_IntConfig(&sw1_pin, MXC_GPIO_INT_FALLING);
    MXC_GPIO_EnableInt(sw1_pin.port, sw1_pin.mask);
    NVIC_EnableIRQ(MXC_GPIO_GET_IRQ(MXC_GPIO_GET_IDX(sw1_pin.port)));

    // 设置输入按钮:P1.7 - SW2
    sw2_pin.pad   = MXC_GPIO_PAD_PULL_UP;
    sw2_pin.func  = MXC_GPIO_FUNC_IN;
    sw2_pin.vssel = MXC_GPIO_VSSEL_VDDIOH;
    MXC_GPIO_Config(&sw2_pin);
    //MXC_GPIO_RegisterCallback(&sw2_pin,     sw2_isr,    &blue_led_pin);
    MXC_GPIO_RegisterCallback(&sw2_pin,     sw2_isr,    &sw2_pin);
    MXC_GPIO_IntConfig(&sw2_pin,  MXC_GPIO_INT_FALLING);
    MXC_GPIO_EnableInt(sw2_pin.port,   sw2_pin.mask);
    NVIC_EnableIRQ(MXC_GPIO_GET_IRQ(MXC_GPIO_GET_IDX(sw2_pin.port)));
}

int main(void)  {
	MOTOR_DIR_Type 		old_motor_dir = -1;			// 马达方向, P0.17
	MOTOR_RUN_Type 	oldmotor_run = -1;			// 马达启停,
	int flagFresh = 0;
	MXC_Delay(MXC_DELAY_MSEC(500)); //Wait for PMIC to power-up
	MXC_SYS_Clock_Select(MXC_SYS_CLOCK_IPO);
	SystemCoreClockUpdate();

    // 初始化OLED
    printf("\r\nOLED Init");
    OLED_init();
    OLED_clear();

    OLED_ShowString(0,   0, (char *)"  EEWORLD & DigiKey", 12, 1);

    MXC_Delay(MXC_DELAY_SEC(1));

	// 设置输入Key
	setInputKey();

	// 初始状态:马达不转(不启动定时器3,顺时针转)
	tmc2209_Init();

	printf("\n Ready! \n");
    OLED_Refresh();
	while (1) {


		MXC_Delay(MXC_DELAY_MSEC(50));
		// 动态显示当前步进电机的转向和转动状态
		if (old_motor_dir  != motor_dir) {
			old_motor_dir = motor_dir;
			flagFresh = 1;
			// 方向:CW-顺时针,CCW-逆时针
			if (motor_dir ==Shun ) {
				OLED_ShowString(10,   21, (char *)"  CW  ", 16, 0);
				OLED_ShowString(70,   21, (char *)" CCW  ", 16, 1);
			} else {
				OLED_ShowString(10,   21, (char *)"  CW  ", 16, 1);
				OLED_ShowString(70,   21, (char *)" CCW  ", 16, 0);
			}
		}

		if (oldmotor_run !=  motor_run) {
			oldmotor_run = motor_run;
			flagFresh = 1;
			// 转动状态:Stop-停止;Run-转动
			if (motor_run ==Stop ) {
				OLED_ShowString(10,   40, (char *)" RUN  ", 16, 1);
				OLED_ShowString(70,   40, (char *)" STOP ", 16, 0);
			} else {
				OLED_ShowString(10,   40, (char *)" RUN  ", 16, 0);
				OLED_ShowString(70,   40, (char *)" STOP ", 16, 1);
			}
		}
		if (flagFresh == 1) {
			OLED_Refresh();
			flagFresh = 0;
		}

	}

    return 0;
}

定时器3负责周期产生PWM脉冲信号,作为TMC2209 的STEP步进脉冲信号。主要代码如下:

// 定时器中断
void Timer3Handler(void) {
    MXC_TMR_ClearFlags(Basic_TIMER);
    if (motor_run == Run) {
    	MXC_GPIO_OutToggle(red_led_pin.port,   red_led_pin.mask);
    	MXC_GPIO_OutToggle(motor_step_pin.port,   motor_step_pin.mask);

    } else {
    	MXC_GPIO_OutSet(red_led_pin.port,   red_led_pin.mask);
    	//MXC_GPIO_OutClr(motor_step_pin.port,   motor_step_pin.mask);
    }
}

void Timer3Init(void)  {
    mxc_tmr_cfg_t tmr;
    uint32_t periodTicks = MXC_TMR_GetPeriod(Basic_TIMER, Basic_CLOCK_SOURCE, 128, Basic_FREQ);

    MXC_TMR_Shutdown(Basic_TIMER);
    MXC_SYS_ClockDisable(MXC_SYS_PERIPH_CLOCK_TMR3);

    tmr.pres = TMR_PRES_128;
    tmr.mode = TMR_MODE_CONTINUOUS;
    tmr.bitMode = TMR_BIT_MODE_16A;
    tmr.clock = Basic_CLOCK_SOURCE;
    tmr.cmp_cnt = periodTicks; //SystemCoreClock*(1/interval_time);
    tmr.pol = 0;

    if (MXC_TMR_Init(Basic_TIMER, &tmr, false) != E_NO_ERROR) {
        printf("Failed Continuous timer Initialization.\n");
        return;
    }

    MXC_NVIC_SetVector(TMR3_IRQn, Timer3Handler);
    NVIC_EnableIRQ(TMR3_IRQn);
    MXC_TMR_EnableInt(Basic_TIMER);
    printf("Continuous timer started.\n");
    MXC_TMR_Start(Basic_TIMER);
}

三、测试效果

四、总结
定时器处理这一块,一开始我是参照TMR例程做的,遇到各种问题。例程中默认在FTHR_RevA开发板模式下使用的是TIMER4的,设置中断处理时又指向了TIMER5,总之是比较乱,不可用。我就改成了TIMER3。在TIMER3中,tmr.bitMode 不要使用 TMR_BIT_MODE_16B,也会出莫名其妙的问题。