2023-03-31 10:19 AM
I’m using the board, motordriver IIC, to control motor through I2C bus.
The chip on the board is PCA9685.
The board is originally a extension for arduino UNO, and I’m using STM32F446RE which also support arduino UNO expansion connector.
The official give the arduino code, here is github link
github.com/YFROBOT-TM/Yfrobot-Motor-Driver-Library
and I translate it into stm32 code.
Now I want to drive a motor connent to M1 on the board with PWM 100%.
The following is the my stm32 code in main function:
And this is the ioc setting(including clock configuration):
[I2C]
standard mode with 100kHz
PB8 for SCL and PB9 for SDA, both pin set as open drain and pull-up.
[RCC]
set HSE to Crystal/Ceramic Resonator
[SYS]
set Debug to Serial Wire
I use the logic analyzer, and the SDA line shows what I trasmit is correct
***Worth mentionong, I found that at the SCL line, some duty cycle are 50%, and some are40%, don’t know if it make the i2c not working…
100kHz = 10us, refer to the form, the duty cycle should be between 40%~53% (?)
2023-03-31 11:11 AM
Why do you use "M1IN1 << 1" as the register address in buffer 1, and likewise shifting by 1 in buffer 2 and 3? Presuming LED0_ON_L is defined as 6, then you are sending 12 (0x0c) as the register address (as shown in the logic analyzer data). That is LED1_OFF_L, probably not what you intend.
2023-04-01 12:29 AM
@Bob S Thanks for answering the question.
I shift the register address left one bit is beacause I thought it will show the right address in the logic analyzer.
I'm curious about how to read the logic analyzer data.
I read SDA 0 or 1 when SCL in evert HIGH pulse. so the result as below, 1~7bit is the data, the 8th bit (red one) for write/read, and the 9th bit (green one) for ACK/NACK.
Because during the 9th high pulse of SCL, SDA is LOW, so it's ACK.
And, 0x06 = 6 in decimal = 110 in binary
Is that right?
2023-04-01 12:55 AM
>I'm curious about how to read the logic analyzer data.
:)
only first byte, = address , has 8. bit (0 write, 1 read ) ; all following bytes are 8 bits data and stay on r/w mode from first byte; so to change "write" to "read" mode, need send address-byte again, called: repeated start; always needed, when read from any chip or eeprom, because first access is write to select address or register in target chip, but to read then this value, need switching from write to read mode.
and 9. bit always ack/nack.
in HAL this is : HAL_I2C_Mem_Read() , or HAL_I2C_Mem_Write() for write to ...
2023-04-01 02:02 AM
I try the example code with arduino, and it works.
@Bob S you're right. I don't need to shift one bit left for control register.
another mistake for me is that the write value for 4096, sould be 4096 fiirst, and then 4096>>8u.
I fix the two mistakes, and comapre the logic analyzer result with STM32 and arduino. Here is the different.
use HAL_I2C_Master_Transmit() in STM32 v.s. arduino
SDA line got some other HIGH signal between every byte
use HAL_I2C_Mem_Write() (recommend from @AScha.3 ) in STM32 v.s. arduino
SDA line data looks ok
but the problem is SCL line, there is no gap between every 9bit
2023-04-06 08:54 PM
There doesn't NEED to be a gap between each byte. A gap appears only then the master is not quite ready to send the next byte after the ACK cycle. As long as the data line is valid on the rising clock edge, all is fine.
2023-04-11 04:47 AM
but it still doesn't work... :( Is there anything I missed checking?
2023-04-11 10:14 AM
What doesn't work? What happens that shouldn't, or what doesn't happen that should?
Post your latest code (using the "code" tags in the toolbar below, not pasted as a screen shot please).
Your original code did not look like it wrote to the MODE1 and/or MODE2 registers (hint).
2023-04-15 02:00 PM - edited 2023-11-20 09:04 AM
/*main.c*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdbool.h"
#include "math.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define PCA9685_I2C_ADDRESS (0x40<<1)
#define PCA9685_MODE1_REG 0x00
#define PCA9685_PRESCALE_REG 0xFE
#define PCA9685_LED0_ON_L_REG 0x06
#define M1IN1 (PCA9685_LED0_ON_L_REG + 4 * 0)
#define M1IN2 (PCA9685_LED0_ON_L_REG + 4 * 1)
#define M1PWM (PCA9685_LED0_ON_L_REG + 4 * 2)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
HAL_StatusTypeDef state1, state2, state3, device_exist;
uint8_t TX_Buffer_1[4] = {0x00, 0x10, 0x00, 0x00};
uint8_t TX_Buffer_2[4] = {0x00, 0x00, 0x00, 0x10};
uint8_t TX_Buffer_3[4] = {0x00, 0x00, 0x00, 0x10};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void PCA9685_Init(void)
{
// write restart bit
uint8_t buffer_[1];
buffer_[0] = 0x80;
HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0x00, I2C_MEMADD_SIZE_8BIT, buffer_, 1, 1000);
HAL_Delay(10);
// write mode1 sleep bit
buffer_[0] = 0x10;
HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0x00, I2C_MEMADD_SIZE_8BIT, buffer_, 1, 1000);
double prescale = (25000000 / (1000 * 4096 * 0.9)) - 1;
// write prescale value
buffer_[0] = (uint8_t)prescale;
HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0xFE, I2C_MEMADD_SIZE_8BIT, buffer_, 1, 1000);
//write mode1 wake
buffer_[0] = 0x00;
HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0x00, I2C_MEMADD_SIZE_8BIT, buffer_, 1, 1000);
HAL_Delay(6);
//write mode1 auto-increment
buffer_[0] = 0xA0;
HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0x00, I2C_MEMADD_SIZE_8BIT, buffer_, 1, 1000);
//write M1IN1, M1IN2 0x00 0x00 0x00 0x10
uint8_t buffer[4] = {0x00, 0x00, 0x00, 0x10};
HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0x06, I2C_MEMADD_SIZE_8BIT, buffer, 4, 1000);
HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0x0A, I2C_MEMADD_SIZE_8BIT, buffer, 4, 1000);
HAL_Delay(1000);
// buffer_[0] = 0b00001001;
// HAL_I2C_Mem_Write(&hi2c1, PCA9685_I2C_ADDRESS, 0x01, I2C_MEMADD_SIZE_8BIT, buffer_, 1, 1000);
// HAL_Delay(6);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
HAL_I2C_MspInit(&hi2c1);
HAL_Delay(500);
PCA9685_Init();
device_exist = HAL_I2C_IsDeviceReady(&hi2c1, PCA9685_I2C_ADDRESS, 10, HAL_MAX_DELAY);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
state1 = HAL_I2C_Master_Transmit(&hi2c1, PCA9685_I2C_ADDRESS, TX_Buffer_1, 5, 1000);
state2 = HAL_I2C_Master_Transmit(&hi2c1, PCA9685_I2C_ADDRESS, TX_Buffer_2, 5, 1000);
state3 = HAL_I2C_Master_Transmit(&hi2c1, PCA9685_I2C_ADDRESS, TX_Buffer_3, 5, 1000);
// HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 180;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLRCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV4;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
}
void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
Here is my latest code. I want to drive a motor rotate in one direction continuously, and the motor connect to M1 on the board.
In the live expression of STM32CubeIDE, I can see the I2C1 is set, and the pBuffPtr in keep showing the value I transmit, and the transmit state that the function is all HAL_OK, also check the device exist or not by "IsDeviceReady" function, and it exists. but the motor doesn't do anything...
Here is the part of live expression
2023-04-16 05:54 AM
I also use the function read the register I write, (0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11), ans the value I read is the value I write in. Also, check the MotorDriver board and the motor, not the hardware issue.