J. Abate
Published

MiniZed Motor Control with I2C Feature

Control MM10 motor using MiniZed's onboard accelerometer

BeginnerFull instructions provided1 hour8
MiniZed Motor Control with I2C Feature

Things used in this project

Hardware components

MiniZed
Avnet MiniZed
×1
Pmod HB3
Digilent Pmod HB3
×1
MM10 3V DC Motor
×1
Jumper wires (generic)
Jumper wires (generic)
×3

Software apps and online services

Vivado Design Suite
Xilinx Vivado Design Suite
Xilinx - Vitis Unified Software Platform

Story

Read more

Schematics

I2C Motor Control Processor System Configuration

Code

mz_i2c_mtrctrl.xdc

Plain text
minized i2c motor control pin constraints
set_property PACKAGE_PIN M15 [get_ports {TTC0_WAVE0_OUT_0     }];  
set_property PACKAGE_PIN L15 [get_ports {GPIO_O_0     }];  
set_property IOSTANDARD LVCMOS33 [get_ports TTC0_WAVE0_OUT_0]
set_property IOSTANDARD LVCMOS33 [get_ports GPIO_O_0]

set_property PACKAGE_PIN G15 [get_ports iic_rtl_scl_io]
set_property PACKAGE_PIN F15 [get_ports iic_rtl_sda_io]

set_property IOSTANDARD LVCMOS33 [get_ports iic_rtl_scl_io]
set_property IOSTANDARD LVCMOS33 [get_ports iic_rtl_sda_io]

i2c_mtrctrl.c

C/C++
#include <stdio.h>
#include <unistd.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xiic.h"
#include "xgpiops.h"
#include "xil_exception.h"
#include "xttcps.h" // triple timer counter
#include "xscugic.h" // generic input controller

// LIS2DS12 data sheet pp35
#define SA0_HI_ADDR  		0x1D
#define SA0_LO_ADDR  		0x1E

// LIS2DS12 data sheet pp40
#define CTRL_REG_1_ADDR  	0X20
#define CTRL_REG_2_ADDR  	0X21
#define CTRL_REG_4_ADDR  	0X23
		
#define ACC_STATUS_REG_ADDR 0X27
#define ACC_X_LSB_REG_ADDR  0X28
#define ACC_X_MSB_REG_ADDR  0X29
#define ACC_Y_LSB_REG_ADDR  0X2A
#define ACC_Y_MSB_REG_ADDR  0X2B
#define ACC_Z_LSB_REG_ADDR  0X2C
#define ACC_Z_MSB_REG_ADDR  0X2D

// LIS2DS12 data sheet pp43
#define WHOAMI_ID_REG_ADDR	0X0F
#define WHO_AM_I_FIXED		0x43

// XPAR params from xparameters.h
#define IIC_BASE_ADDR	XPAR_IIC_0_BASEADDR
u8 		IIC_DEV_ADDR = SA0_HI_ADDR; //by default

// defs for mtr ctrl PS input locations
#define GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_0_DEVICE_ID
#define	TICK_TIMER_FREQ_HZ	100
#define TTC_TICK_DEVICE_ID	XPAR_XTTCPS_0_DEVICE_ID
#define TTC_TICK_INTR_ID	XPAR_XTTCPS_0_INTR

int byte_idx;
u8 send_byte;
u8 w_reg_addr [256];

// motor ctrl function setups
static void TickHandler(void *CallBackRef);
int SetupTicker(XTtcPs *TtcPsInst,u16 DeviceID,u16 TtcTickIntrID,XScuGic *InterruptController);
static int SetupInterruptSystem(u16 IntcDeviceID,XScuGic *IntcInstancePtr);
int SetupTimer(u16 DeviceID,XTtcPs *TtcPsInst);
void set_pwm(u32 cycle);
void display_menu();

typedef struct {
u32 OutputHz;	/* Output frequency */
XInterval Interval;	/* Interval value */
u8 Prescaler;	/* Prescaler value */
u16 Options;	/* Option settings */
} TmrCntrSetup;

XIic IicInstance;	// IIC device inst
XGpioPs Gpio;
XGpioPs_Config *ConfigPtr;
XTtcPs_Config *TtcConfig;
XTtcPs ttcTimer;
TmrCntrSetup *TimerSetup;
XScuGic InterruptController;  	/* Interrupt controller instance */
XTtcPs TtcPsInst;
u32 MatchValue;
static TmrCntrSetup SettingsTable={TICK_TIMER_FREQ_HZ, 0, 0, 0};

// -------------------------IIC---------------------------------

int iic_wr(u8 reg_offst, u8 d_wr) {
	int status = XST_SUCCESS;
	u8 tx_buf[2];
	int tx_len = sizeof(tx_buf);
	int ByteCount = 0;

	tx_buf[0]=reg_offst;
	tx_buf[1]=d_wr;

	ByteCount = XIic_Send(IIC_BASE_ADDR, IIC_DEV_ADDR, tx_buf, tx_len, XIIC_STOP);
	if (ByteCount != tx_len) status = XST_FAILURE;

	return (status);
}

int iic_rd(u8 reg_offst, u8 *d_rd){
	int status = XST_SUCCESS;
	u8 tx_buf[1];
	int tx_len = sizeof(tx_buf);
	u8 rx_buf[1];
	int rx_len = sizeof(rx_buf);
	int ByteCount;

	tx_buf[0] = reg_offst;
	ByteCount = XIic_Send(IIC_BASE_ADDR, IIC_DEV_ADDR, tx_buf, tx_len, XIIC_STOP); // send register offst
	if (ByteCount != tx_len) status = XST_FAILURE;

	ByteCount = XIic_Recv(IIC_BASE_ADDR, IIC_DEV_ADDR, rx_buf, rx_len, XIIC_STOP); // receive 1 byte into rx_buf
	if (ByteCount != rx_len) status = XST_FAILURE;
	if (status == XST_SUCCESS) *d_rd = rx_buf[0];
	return(status);
}

int signed_to_int(u16 din)
{
	u16 dout_16b;
	int dout_14b;
	int sign;

	// check if MSB 1000_000
	if (din & 0x8000) {
		sign = -1;
		dout_16b = (~din) + 1; // bit-wise invert, add 1
	} else {
		sign = 1;
		dout_16b = din;
	}

	dout_14b = sign * (int)(dout_16b >> 2); // data is left-aligned; convert 16-bit to 14-bit
	return(dout_14b);
}

void sensor_init (void) {
	u8 who_am_i;
	IIC_DEV_ADDR = SA0_HI_ADDR; //default

	iic_rd(WHOAMI_ID_REG_ADDR, &who_am_i); // reads data pointer into who_am_i
	printf("With I2C device address 0x%02x received WhoAmI = 0x%02x\r\n", IIC_DEV_ADDR, who_am_i);

	if (who_am_i != WHO_AM_I_FIXED) {
		IIC_DEV_ADDR = SA0_LO_ADDR;
		iic_rd(WHOAMI_ID_REG_ADDR, &who_am_i); // try the other address
		printf("With I2C device address 0x%02x received WhoAmI = 0x%02x\r\n", IIC_DEV_ADDR, who_am_i);
	}

	iic_wr(CTRL_REG_2_ADDR, 0x00);
	iic_wr(CTRL_REG_1_ADDR, 0x60); // Turn on the accelerometer.  14-bit mode, ODR = 400 Hz, FS = 2g
	printf("CTL1 = 0x60 written\r\n");
	iic_wr(CTRL_REG_4_ADDR, 0x01); // Acc data-ready interrupt on INT1
	printf("CTL4 = 0x01 written\r\n");
}

int acc_read() {

	u8 acc_status, d_rdy, d_rd_lsb, d_rd_msb;
	u16 x, y, z;
	int i_x, i_y, i_z, avg;

	d_rdy = 0;

	// data not ready
	while (!d_rdy) {
		iic_rd(ACC_STATUS_REG_ADDR, &acc_status);
		d_rdy = acc_status & 0x01; // bit 0 = d_rdy
	}

	iic_rd(ACC_X_LSB_REG_ADDR, &d_rd_lsb); // fetch reading
	iic_rd(ACC_X_MSB_REG_ADDR, &d_rd_msb); // fetch reading
	x = (d_rd_msb << 8) + d_rd_lsb; // shift d_rd_msb to [15:8]; add lsb to [7:0]
	// result is 16-bit signed word

	iic_rd(ACC_Y_LSB_REG_ADDR, &d_rd_lsb);
	iic_rd(ACC_Y_MSB_REG_ADDR, &d_rd_msb);
	y = (d_rd_msb << 8) + d_rd_lsb;

	iic_rd(ACC_Z_LSB_REG_ADDR, &d_rd_lsb);
	iic_rd(ACC_Z_MSB_REG_ADDR, &d_rd_msb);
	z = (d_rd_msb << 8) + d_rd_lsb;

	i_x = signed_to_int(x);
	i_y = signed_to_int(y);
	i_z = signed_to_int(z);

	//avg = (i_x + i_y + i_z)/3;
	return i_x;
	// printf("Avg acceleration (x,y,z) = %+5d\r\n", avg);
	// printf("  Acceleration = X: %+5d, Y: %+5d, Z: %+5d\r\n",i_x, i_y, i_z);
}

int scale (int acc_din){
	int max = 2600;
	int min = -2600;
	int scaled_read = abs(((acc_din - max)*100)/(max-min));
	int acc_dout;
	
	// limiter
	if (scaled_read>100){
		acc_dout = 100;
	} else {
		acc_dout = scaled_read;
	}
	
	return (acc_dout);
}

// -------------------------IIC---------------------------------

int main()
{
sensor_init();
int acc_din;
int avg_scaled;
int dc_adj;

u8 DutyCycle;
char key_input;
init_platform();
TmrCntrSetup SettingsTable= {TICK_TIMER_FREQ_HZ, 0, 0, 0};
	// lookup config
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	// initialize cfg
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
	// directionality
	XGpioPs_SetDirectionPin(&Gpio, 54, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, 54, 1);
	// write constant value
	XGpioPs_WritePin(&Gpio, 54, 0x1);

// serial terminal application output
printf("www.adiuvoengineering.com\n\r");
printf("DC Motor Control Example\n\r");
SetupInterruptSystem(INTC_DEVICE_ID, &InterruptController);
SetupTicker(&ttcTimer,TTC_TICK_DEVICE_ID,TTC_TICK_INTR_ID,&InterruptController);

while(1){
display_menu();
read(1, (char*)&key_input, 1);
printf("Echo %c\n\r",key_input);

// set speed LUT
switch (key_input) {
case '1': //stop
set_pwm(0);
break;
case '2': //25%
printf("25%\n\r");
DutyCycle = 25;
set_pwm(DutyCycle);
break;
case '3': //33%
DutyCycle = 33;
set_pwm(DutyCycle);
break;
case '4': //50%
DutyCycle = 50;
set_pwm(DutyCycle);
break;
case '5': //66%
DutyCycle = 66;
set_pwm(DutyCycle);
break;
case '6': //75%
DutyCycle = 75;
set_pwm(DutyCycle);
break;
case '7': //100%
DutyCycle = 100;
set_pwm(DutyCycle);
break;

case '8': //100%
acc_din = acc_read();
DutyCycle = scale(acc_din);
set_pwm(DutyCycle);
sleep(1);
break;

}
}
cleanup_platform();
return 0;
}

void display_menu()
{
//Clear the screen
printf("\033[2J");
//Display the main menu
printf("*******************************************\n");
printf("****      www.adiuvoengineering.com    ****\n");
printf("****      Motor Control Example        ****\n");
printf("*******************************************\n");
printf("\n");
printf("   MM10 Motor Control   \n");
printf("------------------------------------------\n");
printf("\n");
printf("Select a Speed:\n");
printf("  (1)   - Stop\n");
printf("  (2)   - 25%\n");
printf("  (3)   - 33%\n");
printf("  (4)   - 50%\n");
printf("  (5)   - 66%\n");
printf("  (6)   - 75%\n");
printf("  (7)   - 100%\n");
printf("  (8)   - accelerometer\n");
printf("\n");
}


// set speed function
void set_pwm(u32 cycle)
{
u32 MatchValue;
MatchValue = (TimerSetup->Interval * cycle) / 100;
XTtcPs_SetMatchValue(&ttcTimer, 0, MatchValue);
}


// setup timer
int SetupTicker(XTtcPs *TtcPsInst,u16 DeviceID,u16 TtcTickIntrID,XScuGic *InterruptController)
{
int Status;
TmrCntrSetup *TimerSetup;
XTtcPs *TtcPsTick;
TimerSetup = &SettingsTable;
TimerSetup->Options |= (XTTCPS_OPTION_INTERVAL_MODE |
XTTCPS_OPTION_MATCH_MODE | XTTCPS_OPTION_WAVE_POLARITY);
Status = SetupTimer(DeviceID,TtcPsInst);
if(Status != XST_SUCCESS) {
return Status;
}
TtcPsTick = TtcPsInst;
Status = XScuGic_Connect(InterruptController, TtcTickIntrID,
(Xil_InterruptHandler)TickHandler, (void *)TtcPsTick);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XScuGic_Enable(InterruptController, TtcTickIntrID);
XTtcPs_EnableInterrupts(TtcPsTick, XTTCPS_IXR_INTERVAL_MASK);
XTtcPs_Start(TtcPsTick);
return Status;
}


// interrupt ctrlr; connect ttc to controller
static int SetupInterruptSystem(u16 IntcDeviceID,XScuGic *IntcInstancePtr)
{
int Status;
XScuGic_Config *IntcConfig;
IntcConfig = XScuGic_LookupConfig(IntcDeviceID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
IntcInstancePtr);
Xil_ExceptionEnable();
return XST_SUCCESS;
}

// zynq interrupt timer
int SetupTimer(u16 DeviceID,XTtcPs *TtcPsInst)
{
int Status;
XTtcPs_Config *Config;
XTtcPs *Timer;
TmrCntrSetup *TimerSetup;
TimerSetup = &SettingsTable;
Timer = TtcPsInst;
Config = XTtcPs_LookupConfig(DeviceID);
if (NULL == Config) {
return XST_FAILURE;
}
Status = XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XTtcPs_SetOptions(Timer, TimerSetup->Options);
XTtcPs_CalcIntervalFromFreq(Timer, TimerSetup->OutputHz,
&(TimerSetup->Interval), &(TimerSetup->Prescaler));
XTtcPs_SetInterval(Timer, TimerSetup->Interval);
XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler);
return XST_SUCCESS;
}

// for interrupts
static void TickHandler(void *CallBackRef)
{
u32 StatusEvent;
/*
* Read the interrupt status, then write it back to clear the interrupt.
*/
StatusEvent = XTtcPs_GetInterruptStatus((XTtcPs *)CallBackRef);
XTtcPs_ClearInterruptStatus((XTtcPs *)CallBackRef, StatusEvent);
//printf("timer\n\r");
/*update the flag if interrupt has been occurred*/
//UpdateFlag = TRUE;
}

Credits

J. Abate

J. Abate

0 projects • 1 follower
Thanks to Avnet and Udhay Kumar.

Comments

Add projectSign up / Login