前次完成了对I2C接口的BH1750的数据访问,以此为基础利用采集到的光强数据作为阈值条件驱动42步进电机。步进电机与螺杆联动,驱动诸如窗帘这样的机构。42步进电机的驱动采用比较简单的时序。
4相5线步进电机 28YBJ-48#include "main.h"
#include "oled.h"
#define I2C_BH1750ADDR 0x46
#define I2C1_SCL_GPIO_PORT CW_GPIOA
#define I2C1_SCL_GPIO_PIN GPIO_PIN_6 //如果改动口线则GPIO初始化代码需要做同步修改
#define I2C1_SDA_GPIO_PORT CW_GPIOA
#define I2C1_SDA_GPIO_PIN GPIO_PIN_5 //如果改动口线则GPIO初始化代码需要做同步修改
//UARTx
#define DEBUG_UARTx CW_UART2
#define DEBUG_UART_CLK SYSCTRL_APB1_PERIPH_UART2
#define DEBUG_UART_APBClkENx SYSCTRL_APBPeriphClk_Enable1
#define DEBUG_UART_BaudRate 115200
#define DEBUG_UART_UclkFreq 16000000
//UARTx GPIO
#define DEBUG_UART_GPIO_CLK (SYSCTRL_AHB_PERIPH_GPIOB)
#define DEBUG_UART_TX_GPIO_PORT CW_GPIOB
#define DEBUG_UART_TX_GPIO_PIN GPIO_PIN_5
#define DEBUG_UART_RX_GPIO_PORT CW_GPIOB
#define DEBUG_UART_RX_GPIO_PIN GPIO_PIN_6
//GPIO AF
#define DEBUG_UART_AFTX PB05_AFx_UART2TXD()
#define DEBUG_UART_AFRX PB06_AFx_UART2RXD()
#define MAX_LIGHT1 150
#define MAX_LIGHT2 150
#define MAX_LIGHT 100
//// LED用GPIO
//#define LED_GPIO_PORT CW_GPIOB
//#define LED_GPIO_PINS GPIO_PIN_2
// 驱动步进马达的接口:PB00,PB01,PB02,PB03
#define MOTOR_GPIO_PORT1 CW_GPIOA
#define MOTOR_GPIO_PIN0 GPIO_PIN_3
#define MOTOR_GPIO_PIN1 GPIO_PIN_4
#define MOTOR_GPIO_PORT2 CW_GPIOB
#define MOTOR_GPIO_PIN2 GPIO_PIN_2
#define MOTOR_GPIO_PIN3 GPIO_PIN_3
// 按钮Key:PB4,PA6
#define KEY1_GPIO_PORT CW_GPIOB
#define KEY1_GPIO_PIN GPIO_PIN_4
void SYSCTRL_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void I2C_Configuration(void);
void Delay(uint16_t nCount);
void DelayMs(__IO uint16_t nCount);
void DelayUs(__IO uint16_t nCount);
void UART_Configuration(void);
void ATIM_IRQHandlerCallBack(void);
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
// ADC转换数据
uint32_t valueAdc;
// 中断标志
volatile uint8_t gFlagIrq;
// 马达控制
// 系统时钟设置为HSI时钟3分频,16MHz, PCLK、HCLK不分频,PCLK=HCLK=SysClk=16MHz
// ATIM边沿计数,上计数模式,80分频,ARR设置为19999,溢出周期200ms
// 每隔200模式,通过PB0~PB3输出步进电机的控制信号
#define MOTOR_MAX_STEP 12000
// 顺时针驱动信号
uint16_t motor_driver_s[4] = {1, 2, 4, 8};
// 逆时针驱动信号
uint16_t motor_driver_n[4] = {8, 4, 2, 1};
// 马达驱动信号间隔(200ms)
uint16_t motor_delay=200;
// 马达步进步数 : 发送一个完整周期,算一步
int32_t motor_step = 0;
// 马达正反转时,脉冲标志,范围0-3,按照顺时针/逆时针的脉冲顺序发送驱动信号
uint8_t motor_step_no=0;
typedef struct {
uint8_t direction ; // 转动方向 1-顺时针;2-逆时针;0-停转
uint8_t no; // 当前脉冲编号,0-3
int32_t stepcnt; // 步进计数
} MOTOR_InitTypeDef;
MOTOR_InitTypeDef motor;
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 配置ATIM
*
*/
void ATIM_Configuration(void) {
ATIM_InitTypeDef ATIM_InitStruct = {DISABLE,0};
ATIM_InitStruct.BufferState = ENABLE; //使能缓存寄存器
ATIM_InitStruct.CounterAlignedMode = ATIM_COUNT_ALIGN_MODE_EDGE; //边沿对齐
ATIM_InitStruct.CounterDirection = ATIM_COUNTING_UP; //向上计数;
ATIM_InitStruct.CounterOPMode = ATIM_OP_MODE_REPETITIVE; //连续运行模式
ATIM_InitStruct.Prescaler = 80-1; // 80分频
ATIM_InitStruct.ReloadValue = 599; // 重载周期499+1
ATIM_InitStruct.RepetitionCounter = 0; // 重复周期0
ATIM_Init(&ATIM_InitStruct);
ATIM_ITConfig(ATIM_IT_UIE, ENABLE); // 有重复计数器溢出产生进入中断
ATIM_Cmd(ENABLE);
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 配置UART
*
*/
void UART_Configuration(void) {
UART_InitTypeDef UART_InitStructure = {0};
UART_InitStructure.UART_BaudRate = DEBUG_UART_BaudRate;
UART_InitStructure.UART_Over = UART_Over_16;
UART_InitStructure.UART_Source = UART_Source_PCLK;
UART_InitStructure.UART_UclkFreq = DEBUG_UART_UclkFreq;
UART_InitStructure.UART_StartBit = UART_StartBit_FE;
UART_InitStructure.UART_StopBits = UART_StopBits_1;
UART_InitStructure.UART_Parity = UART_Parity_No ;
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
UART_Init(DEBUG_UARTx, &UART_InitStructure);
}
int32_t main(void) {
uint8_t bh1750cmd[] = {0x01};
uint8_t bh1750dat[8] = {0,0,0,0,0,0,0,0};
float val = 0;
//uint16_t v = motor.no%4;
// 马达控制初始化
motor.direction = 0;
motor.no = 0;
motor.stepcnt = 0;
//时钟初始化
SYSCTRL_Configuration();
//IO口初始化
GPIO_Configuration();
//配置UART
UART_Configuration();
printf("\r\nStart test ... \r\n");
// 初始化I2C外设
I2C_Configuration();
// 初始化OLED
OLED_Init();
// 显示光强标签:当前光强:
SYSTEM_Init();
// 显示参数
OLED_ShowNum(76, 0, 0, 6, 16);
// 设置ATIM参数
ATIM_Configuration();
/* 允许ATIM中断 */
NVIC_Configuration();
while (1) {
// // LED
// GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PINS);
// 采集光强数据:BH1750(I2C方式)
bh1750cmd[0] = 0x01; // PowerOn
I2C_MasterSendDataToSlave(CW_I2C1, I2C_BH1750ADDR, bh1750cmd, 1);
//bh1750cmd[0] = 0x10; // 高分辨率连续测量命令=0x10,测量时间120mS
bh1750cmd[0] = 0x23; // 一次低分辨率测量命令=0x23,测量时间16mS
I2C_MasterSendDataToSlave(CW_I2C1, I2C_BH1750ADDR, bh1750cmd, 1);
DelayMs(20); // 根据测量模式,调整为足够的等待时间,等待测试完成
I2C_MasterRecDataFromSlave(CW_I2C1, I2C_BH1750ADDR, bh1750dat, 2);
val = (bh1750dat[0]>>8 | bh1750dat[1])/1.2;
// 显示光强数据
printf("\r\nBH1750 Value = %f", val);
OLED_ShowNum(76, 0, (int)val, 6, 16);
// 根据光强值,确定步进电机正转还是反转
if (val >= MAX_LIGHT && motor.stepcnt < MOTOR_MAX_STEP) {
motor.direction = 1;
OLED_ShowChinese(48, 16, 11, 16);
} else if (val < MAX_LIGHT && motor.stepcnt > 0) {
motor.direction = 2;
OLED_ShowChinese(48, 16, 14, 16);
} else {
motor.direction = 0;
OLED_ShowChinese(48, 16, 9, 16);
}
// 显示步进计数
OLED_ShowNum(76, 16, (int)motor.stepcnt , 6, 16);
OLED_Refresh();
}
}
void ATIM_IRQHandlerCallBack(void) {
uint8_t v = 0;
if (ATIM_GetITStatus(ATIM_STATE_UIF)) {
// 清除中断标志位
ATIM_ClearITPendingBit(ATIM_STATE_UIF);
// 根据马达状态,发出控制信号
if (motor.direction == 0) {
// 禁止转动
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN0, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN1, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN2, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN3, GPIO_Pin_RESET);
motor.no=0;
} else {
// 根据正反转,获取不同的时序驱动数据
if (motor.direction == 1) {
v = motor_driver_s[motor.no];
} else if (motor.direction == 2) {
v = motor_driver_n[motor.no];
} else {
return;
}
// 输出驱动数据到GPIO口
if (v==1) {
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN1, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN2, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN3, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN0, GPIO_Pin_SET);
} else if (v==2) {
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN0, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN2, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN3, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN1, GPIO_Pin_SET);
} else if (v==4) {
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN0, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN1, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN3, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN2, GPIO_Pin_SET);
} else if (v==8) {
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN0, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOA, MOTOR_GPIO_PIN1, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN2, GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOB, MOTOR_GPIO_PIN3, GPIO_Pin_SET);
}
// 根据正反转,计算下一次时序脉冲数据,并调整步进计数
if (motor.direction == 1) {
// 正转
if (motor.stepcnt<MOTOR_MAX_STEP) {
motor.stepcnt++;
motor.no=(motor.no+1)%4;
} else {
// 超出步进上限,停止转动
motor.direction = 0;
}
} else if (motor.direction == 2) {
// 反转
if (motor.stepcnt>0) {
motor.stepcnt--;
motor.no=(motor.no+1)%4;
} else {
// 返回上电的初始位置时,停止转动
motor.direction = 0;
}
}
}
}
}
/**
* @brief Configures the different system clocks.
* @param None
* @retval None
*/
void SYSCTRL_Configuration(void) {
SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV3); // 三分频,16MHz主频(48/3)
//SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV6);
__SYSCTRL_ATIM_CLK_ENABLE(); // ATIM用时钟
__SYSCTRL_GPIOA_CLK_ENABLE(); // PA用PORT时钟
__SYSCTRL_GPIOB_CLK_ENABLE(); // PB用PORT时钟
__SYSCTRL_I2C_CLK_ENABLE(); // I2C用PORT时钟
//外设时钟使能
SYSCTRL_AHBPeriphClk_Enable(DEBUG_UART_GPIO_CLK, ENABLE);
DEBUG_UART_APBClkENx(DEBUG_UART_CLK, ENABLE);
}
/**
* @brief Configure the GPIO Pins.
* @param None
* @retval None
*/
// 初始化GPIO口
void GPIO_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure = {0};
// 按钮(PB4)
GPIO_InitStructure.IT = GPIO_IT_NONE;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
GPIO_InitStructure.Pins = KEY1_GPIO_PIN;
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
// 步进电机GPIO口:PA03、PA04、PB02、PB03
GPIO_InitStructure.IT = GPIO_IT_NONE;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pins = MOTOR_GPIO_PIN0 | MOTOR_GPIO_PIN1;
GPIO_Init(MOTOR_GPIO_PORT1, &GPIO_InitStructure);
GPIO_InitStructure.Pins = MOTOR_GPIO_PIN2 | MOTOR_GPIO_PIN3;
GPIO_Init(MOTOR_GPIO_PORT2, &GPIO_InitStructure);
// PA05、PA06复用为SDA、SCL
PA05_AFx_I2C1SDA();
PA06_AFx_I2C1SCL();
GPIO_InitStructure.Pins = I2C1_SCL_GPIO_PIN | I2C1_SDA_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_Init(I2C1_SCL_GPIO_PORT, &GPIO_InitStructure);
// 初始化串口:PB6 - RX, PB5 - TX
GPIO_WritePin(DEBUG_UART_TX_GPIO_PORT, DEBUG_UART_TX_GPIO_PIN,GPIO_Pin_SET); // 设置TXD的默认电平为高,空闲
GPIO_InitStructure.Pins = DEBUG_UART_TX_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(DEBUG_UART_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.Pins = DEBUG_UART_RX_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
GPIO_Init(DEBUG_UART_RX_GPIO_PORT, &GPIO_InitStructure);
//UART TX RX 复用
DEBUG_UART_AFTX;
DEBUG_UART_AFRX;
}
// 初始化I2C参数
void I2C_Configuration(void) {
I2C_InitTypeDef I2C_InitStruct = {0};
//I2C初始化
// 串行时钟发生器采用 PCLK 作为输入时钟,通过 1 个 8bit的计数器计数,输出所需波特率的 I2C 时钟信号。
I2C_InitStruct.PCLK_Freq = 16000000; // fSCL = fPCLK / 8 / ( BRR + 1 )
I2C_InitStruct.I2C_SCL_Freq = 1000000; // SCL频率=1MHz
I2C_InitStruct.I2C_SCL_Source = I2C_SCL_SRC_GPIO; //
I2C_InitStruct.I2C_SDA_Source = I2C_SDA_SRC_GPIO; //
I2C1_DeInit();
I2C_Master_Init(CW_I2C1,&I2C_InitStruct);//初始化模块
I2C_AcknowledgeConfig(CW_I2C1,ENABLE);//ACK打开
I2C_Cmd(CW_I2C1, ENABLE);//模块使能
}
/**
* @brief Configure the nested vectored interrupt controller.
* @param None
* @retval None
*/
void NVIC_Configuration(void) {
__disable_irq();
//NVIC_EnableIRQ(I2C1_IRQn);
NVIC_EnableIRQ(ATIM_IRQn); // 允许ATIM中断
__enable_irq();
}
/**
* @brief 循环延时
*
* @param nCount
*/
void Delay(__IO uint16_t nCount) {
/* Decrement nCount value */
while (nCount != 0) {
nCount--;
}
}
void DelayMs(__IO uint16_t nCount) {
/* Decrement nCount value */
while (nCount-- != 0) {
Delay(1300);
}
}
void DelayUs(__IO uint16_t nCount)
{
/* Decrement nCount value */
while (nCount-- != 0) {
Delay(130);
}
}
/**
* @brief Retargets the C library printf function to the UART.
*
*/
PUTCHAR_PROTOTYPE
{
UART_SendData_8bit(DEBUG_UARTx, (uint8_t)ch);
while (UART_GetFlagStatus(DEBUG_UARTx, UART_FLAG_TXE) == RESET);
return ch;
}
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
size_t nChars = 0;
if (buffer == 0)
{
/*
* This means that we should flush internal buffers. Since we
* don't we just return. (Remember, "handle" == -1 means that all
* handles should be flushed.)
*/
return 0;
}
for (/* Empty */; size != 0; --size)
{
UART_SendData_8bit(DEBUG_UARTx, *buffer++);
while (UART_GetFlagStatus(DEBUG_UARTx, UART_FLAG_TXE) == RESET);
++nChars;
}
return nChars;
}
/******************************************************************************
* EOF (not truncated)
******************************************************************************/
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* [url=home.php?mod=space&uid=266161]@return[/url] None
*/
void assert_failed(uint8_t *file, uint32_t line) {
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */