手机APP遥控电动滑板车
拆解平衡车
主芯片 GD32F130
万用表探查电路,分析管脚功能;
使用KEIL编写单片机控制代码:
void gpio_config(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
/* configure PA6(TIMER2 CH0) as alternate function */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10);
gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10);
rcu_periph_clock_enable(RCU_GPIOB);
/* configure output GPIO port */
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
gpio_bit_reset(GPIOB, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
/* enable the Key GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* configure button pin as input */
gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_1);
/* enable the Key GPIO clock */
rcu_periph_clock_enable(RCU_GPIOB);
/* configure button pin as input */
gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_11);
/* enable the Key GPIO clock */
rcu_periph_clock_enable(RCU_GPIOC);
/* configure button pin as input */
gpio_mode_set(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_14);
}
/*!
\brief configure the TIMER peripheral
\param[in] none
\param[out] none
\retval none
*/
void timer_config(void)
{
/* -----------------------------------------------------------------------
TIMER2CLK is 50KHz
TIMER2 channel0 duty cycle = (25000/ 50000)* 100 = 50%
----------------------------------------------------------------------- */
timer_oc_parameter_struct timer_ocintpara;
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_TIMER0);
timer_deinit(TIMER0);
/*timer_initpara.prescaler = 1339;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 49999;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER0,&timer_initpara);*/
/* TIMER0 configuration */
timer_initpara.prescaler = 719;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 499;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER0,&timer_initpara);
/* CH0 configuration in PWM mode */
timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
timer_channel_output_config(TIMER0,TIMER_CH_0,&timer_ocintpara);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,PWM_val);
timer_channel_output_mode_config(TIMER0,TIMER_CH_0,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);
//timer_primary_output_config(TIMER0,ENABLE);
/*timer_channel_output_config(TIMER0, TIMER_CH_0,&timer_ocintpara);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,24999);
timer_channel_output_mode_config(TIMER0,TIMER_CH_0,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);*/
timer_channel_output_config(TIMER0,TIMER_CH_1,&timer_ocintpara);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,PWM_val);
timer_channel_output_mode_config(TIMER0,TIMER_CH_1,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);
timer_channel_output_config(TIMER0,TIMER_CH_2,&timer_ocintpara);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_2,PWM_val);
timer_channel_output_mode_config(TIMER0,TIMER_CH_2,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER0,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);
/* auto-reload preload enable */
timer_auto_reload_shadow_enable(TIMER0);
/* auto-reload preload enable */
timer_enable(TIMER0);
timer_primary_output_config(TIMER0, ENABLE);
}
/*******************************************************************************
* º¯ÊýÃû³Æ: PWM();
* ¹¦ÄÜÃèÊö: ¹Ø±Õ´ò¿ªPWM
* ÊäÈë²ÎÊý: ÎÞ
* ·µ»Ø²ÎÊý: ÎÞ
********************************************************************************/
void CH_ON(uint32_t pin)
{
rcu_periph_clock_enable(RCU_GPIOA);
/* configure PA6(TIMER2 CH0) as alternate function */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE,pin);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,pin);
gpio_af_set(GPIOA, GPIO_AF_2, pin);
}
void CH_OFF(uint32_t pin)
{
gpio_bit_reset(GPIOA, pin);
/* enable the GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* configure GPIO port */
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, pin);
/* reset GPIO pin */
gpio_bit_reset(GPIOA, pin);
}
/*******************************************************************************
* º¯ÊýÃû³Æ: Table_EX ͨµÀÅú´¦Àí
8--13
9--14
10--15
* ¹¦ÄÜÃèÊö:
* ÊäÈë²ÎÊý: ÎÞ
* ·µ»Ø²ÎÊý: ÎÞ
********************************************************************************/
void Table_EX(unsigned int table[6])
{
if(table[0]==2){
CH_ON(GPIO_PIN_8);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,PWM_val);//PWM
gpio_bit_set(GPIOB, GPIO_PIN_13);//¸ßPWMÊä³ö
}
if(table[0]==1){
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,0);
CH_OFF(GPIO_PIN_8);
gpio_bit_reset(GPIOB, GPIO_PIN_13);//µÍµ¼Í¨
}
if(table[0]==0){
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_0,0);
CH_OFF(GPIO_PIN_8);//µÍ
gpio_bit_set(GPIOB, GPIO_PIN_13);//·´Ïà½ØÖÁ
}
if(table[1]==2){
CH_ON(GPIO_PIN_9);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,PWM_val);//PWM
gpio_bit_set(GPIOB, GPIO_PIN_14);//¸ßPWMÊä³ö
}
if(table[1]==1){
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,0);
CH_OFF(GPIO_PIN_9);
gpio_bit_reset(GPIOB, GPIO_PIN_14);//µÍµ¼Í¨
}
if(table[1]==0){
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_1,0);
CH_OFF(GPIO_PIN_9);//µÍ
gpio_bit_set(GPIOB, GPIO_PIN_14);//¸ß
}
if(table[2]==2){
CH_ON(GPIO_PIN_10);
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_2,PWM_val);//PWM
gpio_bit_set(GPIOB, GPIO_PIN_15);//¸ßPWMÊä³ö
}
if(table[2]==1){
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_2,0);
CH_OFF(GPIO_PIN_10);
gpio_bit_reset(GPIOB, GPIO_PIN_15);//µÍµ¼Í¨
}
if(table[2]==0){
timer_channel_output_pulse_value_config(TIMER0,TIMER_CH_2,0);
CH_OFF(GPIO_PIN_10);//µÍ
gpio_bit_set(GPIOB, GPIO_PIN_15);//¸ß
}
}
/*******************************************************************************
* º¯ÊýÃû³Æ: BLDC_Hall_Convet();
* ¹¦ÄÜÃèÊö: ¸ù¾Ý»ô¶û״̬½øÐл»Ïò--T1 T3 T5/T4 T6 T2ÅÅÁÐ
* ÊäÈë²ÎÊý: ÎÞ
* ·µ»Ø²ÎÊý: ÎÞ
********************************************************************************/
void BLDC_Hall_Convet(void)
{
PC14_Value = gpio_input_bit_get(GPIOC, GPIO_PIN_14)*1;
PA1_Value = gpio_input_bit_get(GPIOA, GPIO_PIN_1)*2;
PB11_Value = gpio_input_bit_get(GPIOB, GPIO_PIN_11)*4;
HALL_Value = PC14_Value+PA1_Value+PB11_Value;
if(n>10000){
HALL_Value = HALL_Value+1;
}
if(x!=HALL_Value){
/*table[0]=0;
table[1]=0;
table[2]=0;
table[3]=0;
table[4]=0;
table[5]=0;
Table_EX(table);
delay_1ms(1);*/
if(HALL_flag ==1) //Õýת
{
switch(HALL_Value) //¸ù¾Ýת×ÓλÖ㬾ö¶¨CCERÊä³öÏàλºÍת×Ó×ÖÆ«ÒÆÁ¿
{
case 5:
table[0]=0;
table[1]=1;
table[2]=2;
Table_EX(table);
break;
case 1:
table[0]=2;
table[1]=1;
table[2]=0;
Table_EX(table);
break;
case 3:
table[0]=2;
table[1]=0;
table[2]=1;
Table_EX(table);
break;
case 2:
table[0]=0;
table[1]=2;
table[2]=1;
Table_EX(table);
break;
case 6:
table[0]=1;
table[1]=2;
table[2]=0;
Table_EX(table);
break;
case 4:
table[0]=1;
table[1]=0;
table[2]=2;
Table_EX(table);
break;
default:break;
}
}
if(HALL_flag ==2) //·´×ª
{
switch(HALL_Value) //¸ù¾Ýת×ÓλÖ㬾ö¶¨CCERÊä³öÏàλºÍת×Ó×ÖÆ«ÒÆÁ¿
{
case 5:
table[0]=1;
table[1]=0;
table[2]=2;
Table_EX(table);
break;
case 1:
table[0]=0;
table[1]=1;
table[2]=2;
Table_EX(table);
break;
case 3:
table[0]=2;
table[1]=1;
table[2]=0;
Table_EX(table);
break;
case 2:
table[0]=2;
table[1]=0;
table[2]=1;
Table_EX(table);
break;
case 6:
table[0]=0;
table[1]=2;
table[2]=1;
Table_EX(table);
break;
case 4:
table[0]=1;
table[1]=2;
table[2]=0;
Table_EX(table);
break;
default:break;
}
}
x=HALL_Value;
IWDG_FEED();
n=0;
}else{
n++;
}
}
//USART1³õʼ»¯
void uart1_init(uint32_t bound)
{
/* ?? GPIOA ??? */
rcu_periph_clock_enable(RCU_GPIOA);
/* ?? USART1?? */
rcu_periph_clock_enable(RCU_USART1);
/* ??PA2? USARTx_Tx */
gpio_af_set(GPIOA, GPIO_AF_1,GPIO_PIN_2);
/* ??PA2????????????? */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
/* ??PA3? USARTx_Rx */
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_3);
/* ??PA3?????????????*/
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_3);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
/* USART configure */
usart_deinit(USART1);
usart_baudrate_set(USART1, bound);
usart_word_length_set(USART1, USART_WL_8BIT);
usart_stop_bit_set(USART1, USART_STB_1BIT);
usart_parity_config(USART1, USART_PM_NONE);
usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE);
usart_receive_config(USART1, USART_RECEIVE_ENABLE);
usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
usart_enable(USART1);
// nvic_irq_enable(USART1_IRQn, 0, 0);
usart_interrupt_enable(USART1 , USART_INT_RBNE);
}
//USART1½ÓÊÜÖжÏ
void USART1_IRQHandler(void)
{
k=20;
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE) != RESET) //½ÓÊÕÖжÏ
{
Usart_rec = usart_data_receive(USART1);
//usart_data_transmit(USART1, Usart_rec);
if(Usart_rec=='A'||flag==1){
flag=1;
if(i < k+1)
{
aRxBuffer[i] = Usart_rec;
i++;
}
if(Usart_rec=='E'){
//printf("%s\r\n",aRxBuffer);
if(aRxBuffer[0]=='A'&&aRxBuffer[10]=='E'){
if (aRxBuffer[5]=='R'){
HALL_Value=0;
Run_flag =1;
HALL_flag=2;
PWM_val=(aRxBuffer[6]-'0')*100+(aRxBuffer[7]-'0')*10+(aRxBuffer[8]-'0')*1;
}
if (aRxBuffer[5]=='F'){
HALL_Value=0;
Run_flag =1;
HALL_flag=1;
PWM_val=(aRxBuffer[6]-'0')*100+(aRxBuffer[7]-'0')*10+(aRxBuffer[8]-'0')*1;
}
if(PWM_val>0&&PWM_val<499){
}else{
PWM_val=0;
}
if (aRxBuffer[9]=='S'){
Run_flag =0;
}
i=0;
for(j=0;j<k+1;j++){
aRxBuffer[j]=' ';
}
flag=0;
}
}
}
}
// if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_TBE)) //·¢ËÍÖжÏ
// {
// }
}
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
systick_config();
uart1_init(115200);
gpio_config();
timer_config();
//printf("\r\n start~\r\n");
x=8;
HALL_Value=0;
IWDG_INIT();
while(1){
if(Run_flag ==1){
BLDC_Hall_Convet();//»»Ïò
}else{
table[0]=0;
table[1]=0;
table[2]=0;
Table_EX(table);
IWDG_FEED();
}
USART1_IRQHandler();
}
}
使用ST-link下载程序到单片机,滑板车的程序部分就搞定了。
需要自己制作一个电机安装支架,喜欢DIY的可以自己做,也可以某宝买个;
电路板用3D打印了一个壳子,方便安装。
接下来制作手机的APP,效果图如下,使用蓝牙透传连接到滑板车。
APP主要控制代码如下:
@SuppressLint("ClickableViewAccessibility")
private void touch_pad(){
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.touch_pad);
final DrawView drawView = new DrawView(this);
drawView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
moveX = event.getX();
moveY = event.getY();
Log.d("move","moveX= "+moveX);
Log.d("move","moveY= "+moveY);
//Log.d("time","sys= "+System.currentTimeMillis()+"temp= "+temp);
if(moveX<0){
moveX=0;
}
if(moveX>padlength){
moveX=padlength;
}
if(moveY<0){
moveY=0;
}
if(moveY>padlength){
moveY=padlength;
}
drawView.currentX = moveX;
drawView.currentY = moveY;
drawView.invalidate();//通过draw组件重绘
if(System.currentTimeMillis()-temp>200) {
moveY = padlength / 2 - moveY;
moveX = moveX - padlength / 2;
WL = (int) moveY + (int) moveX;
WR = (int) moveY - (int) moveX;
if (WL > 499) {//限幅
WL = 498;
}else if(WL<-499){
WL=-498;
}
if (WR > 499) {
WR = 498;
}else if(WR<-499){
WR=-498;
}
if(WL>0){
sendstr = "AR" +numtostr(WL);
}else{
sendstr = "AF" +numtostr(-WL);
}
if(WR>0){
sendstr = sendstr+"R" +numtostr(WR);
}else{
sendstr = sendstr+"F" +numtostr(-WR);
}
sendstr = sendstr+"RE";
mBLESPPUtils.send(sendstr.getBytes());
temp=System.currentTimeMillis();
}
break;
case MotionEvent.ACTION_UP:
drawView.currentX = padlength/2;
drawView.currentY = padlength/2;
drawView.invalidate();//通过draw组件重绘
sendstr="AS000S000SE";
mBLESPPUtils.send(sendstr.getBytes());
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
});
linearLayout.addView(drawView);
}
APP及单片机源代码链接
单片机控制代码
安卓APP控制源代码