本篇文章說明以PWM控制Servo Motor。以Raspberry Pi Pico整合ADC, nRF24L01+遠端遙控ESP32-CAM鏡頭拍攝的角度。
一、Servo motor and PWM:
系統clock為125M Hz
125M Hz/125/20000 = 50Hz。
以下列指令設定PWM duty Cycle:
pwm_set_chan_level(slice, pwm_ch, top_count);
top_count:angle*1000/180+15000, 0度時1.5ms duty cycle。-90~90為1ms~2ms。
實際使用時可能因為所使用元件誤差,需要調整,在本篇文章的實驗時-90~90為0.5ms~2.5ms。
二、ADC:
Raspberry Pi Pico 共有4個ADC ports,如上圖所示。解析度為12 bit因此取樣值為0~4095。下列指令
adc_init();
adc_gpio_init(ADC_PIN); // init and assign ADC pin
adc_select_input(ADC_NUM); // gpio 26: adc_num 0, gpio 27:1, gpio 28 : 2
adv_value = adc_read();
三、nRF24L01+:
驅動程式請參閱[Raspberry Pi Pico] nRF24L01+ Ep. 1 : Pico-SDK C-code driver using IRQ,相關網路拓撲請參閱[Raspberry Pi Pico] nRF24L01+ Ep. 2 : Various types of network topologies
- 發送端設定:
nRF24_spi_default_init(20, 21, nRF24_irq_callback);
nRF24_config_mode(TRANSMITTER);
nRF24_enable_feature(FEATURE_EN_DPL, true);
nRF24_set_TX_addr(nRF24_addr0, 5);
nRF24_set_RX_addr(0, nRF24_addr0, 5);
nRF24_enable_data_pipe_dynamic_payload_length(0, true);
- 接收端設定:
nRF24_spi_default_init(20, 21, nRF24_irq_callback);
nRF24_config_mode(RECEIVER);
nRF24_enable_feature(FEATURE_EN_DPL, true);
nRF24_set_RX_addr(0, nRF24_addr0, 5);
nRF24_enable_data_pipe_dynamic_payload_length(0, true);
- nRF24_irq_callback:
static uint8_t XY_AXIS;
static uint8_t ang[4];
uint8_t data_buffer[33];
uint8_t data_len;
switch(event_type) {
case EVENT_RX_DR:
#ifdef TRANS_SIDE
#else
data_len = width;
memcpy(data_buffer, data, width);
deencrypt_data(data_buffer, &data_len);
XY_AXIS=data_buffer[0];
memset(ang,0,sizeof(ang));
memcpy(ang, data_buffer+2, width-2);
if (XY_AXIS == 'X') {
setServoAngle(atoi(ang), pwm_ch_x, pwm_slice_x);
}
if (XY_AXIS == 'Y') {
setServoAngle(atoi(ang), pwm_ch_y, pwm_slice_y);
}
#endif
break;
case EVENT_TX_DS:
break;
case EVENT_MAX_RT:
break;
}
}
四、成果影片:
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/clocks.h" #include "stdlib.h" #include "hardware/pwm.h" #include "hardware/adc.h" #include "nRF24L01.h" #include "string.h" #define PWM_X_PIN 14 #define PWM_Y_PIN 15 uint pwm_ch_x, pwm_slice_x; uint pwm_ch_y, pwm_slice_y; int32_t servo_x_ang, servo_y_ang; #define ADC_X_PIN 26 #define ADC_X_NUM 0 #define ADC_Y_PIN 27 #define ADC_Y_NUM 1 #define ADC_RANGE (1<<12) // pi pico 12 bits ADC int32_t adc_x_center, adc_y_center; #define TRANS_SIDE uint8_t nRF24_addr0[] = "0node"; void encrypt_data(uint8_t *data, uint8_t *len) { // do data encryption return; } void deencrypt_data(uint8_t *data, uint8_t *len) { // do data deencryption return; } void setServoAngle(int ang, uint pwm_ch, uint slice) { //uint16_t top_count = (uint16_t)(1000/180*ang + 1500); // angle 0: 1.5 ms duty uint16_t top_count = (uint16_t)(ang/0.09 + 1500); // angle 0: 1.5 ms duty pwm_set_chan_level(slice, pwm_ch, top_count); } void servo_init(uint pin, uint *pwm_ch, uint* slice) { // set Servo pwm gpio_set_function(pin, GPIO_FUNC_PWM); *slice = pwm_gpio_to_slice_num(pin); *pwm_ch = pwm_gpio_to_channel(pin); pwm_config c = pwm_get_default_config(); pwm_config_set_clkdiv(&c, 125); // 20ms period, steps 20000, clkdiv = 125 pwm_config_set_wrap(&c, 20000); pwm_config_set_phase_correct(&c, false); pwm_init(*slice, &c, true); } void nRF24_irq_callback(uint8_t event_type, uint8_t datapipe, uint8_t* data, uint8_t width) { static uint8_t XY_AXIS; static uint8_t ang[4]; uint8_t data_buffer[33]; uint8_t data_len; switch(event_type) { case EVENT_RX_DR: #ifdef TRANS_SIDE #else data_len = width; memcpy(data_buffer, data, width); deencrypt_data(data_buffer, &data_len); XY_AXIS=data_buffer[0]; memset(ang,0,sizeof(ang)); memcpy(ang, data_buffer+2, width-2); if (XY_AXIS == 'X') { setServoAngle(atoi(ang), pwm_ch_x, pwm_slice_x); } if (XY_AXIS == 'Y') { setServoAngle(atoi(ang), pwm_ch_y, pwm_slice_y); } #endif break; case EVENT_TX_DS: break; case EVENT_MAX_RT: break; } } int main() { uint8_t payload[33]; uint8_t payload_len; stdio_init_all(); nRF24_spi_default_init(20, 21, nRF24_irq_callback); #ifdef TRANS_SIDE nRF24_config_mode(TRANSMITTER); nRF24_enable_feature(FEATURE_EN_DPL, true); nRF24_set_TX_addr(nRF24_addr0, 5); nRF24_set_RX_addr(0, nRF24_addr0, 5); nRF24_enable_data_pipe_dynamic_payload_length(0, true); adc_init(); // init and assign ADC pin adc_gpio_init(ADC_X_PIN); adc_gpio_init(ADC_Y_PIN); for (int i=0; i < 100; i++) { // average 100 samples as mouse static position adc_select_input(ADC_X_NUM); adc_x_center += adc_read(); adc_select_input(ADC_Y_NUM); adc_y_center += adc_read(); } adc_x_center /=100; adc_y_center /=100; #else nRF24_config_mode(RECEIVER); nRF24_enable_feature(FEATURE_EN_DPL, true); nRF24_set_RX_addr(0, nRF24_addr0, 5); nRF24_enable_data_pipe_dynamic_payload_length(0, true); servo_y_ang = 0; servo_x_ang = 0; servo_init(PWM_X_PIN, &pwm_ch_x, &pwm_slice_x); servo_init(PWM_Y_PIN, &pwm_ch_y, &pwm_slice_y); setServoAngle(servo_x_ang, pwm_ch_x, pwm_slice_x); setServoAngle(servo_y_ang, pwm_ch_y, pwm_slice_y); #endif int32_t adc_raw; while(1) { #ifdef TRANS_SIDE adc_select_input(ADC_X_NUM); adc_raw = adc_read(); if(abs(adc_raw-adc_x_center) > 60) { //servo_x_ang=(adc_raw-adc_x_center)*90/adc_x_center; if (adc_raw < adc_x_center) { servo_x_ang++; if (servo_x_ang > 90) servo_x_ang = 90; } else { servo_x_ang--; if (servo_x_ang < -90) servo_x_ang = -90; } //setServoAngle(servo_x_ang, pwm_ch_x, pwm_slice_x); sprintf(payload,"X:%d", servo_x_ang); payload_len = strlen(payload); encrypt_data(payload, &payload_len); nRF24_write_payload(payload, payload_len); printf("payload:%s\n", payload); } adc_select_input(ADC_Y_NUM); adc_raw = adc_read(); if(abs(adc_raw-adc_y_center) > 60) { if (adc_raw < adc_y_center) { servo_y_ang++; if (servo_y_ang > 90) servo_y_ang = 90; } else { servo_y_ang--; if (servo_y_ang < -90) servo_y_ang = -90; } //setServoAngle(servo_y_ang, pwm_ch_y, pwm_slice_y); sprintf(payload,"Y:%d", servo_y_ang); payload_len = strlen(payload); encrypt_data(payload, &payload_len); nRF24_write_payload(payload, payload_len); printf("payload:%s\n", payload); } //printf("payload:%s\n", payload); #endif sleep_ms(20); } return 0; }
沒有留言:
張貼留言