在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(更新了一些解释,用C语言实)

方法是:http://stackoverflow.com/questions/11694546/divide-a-number-by-3-without-using-operators这里的,我对其进行了一些解释和翻译,有需要的请点击上述链接查看原文。

问:在不使用*、/、+、-、%操作符的情况下,如何求一个数的1/3?(用C语言实现)

第一种方法:使用位操作符并实现“+”操作

 
  1. // 替换加法运算符 
  2. int add(int x, int y) { 
  3.     int a, b; 
  4.     do { 
  5.         a = x & y; /*判断是否需要进位*/
  6.         b = x ^ y; /*求和,忽略了进位*/
  7.         x = a << 1; /*向左移一位,为进位准备*/
  8.         y = b; /*求和结果,忽略了进位*/
  9.     } while (a); /*判断是否需要进位,如果为0,意味着没有对应位同时为1的情况,说明b就是结果*/
  10.     return b; 
  11.  
  12. int divideby3 (int num) { 
  13.     int sum = 0; 
  14.     while (num > 3) { 
  15.         sum = add(num >> 2, sum); 
  16.         num = add(num >> 2, num & 3); /*加上num&3是为了不丢失除法过程中的尾数,3即为11,即末尾两位*/
  17.     } 
  18.     if (num == 3) 
  19.         sum = add(sum, 1); 
  20.     return sum;  

原理:

有一位网友的解释很到位:

Here's a trick i found which got me a similar solution.

In decimal: 1 / 3 = 0.333333, the repeating numbers make this easy to calculate using a / 3 = a/10*3 + a/100*3 + a/1000*3 + (..). 

In binary it's almost the same: 1 / 3 = 0.0101010101 (base 2), which leads to a / 3 = a/4 + a/16 + a/64 + (..). 

Dividing by 4 is where the bit shift comes from.

 The last check on num==3 is needed because we've only got integers to work with. 

ps:In base 4 it gets even better: a / 3 = a * 0.111111 (base 4) = a * 4^-1 + a * 4^-2 + a * 4^-3 + (..) = a >> 2 + a >> 4 + a >> 6 + (..). The base 4 also explains why only 3 is rounded up at the end, while 1 and 2 can be rounded down

下面是公式啦:

n = 4 * a + b; 

n / 3 = a + (a + b) / 3; 

因此:

sum += a, n = a + b 并迭代; 

当 a == 0 (n < 4)时,sum += floor(n / 3); i.e. 1, if n == 3, else 0

第二种方法:

 
  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. int main() 
  4.     FILE * fp=fopen("temp.dat","w+b"); 
  5.     int number=1234; /*number不能过大*/
  6.     int divisor=3; 
  7.     char * buf = calloc(number,1); 
  8.     fwrite(buf,number,1,fp); 
  9.     rewind(fp); 
  10.     int result=fread(buf,divisor,number,fp); 
  11.     printf("%d / %d = %d", number, divisor, result); 
  12.     free(buf); 
  13.     fclose(fp); 
  14.     return 0; 

注意使用的编译器。

原理:

申请number大的空间,然后以3为单位读出空间的内容,读的次数就是number/3.当然这种方法number不能过大啊。

这是一个很巧妙的思想啊。不过有些函数我记不牢啦。

关键函数:

 calloc

  void *calloc(unsigned n,unsigned size);
  功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。

 fwrite

 fwrite(const void* buffer,size_t size,size_t count,FILE* stream);
  返回值:返回实际写入的数据块数目
  (1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
  (2)size:要写入内容的单字节数;
  (3)count:要进行写入size字节的数据项的个数;
  (4)stream:目标文件指针。
返回实际写入的数据项个数count

rewind
  函数名: rewind
  功 能: 将文件内部的位置指针重新指向一个流(数据流/文件)的开头
  注意:不是文件指针而是文件内部的位置指针,随着对文件的读写文件的位置指针(指向当前读写字节)向后移动。而文件指针是指向整个文件,如果不重新赋值文件指针不会改变。
  用 法: void rewind(FILE *stream);

fread
  功 能: 从一个流中读数据
  函数原型: size_t fread(void*buffer,size_tsize,size_tcount,FILE*stream);

参 数:
  1.用于接收数据的地址(指针)(buffer)
  2.单个元素的大小(size) :单位是字节而不是位,例如读取一个int型数据就是4个字节
  3.读取size个字节的次数(count)
  4.提供数据的文件指针(stream)
  返回值:读取的元素的个数

第三种方法:

 
  1. log(pow(exp(number),0.33333333333333333333)) /* :-) */ 

增强版:

 
  1. log(pow(exp(number),sin(atan2(1,sqrt(8)))))  
使用了公式,对,有些事情本来就很简单的。

第四种方法:

 
  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. int main(int argc, char *argv[]) 
  4.     int num = 1234567; 
  5.     int den = 3; 
  6.     div_t r = div(num,den); // div()是标准C语言函数
  7.     printf("%d\n", r.quot); 
  8.     return 0; 

第五种方法:使用内联汇编

 
  1. #include <stdio.h> 
  2. int main() { 
  3.   int dividend = -42, divisor = 3, quotient, remainder; 
  4.  
  5.   __asm__ ( "movl   %2, %%edx;" 
  6.             "sarl  $31, %%edx;" 
  7.             "movl   %2, %%eax;" 
  8.             "movl   %3, %%ebx;" 
  9.             "idivl      %%ebx;" 
  10.           : "=a" (quotient), "=d" (remainder) 
  11.           : "g"  (dividend), "g"  (divisor) 
  12.           : "ebx" ); 
  13.  
  14.   printf("%i / %i = %i, remainder: %i\n", dividend, divisor, quotient, remainder); 

第六种方法:

 
  1. // 注意: itoa 不是个标准函数,但它可以实现
  2. // don't seem to handle negative when base != 10 
  3. int div3(int i) { 
  4.   char str[42]; 
  5.   sprintf(str, "%d", INT_MIN); // put minus sign at str[0] 
  6.   if (i>0) str[0] = ' ';       // remove sign if positive 
  7.   itoa(abs(i), &str[1], 3);    // put ternary absolute value starting at str[1] 
  8.   str[strlen(&str[1])] = '\0'// drop last digit 
  9.   return strtol(str, NULL, 3); // read back result 

第七种方法:

 
  1. unsigned div_by(unsigned const x, unsigned const by) { 
  2.   unsigned floor = 0; 
  3.   for (unsigned cmp = 0, r = 0; cmp <= x;) { 
  4.     for (unsigned i = 0; i < by; i++) 
  5.       cmp++; // 这是++操作符,不是+ 
  6.     floor = r; 
  7.     r++; // 这也不是 
  8.   } 
  9.   return floor; 

替换掉上面算法的++运算符:

 
  1. unsigned inc(unsigned x) { 
  2.   for (unsigned mask = 1; mask; mask <<= 1) { 
  3.     if (mask & x) 
  4.       x &= ~mask; 
  5.     else 
  6.       return x & mask; 
  7.   } 
  8.   return 0; // 溢出(注意:这里的x和mask都是0)

这个版本更快一些:

 
  1. unsigned add(char const zero[], unsigned const x, unsigned const y) { 
  2.   // 这是因为:如果foo是char*类型, &foo[bar] == foo+bar
  3.   return (int)(uintptr_t)(&((&zero[x])[y])); 
  4.  
  5. unsigned div_by(unsigned const x, unsigned const by) { 
  6.   unsigned floor = 0; 
  7.   for (unsigned cmp = 0, r = 0; cmp <= x;) { 
  8.     cmp = add(0,cmp,by); 
  9.     floor = r; 
  10.     r = add(0,r,1); 
  11.   } 
  12.   return floor; 

第八种方法:实现乘法

 
  1. int mul(int const x, int const y) { 
  2.   return sizeof(struct { 
  3.     char const ignore[y]; 
  4.   }[x]); 

第九种方法:极限

 
  1. public static int div_by_3(long a) { 
  2.     a <<= 30; 
  3.     for(int i = 2; i <= 32 ; i <<= 1) { 
  4.         a = add(a, a >> i); 
  5.     } 
  6.     return (int) (a >> 32); 
  7.  
  8. public static long add(long a, long b) { 
  9.     long carry = (a & b) << 1; 
  10.     long sum = (a ^ b); 
  11.     return carry == 0 ? sum : add(carry, sum); 

原理:

因为, 1/3 = 1/4 + 1/16 + 1/64 + ...

所以,

a/3 = a * 1/3  

a/3 = a * (1/4 + 1/16 + 1/64 + ...)

a/3 = a/4 + a/16 + 1/64 + ...

a/3 = a >> 2 + a >> 4 + a >> 6 + ...

第十种方法:

 
  1. public static int DivideBy3(int a) { 
  2.     bool negative = a < 0
  3.     if (negative) a = Negate(a); 
  4.     int result; 
  5.     int sub = 3 << 29
  6.     int threes = 1 << 29
  7.     result = 0
  8.     while (threes > 0) { 
  9.         if (a >= sub) { 
  10.             a = Add(a, Negate(sub)); 
  11.             result = Add(result, threes); 
  12.         } 
  13.         sub >>= 1; 
  14.         threes >>= 1; 
  15.     } 
  16.     if (negative) result = Negate(result); 
  17.     return result; 
  18. public static int Negate(int a) { 
  19.     return Add(~a, 1); 
  20. public static int Add(int a, int b) { 
  21.     int x = 0
  22.     x = a ^ b; 
  23.     while ((a & b) != 0) { 
  24.         b = (a & b) << 1
  25.         a = x
  26.         x = a ^ b; 
  27.     } 
  28.     return x; 

注:本例是C#实现,因为作者更熟悉C#,但本题更倾向于算法,所以语言并不是太重要吧?(当然只是在不使用语言特性的前提下。)

如果你还想了解更多的方法可以点击这里

例题:http://ac.jobdu.com/problem.php?pid=1507

main.c:“/* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* 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 */ /* USER CODE END 0 */ 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_TIM2_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != 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_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT 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, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */” gpio.c:“/* Includes ------------------------------------------------------------------*/ #include "gpio.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /*----------------------------------------------------------------------------*/ /* Configure GPIO */ /*----------------------------------------------------------------------------*/ /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0|GPIO_PIN_1, GPIO_PIN_RESET); /*Configure GPIO pins : PC0 PC1 */ GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ ” tim.c:“/* Includes ------------------------------------------------------------------*/ #include "tim.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ TIM_HandleTypeDef htim2; /* TIM2 init function */ void MX_TIM2_Init(void) { /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ htim2.Instance = TIM2; htim2.Init.Prescaler = 71; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 1000; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 500; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM2_Init 2 */ /* USER CODE END TIM2_Init 2 */ HAL_TIM_MspPostInit(&htim2); } void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle) { if(tim_pwmHandle->Instance==TIM2) { /* USER CODE BEGIN TIM2_MspInit 0 */ /* USER CODE END TIM2_MspInit 0 */ /* TIM2 clock enable */ __HAL_RCC_TIM2_CLK_ENABLE(); /* TIM2 interrupt Init */ HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM2_IRQn); /* USER CODE BEGIN TIM2_MspInit 1 */ /* USER CODE END TIM2_MspInit 1 */ } } void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(timHandle->Instance==TIM2) { /* USER CODE BEGIN TIM2_MspPostInit 0 */ /* USER CODE END TIM2_MspPostInit 0 */ __HAL_RCC_GPIOA_CLK_ENABLE(); /**TIM2 GPIO Configuration PA0-WKUP ------> TIM2_CH1 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* USER CODE BEGIN TIM2_MspPostInit 1 */ /* USER CODE END TIM2_MspPostInit 1 */ } } void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* tim_pwmHandle) { if(tim_pwmHandle->Instance==TIM2) { /* USER CODE BEGIN TIM2_MspDeInit 0 */ /* USER CODE END TIM2_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_TIM2_CLK_DISABLE(); /* TIM2 interrupt Deinit */ HAL_NVIC_DisableIRQ(TIM2_IRQn); /* USER CODE BEGIN TIM2_MspDeInit 1 */ /* USER CODE END TIM2_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ ” usart.c:“/* Includes ------------------------------------------------------------------*/ #include "usart.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ UART_HandleTypeDef huart1; /* USART1 init function */ void MX_USART1_UART_Init(void) { /* USER CODE BEGIN USART1_Init 0 */ /* USER CODE END USART1_Init 0 */ /* USER CODE BEGIN USART1_Init 1 */ /* USER CODE END USART1_Init 1 */ huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN USART1_Init 2 */ /* USER CODE END USART1_Init 2 */ } void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(uartHandle->Instance==USART1) { /* USER CODE BEGIN USART1_MspInit 0 */ /* USER CODE END USART1_MspInit 0 */ /* USART1 clock enable */ __HAL_RCC_USART1_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /**USART1 GPIO Configuration PB6 ------> USART1_TX PB7 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); __HAL_AFIO_REMAP_USART1_ENABLE(); /* USART1 interrupt Init */ HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE END USART1_MspInit 1 */ } } void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) { if(uartHandle->Instance==USART1) { /* USER CODE BEGIN USART1_MspDeInit 0 */ /* USER CODE END USART1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_USART1_CLK_DISABLE(); /**USART1 GPIO Configuration PB6 ------> USART1_TX PB7 ------> USART1_RX */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6|GPIO_PIN_7); /* USART1 interrupt Deinit */ HAL_NVIC_DisableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspDeInit 1 */ /* USER CODE END USART1_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ ” stm32f1xx_it.c:“/* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f1xx_it.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN TD */ /* USER CODE END TD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /* External variables --------------------------------------------------------*/ extern TIM_HandleTypeDef htim2; extern UART_HandleTypeDef huart1; /* USER CODE BEGIN EV */ /* USER CODE END EV */ void NMI_Handler(void) { /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ /* USER CODE END NonMaskableInt_IRQn 0 */ /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ while (1) { } /* USER CODE END NonMaskableInt_IRQn 1 */ } void HardFault_Handler(void) { /* USER CODE BEGIN HardFault_IRQn 0 */ /* USER CODE END HardFault_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_HardFault_IRQn 0 */ /* USER CODE END W1_HardFault_IRQn 0 */ } } void MemManage_Handler(void) { /* USER CODE BEGIN MemoryManagement_IRQn 0 */ /* USER CODE END MemoryManagement_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */ /* USER CODE END W1_MemoryManagement_IRQn 0 */ } } void BusFault_Handler(void) { /* USER CODE BEGIN BusFault_IRQn 0 */ /* USER CODE END BusFault_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_BusFault_IRQn 0 */ /* USER CODE END W1_BusFault_IRQn 0 */ } } void UsageFault_Handler(void) { /* USER CODE BEGIN UsageFault_IRQn 0 */ /* USER CODE END UsageFault_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_UsageFault_IRQn 0 */ /* USER CODE END W1_UsageFault_IRQn 0 */ } } void SVC_Handler(void) { /* USER CODE BEGIN SVCall_IRQn 0 */ /* USER CODE END SVCall_IRQn 0 */ /* USER CODE BEGIN SVCall_IRQn 1 */ /* USER CODE END SVCall_IRQn 1 */ } void DebugMon_Handler(void) { /* USER CODE BEGIN DebugMonitor_IRQn 0 */ /* USER CODE END DebugMonitor_IRQn 0 */ /* USER CODE BEGIN DebugMonitor_IRQn 1 */ /* USER CODE END DebugMonitor_IRQn 1 */ } void PendSV_Handler(void) { /* USER CODE BEGIN PendSV_IRQn 0 */ /* USER CODE END PendSV_IRQn 0 */ /* USER CODE BEGIN PendSV_IRQn 1 */ /* USER CODE END PendSV_IRQn 1 */ } void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ } void RCC_IRQHandler(void) { /* USER CODE BEGIN RCC_IRQn 0 */ /* USER CODE END RCC_IRQn 0 */ /* USER CODE BEGIN RCC_IRQn 1 */ /* USER CODE END RCC_IRQn 1 */ } void TIM2_IRQHandler(void) { /* USER CODE BEGIN TIM2_IRQn 0 */ /* USER CODE END TIM2_IRQn 0 */ HAL_TIM_IRQHandler(&htim2); /* USER CODE BEGIN TIM2_IRQn 1 */ /* USER CODE END TIM2_IRQn 1 */ } void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */” main.h:“/* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __MAIN_H #define __MAIN_H #ifdef __cplusplus extern "C" { #endif /* Includes ------------------------------------------------------------------*/ #include "stm32f1xx_hal.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Exported types ------------------------------------------------------------*/ /* USER CODE BEGIN ET */ /* USER CODE END ET */ /* Exported constants --------------------------------------------------------*/ /* USER CODE BEGIN EC */ /* USER CODE END EC */ /* Exported macro ------------------------------------------------------------*/ /* USER CODE BEGIN EM */ /* USER CODE END EM */ /* Exported functions prototypes ---------------------------------------------*/ void Error_Handler(void); /* USER CODE BEGIN EFP */ /* USER CODE END EFP */ /* Private defines -----------------------------------------------------------*/ #define DIR_Pin GPIO_PIN_0 #define DIR_GPIO_Port GPIOC #define EN_Pin GPIO_PIN_1 #define EN_GPIO_Port GPIOC /* USER CODE BEGIN Private defines */ /* USER CODE END Private defines */ #ifdef __cplusplus } #endif #endif /* __MAIN_H */ ” gpio.h:“/* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __GPIO_H__ #define __GPIO_H__ #ifdef __cplusplus extern "C" { #endif /* Includes ------------------------------------------------------------------*/ #include "main.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* USER CODE BEGIN Private defines */ /* USER CODE END Private defines */ void MX_GPIO_Init(void); /* USER CODE BEGIN Prototypes */ /* USER CODE END Prototypes */ #ifdef __cplusplus } #endif #endif /*__ GPIO_H__ */” tim.h:“/* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __TIM_H__ #define __TIM_H__ #ifdef __cplusplus extern "C" { #endif /* Includes ------------------------------------------------------------------*/ #include "main.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ extern TIM_HandleTypeDef htim2; /* USER CODE BEGIN Private defines */ /* USER CODE END Private defines */ void MX_TIM2_Init(void); void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim); /* USER CODE BEGIN Prototypes */ /* USER CODE END Prototypes */ #ifdef __cplusplus } #endif #endif /* __TIM_H__ */ ” usart.h:“/* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __USART_H__ #define __USART_H__ #ifdef __cplusplus extern "C" { #endif /* Includes ------------------------------------------------------------------*/ #include "main.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ extern UART_HandleTypeDef huart1; /* USER CODE BEGIN Private defines */ /* USER CODE END Private defines */ void MX_USART1_UART_Init(void); /* USER CODE BEGIN Prototypes */ /* USER CODE END Prototypes */ #ifdef __cplusplus } #endif #endif /* __USART_H__ */” stm32f1xx_it.h:“/* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __STM32F1xx_IT_H #define __STM32F1xx_IT_H #ifdef __cplusplus extern "C" { #endif /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Exported types ------------------------------------------------------------*/ /* USER CODE BEGIN ET */ /* USER CODE END ET */ /* Exported constants --------------------------------------------------------*/ /* USER CODE BEGIN EC */ /* USER CODE END EC */ /* Exported macro ------------------------------------------------------------*/ /* USER CODE BEGIN EM */ /* USER CODE END EM */ /* Exported functions prototypes ---------------------------------------------*/ void NMI_Handler(void); void HardFault_Handler(void); void MemManage_Handler(void); void BusFault_Handler(void); void UsageFault_Handler(void); void SVC_Handler(void); void DebugMon_Handler(void); void PendSV_Handler(void); void SysTick_Handler(void); void RCC_IRQHandler(void); void TIM2_IRQHandler(void); void USART1_IRQHandler(void); /* USER CODE BEGIN EFP */ /* USER CODE END EFP */ #ifdef __cplusplus } #endif #endif /* __STM32F1xx_IT_H */ ”目前我已经有以上代码,那么完成一个42步进电机的控制,采用TB6600驱动器,串口发送一个多位据,比如第一部分控制正转反转与停止,第二位部分控制圈,第三部分控制速度,如果要现该功能在以上代码的基础上那么还需要完成哪些部分,请帮我完成,详细一些,还要确保所有量或函都要声明,一定确保正确性,记得反复检查下
最新发布
06-10
<think>我们有一个主函和一些初始化设置,但需要添加串口控制功能。需:通过串口发送多位据控制电机的正转、反转、停止、圈和速度。思路:1.首先,我们需要配置串口中断,以便接收据。根据引用[2],我们需要注意能将电机的控制放在主循环中简单的高低电平赋值,因为这样会影响测速等功能。因此,我们考虑在串口中断服务函中解析命令,然后设置相应的控制标志或参,然后在主循环中根据这些标志或参执行相应的电机控制动作。2.我们需要定义串口接收的协议。例如,假设我们使用以下格式(简单示例):“STOP”->停止“CWns”->顺时针旋转n圈,速度为s(脉冲频率)“CCWns”->逆时针旋转n圈,速度为s但考虑到使用,我们可以设计一个简单的文本协议,例如:命令类型(1字节)+参或者更简单,使用字符串,如:设置一个字符串组,以空格分隔参3.根据引用[1],我们已经有了一个绝对定位函Locate_Rle,它的参是:脉冲,频率,方向。其中,脉冲对应圈:如果我们设置细分,那么一圈所需的脉冲就是细分的倍。如果设置细分,那么一圈需要200个脉冲(因为42步进电机一般为200步/圈)。所以,圈n对应的脉冲=n*200*细分倍(如果驱动器设置了细分,则需要乘以这个倍,这里假设用户设置的是x细分,那么脉冲=n*200*x)。4.速度s:在函Locate_Rle中已经使用了频率参。所以我们可以直接将该速度作为频率参。5.停止功能:我们需要一个停止电机的函。可以设计一个,停止当前正在运行的步进电机。在定时器中停止脉冲输出。6.我们现有的主循环是循环正反转,现在需要改成根据串口命令执行。步骤:第一步:添加串口中断服务函,用于接收据并解析命令。-在串口初始化时,开启接收中断。-定义一个缓冲区,用于存储接收到的据,直到收到一个完整的命令(例如以换行符&#39;\n&#39;结束)。-在中断服务函中,每接收到一个字符就存入缓冲区,如果收到结束符,则设置一个标志,通知主循环处理。第二步:定义解析函,解析串口接收到的命令,并设置全局控制变量。-定义全局变量:例如,目标圈,速度,方向,以及一个命令执行标志。还可以定义一个停止标志。-解析接收到的字符串,根据同的命令设置这些全局变量。第三步:修改主循环,根据解析后的命令来执行相应的电机控制。-在主循环中检测是否有新的命令需要执行(通过全局标志判断)。-如果有,则根据命令的类型,调用Locate_Rle函(对于正反转)或者停止函(对于停止)。-注意:在发送脉冲的过程中,能打断(根据引用[2],我们使用定时器中断来发送脉冲,这样就会因为主循环处理其他事情而停止)。第四步:确保有停止电机的函。我们需要在命令解析到停止时,停止定时器,从而停止脉冲发送。具体代码修改:1.在串口初始化中开启接收中断(如果还没有的话)。在uart_init函中,添加接收中断使能。2.定义全局变量用于串口命令解析和执行:```c//定义串口接收缓冲区#defineRX_BUF_SIZE128charrx_buf[RX_BUF_SIZE];uint8_trx_index=0;volatileuint8_trx_flag=0;//接收到完整命令标志//电机控制参typedefenum{STOP,CW,CCW}DirType;//方向:停止、正转、反转volatileDirTypegDir=STOP;//当前方向命令volatileuint32_tgSteps=0;//需要运行的脉冲(根据圈计算得到)volatileuint32_tgFreq=500;//频率,默认为500Hzvolatileuint8_tgRunFlag=0;//需要执行电机动作为1,否则为0```3.串口中断服务函(以USART1为例,具体串口根据你的硬件连接):```cvoidUSART1_IRQHandler(void){if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET){charch=USART_ReceiveData(USART1);//读取据//如果收到换行符(或者自定义结束符,如&#39;\r&#39;),则置位标志if(ch==&#39;\n&#39;){if(rx_index< RX_BUF_SIZE-1){rx_buf[rx_index]=&#39;\0&#39;;//字符串结束符rx_flag=1;rx_index=0;}else{//缓冲区溢出,重新开始rx_index=0;}}else{if(rx_index< RX_BUF_SIZE-1){rx_buf[rx_index++]=ch;}else{//缓冲区满,重新开始(或丢弃)rx_index=0;}}}}```4.命令解析函(在主循环中调用,当rx_flag为1时):```cvoidparse_command(void){//假设命令格式:命令类型[参]//例如:"STOP","CW51000","CCW10500"charcmd[10];intn,s;if(sscanf(rx_buf,"%s%d%d",cmd,&n,&s)>=1){if(strcmp(cmd,"STOP")==0){gDir=STOP;gRunFlag=1;//表示需要执行停止}elseif(strcmp(cmd,"CW")==0){//当命令有两个参时,设置n和s;如果只有一个,则使用默认速度?if(sscanf(rx_buf,"%s%d%d",cmd,&n,&s)<3){//没有速度参使用默认速度s=gFreq;if(sscanf(rx_buf,"%s%d",cmd,&n)<2){//也没有圈,默认为1圈n=1;}}gDir=CW;//计算脉冲:这里假设驱动器细分设置为1(一圈200脉冲),如果是其他细分,需要乘以细分倍//例如,如果设置2细分,则一圈400脉冲//我们这里假设用户使用一个全局的细分倍变量,例如:gSubdivision,默认200(一圈200脉冲,即细分1倍)gSteps=n*gSubdivision;gFreq=s;//设置频率gRunFlag=1;}elseif(strcmp(cmd,"CCW")==0){//处理同CWif(sscanf(rx_buf,"%s%d%d",cmd,&n,&s)<3){s=gFreq;if(sscanf(rx_buf,"%s%d",cmd,&n)<2){n=1;}}gDir=CCW;gSteps=n*gSubdivision;gFreq=s;gRunFlag=1;}}rx_flag=0;//解析完成,清除标志}```5.停止函:```cvoidStop_Motor(void){//这里我们需要停止定时器输出,具体方法://例如,我们使用的是TIM1或TIM8,可以停止定时器:TIM_Cmd(TIM8,DISABLE);//假设使用的TIM8//或者可能还需要关闭通道输出等,根据际驱动代码来调整}```6.修改主循环:```cintmain(void){//初始化(包括串口、驱动器等)//...原有初始化//开启串口接收中断USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_EnableIRQ(USART1_IRQn);//设置默认细分倍(根据驱动器硬件设置)gSubdivision=200;//默认一圈200脉冲while(1){if(rx_flag)//如果收到完整命令{parse_command();}if(gRunFlag)//有命令需要执行{gRunFlag=0;//清除标志if(gDir==STOP){Stop_Motor();}else{//调用定位函,根据方向、脉冲、频率Locate_Rle(gSteps,gFreq,gDir);//注意:这里我们假设Locate_Rle会阻塞直到完成?//但根据引用[1],原来的Locate_Rle可能是非阻塞的(使用了中断和重复计器),所以会阻塞主循环。//然而,我们需要考虑在执行新的命令前,上一次的命令是否完成?//如果需要等上一次完成,那么这里可能需要等待,或者我们设计一个状态机。//建议:在Locate_Rle执行后,设置一个状态为忙,直到定时器完成中断置位标志,在忙状态下忽略新的命令,或者缓存命令。//由于我们这里需比较简单,且串口命令频繁,我们可以考虑使用阻塞方式等待完成(如果原函是阻塞的)。//但根据原始代码,主循环中的两次Locate_Rle之间只有10ms延时,所以应该是阻塞的(否则会阻塞在那里)。//因此,我们可能需要修改Locate_Rle函,使其在定时器完成中断中设置一个完成标志,然后我们在这里轮询直到完成,再执行下一个命令。//或者,我们设计一个状态机,在电机忙的时候才执行新的命令。//为了简化,我们在执行完Locate_Rle后,使用一个循环等待直到电机停止(在定时器中每次输出指定个的脉冲后,会停止,我们可以在中断里设置一个完成标志)。}}//如果原主循环中有其他操作,可以保留,但是原循环中的电机操作需要去掉//原来有:Locate_Rle(800,400,CW);delay_ms(10);Locate_Rle(800,400,CCW);delay_ms(10);//现在去掉,因为我们改为根据命令执行delay_ms(10);//适当延时,降低CPU使用率}}```7.在定时器中断中,处理脉冲发送完成后的标志:在定时器中断函(比如TIM8_UP_IRQHandler)中,当重复计器完成时,可以设置一个标志:```cexternvolatileuint8_tgMotorIdle;//全局标志,1表示空闲,0表示忙voidTIM8_UP_IRQHandler(void){if(TIM_GetITStatus(TIM8,TIM_IT_Update)!=RESET){TIM_ClearITPendingBit(TIM8,TIM_IT_Update);//在重复计模式下,当重复计器减到0并重新加载时,会产生更新中断//所以在这里可以判断是否完成了整个脉冲序列?际上,重复计功能可以设定脉冲个,当达到设定个后,就会停止(定时器单脉冲模式)。//因此,我们可以在此时设置空闲标志。gMotorIdle=1;//设置空闲//同时停止定时器TIM_Cmd(TIM8,DISABLE);}}```然后在主循环中,执行Locate_Rle函会重新设置重复计器的值,并启动定时器,同时将gMotorIdle置0。在Locate_Rle函中:```cvoidLocate_Rle(uint32_trle,uint32_tfreq,DirTypedir){//...原有设置方向、频率等操作//设置重复计器的值TIM_SetAutoreload(TIM8,1000000/freq-1);//设置ARR(假设定时器时钟为1MHz)TIM_SetRepetitionCounter(TIM8,rle-1);//重复计器设置为rle-1(因为重复计器从0开始计,所以输出rle个脉冲)//启动定时器gMotorIdle=0;//设置为忙TIM_Cmd(TIM8,ENABLE);//启动输出(根据你的驱动器需要,可能需要设置控制引脚)}```然后在主循环中,如果需要等待电机完成,可以:```cif(gRunFlag){gRunFlag=0;if(gDir==STOP){Stop_Motor();gMotorIdle=1;//停止后设置为空闲}else{if(gMotorIdle)//如果当前空闲,才能开始新的运动{Locate_Rle(gSteps,gFreq,gDir);}//否则,忽略本次命令(或者可以存起来下次执行)}}```8.这样,我们基本现了串口控制。但为了健壮性,还需要考虑当电机正在运动时收到新的命令怎么办?我们可以将其缓存,待完成后再执行。或者直接打断当前运动执行新的命令。根据需,我们可以选择简单的策略:当有新的命令到来时,首先停止当前的运动(如果正在运动),然后执行新的命令。修改:在parse_command函解析到任何命令(除了STOP)后,都需要先停止当前运动(如果正在运动),然后再执行新的运动。但是这样可能会频繁打断,需要根据际应用来定。另一种策略:设置一个命令队列,将串口命令放入队列,然后主循环依次执行。但考虑到资源有限,队列长度可以设置小一点。考虑到简单性,我们可以这样:在解析命令后,设置一个新的控制结构,然后主循环执行新的命令时,先停止当前运动,再开始新的运动。修改主循环部分:```cif(gRunFlag){gRunFlag=0;//首先,管当前是否在运动,都停止当前运动(因为要执行新命令)Stop_Motor();//这个函会停止定时器,停止脉冲输出//如果是STOP命令,则已经停止,再做其他动作if(gDir!=STOP){Locate_Rle(gSteps,gFreq,gDir);}}```注意:Stop_Motor()需要能够立即停止定时器,并且可能还需要清除一些状态。这样,新的命令就会打断之前的运动。9.最后,需要确保所有变量和函都正确声明。如gSubdivision需要在全局定义:```cvolatileuint32_tgSubdivision=200;//默认一圈200脉冲(细分1倍)```但是,细分倍应该由驱动器上的拨码开关决定,因此用户可能通过串口设置细分倍?目前需中没有提到,所以我们可以先固定设置。若需要设置,可以添加一条串口命令如“SET_SUB4”表示4细分(即一圈800脉冲),然后修改gSubdivision。总结:我们添加了串口接收和解析功能,修改了主循环以执行串口命令,并添加了停止功能。同时,我们修改了电机控制逻辑,可以随时打断当前运动。由于代码量较大,且需要结合原有代码,以上仅为主要思路和关键代码片段。注意:由于引用[3]中提到,TIM1和TIM8具有重复计器功能,所以我们使用重复计器来产生指定目的脉冲。问题:原有Locate_Rle函现我们并清楚,但根据函名和调用参,我们假设它使用了定时器的重复计器模式。为了确保功能可靠,需要仔细检查定时器的配置(TIM8_OPM_RCR_Init函)是否支持产生指定目的脉冲。同时,在停止函和启动新运行之间,需要正确设置定时器的状态。这个现方案可以满足基本需,但在际应用中可能还需要根据具体情况进行调整。以上。</think> ### 基于STM32控制42步进电机(TB6600驱动器)的串口控制现 #### 核心思路 1. **串口通信协议设计**:定义简单高效的据格式 2. **电机控制函优化**:扩展现有定位函 3. **中断协同机制**:串口中断+定时器中断配合 4. **状态机设计**:现异步控制 #### 现步骤 1. **串口协议设计(建议格式)** ``` CMD DIR:N:FREQ:PULSE (示例:CMD CW:N:1000:800) ``` - `DIR`:方向(CW/CCW/STOP) - `N`:圈(整- `FREQ`:脉冲频率(Hz) - `PULSE`:单圈脉冲(默认800对应200步×4细分) 2. **全局变量声明(添加到头文件)** ```c // 串口接收缓冲区 #define CMD_SIZE 32 volatile char uart_cmd[CMD_SIZE]; volatile uint8_t cmd_flag = 0; // 电机控制参结构体 typedef struct { uint32_t pulse_count; // 总脉冲 uint32_t frequency; // 脉冲频率(Hz) DirType direction; // 运动方向(CW/CCW) uint8_t is_running; // 运行状态标志 } Motor_Ctrl; volatile Motor_Ctrl motor = {0}; ``` 3. **串口中断接收函(添加到`uart.c`)** ```c void USARTx_IRQHandler(void) { static uint8_t idx = 0; if(USART_GetITStatus(USARTx, USART_IT_RXNE)){ char ch = USART_ReceiveData(USARTx); if(ch == &#39;\n&#39; || idx >= CMD_SIZE-1) { uart_cmd[idx] = &#39;\0&#39;; idx = 0; cmd_flag = 1; // 触发命令解析 } else { uart_cmd[idx++] = ch; } } } ``` 4. **命令解析与执行函** ```c void Parse_Execute_Command(void) { if(!cmd_flag) return; // 示例:CMD CW:N:1000:800 char dir[5]; uint32_t circle, freq, pulse_per_circle; if(sscanf(uart_cmd, "CMD %4s:%lu:%lu:%lu", dir, &circle, &freq, &pulse_per_circle) >= 3) { // 方向解析 DirType new_dir = STOP; if(strcmp(dir, "CW") == 0) new_dir = CW; if(strcmp(dir, "CCW") == 0) new_dir = CCW; // 停止命令处理 if(strcmp(dir, "STOP") == 0 || new_dir == STOP) { TIM_Cmd(TIM8, DISABLE); motor.is_running = 0; return; } // 参校验 freq = (freq < 500) ? 500 : freq; // 频率下限限制 pulse_per_circle = (pulse_per_circle == 0) ? 800 : pulse_per_circle; // 更新电机参 motor.direction = new_dir; motor.frequency = freq; motor.pulse_count = circle * pulse_per_circle; motor.is_running = 1; // 启动定时器(重要!能阻塞中断) TIM_SetAutoreload(TIM8, (SystemCoreClock/2)/freq - 1); // 计算ARR值 TIM_SetRepetitionCounter(TIM8, motor.pulse_count - 1); TIM_Cmd(TIM8, ENABLE); } cmd_flag = 0; // 清除标志 } ``` 5. **修改主函结构** ```c int main(void) { // 初始化(保持原样) NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); delay_init(168); uart_init(115200); USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); // 新增串口中断使能 Driver_Init(); TIM8_OPM_RCR_Init(999,168-1); while(1) { Parse_Execute_Command(); // 解析命令 // 添加LED状态指示(可选) if(motor.is_running) LED_ON; else LED_OFF; delay_ms(10); } } ``` #### 关键现细节 1. **定时器配置原则** - 使用TIM1/TIM8高级定时器(支持重复计器功能)[^3] - 脉冲计算:$ARR = \frac{F_{clk}}{F_{pwm}} - 1$($F_{clk}$=84MHz) - 例如500Hz脉冲:$ARR = \frac{84,000,000}{500} - 1 = 167,999$ 2. **硬件接线规范** - 脉冲信号引脚:必须使用支持PWM输出的定时器通道(如TIM8_CH1- 方向信号引脚:普通GPIO即可 - 串口连接:USART1/2(需在CubeMX中配置) 3. **安全机制** - 脉冲频率范围限制(500-100KHz) - 中断优先级设置(步进脉冲中断 > 串口中断) - 空闲状态自动关闭定时器 #### 测试命令示例 1. 正转5圈(1000Hz脉冲): ``` CMD CW:5:1000:800 ``` 2. 反转3圈(500Hz脉冲): ``` CMD CCW:3:500:800 ``` 3. 紧急停止: ``` CMD STOP ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值