Can USB firmware or hardware interfere with PWM generation ?
I'm working on a device that uses Pulse-Width-Modulation to control a DRV2605L LRA/ERM haptic feedback motor driver (datasheet found here: https://www.ti.com/lit/ds/symlink/drv2605l.pdf?ts=1683824689205&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FDRV2605L)
The devices uses two DRV2605L, each connected to an STM32L552ZET6Q microcontroller via their own I2C bus (I2C1 & I2C2) and a PWM line (TIM1 Channel 1 & Channel 2 respectively). The DRV2605Ls are configured via I2C to take a PWM signal from their respective TIM1 channels and constantly drive an LRA haptic motor at a strength depending on the PWM signal. I'm unsure of precisely why, but the strength of the motor's vibrations are inversely proportional to the PWM signal (i.e. lower duty cycle means higher vibration strength).
The way the device works is as follows: instructions are sent from a PC to the STM32L5 via USB. These instructions contain which channel from TIM1 is having its duty cycle adjusted, and what the duty cycle is being adjusted to. The DRV2605Ls are constantly monitoring the duty cycle of their respective PWM inputs and adjusting their motors' vibration strength accordingly. The PC is constantly receiving data from another device to figure out how strong the LRA motor's vibration needs to be, and is constantly updating that information via USB.
below are the changes I made to the usb receive function.
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
uint8_t len = (uint8_t) *Len;
memset(buffer, '\0', 64);
memcpy(buffer, Buf, len);
if((char)buffer[0] == 'z')
{
TR_comCheck();
}
else
{
key = buffer[0];
command = buffer[1];
unit = buffer[2];
data1 = buffer[3];
data2 = buffer[4];
}
memset(Buf, '\0', len);
flag = 1;
return (USBD_OK);
/* USER CODE END 6 */
}instructions are sent as uint8_t arrays of a key, command, unit, data1 and data2, though data2 isn't relevant to the haptic sections of the device. key is a randomly generated and used to double check that the command was received properly. command is used to select an action (initialize DRV2605Ls, double-check device connection, etc). unit is used to determine which specific drv2605 to talk to. data1 sets the new duty cycle.
while (1)
{
if(flag == 1)
{
echoCommandBack();
switch(command)
{
case(0x01): //Move Servo motors
moveServoMotor();
break;
case(0x02):
writeHapticPWM();
break;
}
flag = 0;
}
}
void writeHapticPWM()
{
//int value = 0;
int levelOfStrength = 0;
switch(unit)
{
case(0x00):
//value = drvM;
if(LRA1->status != DRV2605_ACTIVE)
{
LRA1->status = DRV2605_ACTIVE;
LRA2->status = DRV2605_STANDBY;
DRV2605_PWM_Output(LRA2, 65);
}
levelOfStrength = selectHapticStrength(LRA1, data1);
if(levelOfStrength == 0)
{
printf("ERROR: LRA PWM = 0");
}
if(levelOfStrength != LRA1->currentPosition)
{
DRV2605_PWM_Output(LRA1, levelOfStrength);
}
break;
case(0x01):
if(LRA2->status != DRV2605_ACTIVE)
{
LRA2->status = DRV2605_ACTIVE;
LRA1->status = DRV2605_STANDBY;
DRV2605_PWM_Output(LRA1, 65);
}
levelOfStrength = selectHapticStrength(LRA2, data1);
if(levelOfStrength == 0)
{
printf("ERROR: LRA PWM = 0");
}
if(levelOfStrength != LRA2->currentPosition)
{
DRV2605_PWM_Output(LRA2, levelOfStrength);
}
break;
case(0x02):
LRA1->status = DRV2605_STANDBY;
LRA2->status = DRV2605_STANDBY;
DRV2605_PWM_Output(LRA1, 65);
DRV2605_PWM_Output(LRA2, 65);
break;
default:
return;
}
usb_ACK();
}
void DRV2605_PWM_Output(struct DRV *drv, int output)
{
TIM_HandleTypeDef *Timer = drv->timer;
uint32_t channel = drv->channel;
switch(channel)
{
case(TIM_CHANNEL_1):
Timer->Instance->CCR1 = output;
drv->currentPosition = output;
break;
case(TIM_CHANNEL_2):
Timer->Instance->CCR2 = output;
drv->currentPosition = output;
break;
}
}
So, a transmission of [randomkey], 0x02,0x00,0x2D translates to: adjust channel 1's duty cycle to 45.
This is what showed me the problem: Every once and a while, one of the PWM channels' duty cycle will rapidly drop to what looks like zero, then quickly bounce back to its original value. This only occurs when the PC is constantly sending new instructions for what the LRA vibration strength should be. Additionally, I have not seen this behaviour when I send a single instruction and nothing else after.
All of this so far seems to be pointing me towards the constant USB communications being the problem, though I'm not certain how. I'm still researching and testing, but I wanted to see if anyone had any potential insights.
