I am currently working in project with nrf54l15dk and 3.0.2 sdk. I'm trying to work with peripherals and I want my project to do the following:
-Have 2 PWM outputs that used for IN1 and IN2 for DC motor.
-Have 1 ADC input that measure the current of the motor and led blink when it is more than the threshold current.
But I have come across with an issue while I was setting the adc within my code.
Here is my whole main.c:
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/util.h>
#include <inttypes.h>
#include <zephyr/drivers/pwm.h> //PWM ekledik
#include <zephyr/drivers/adc.h> //ADC ekledik
#include <zephyr/logging/log.h> // log için
LOG_MODULE_REGISTER(main);
//ADC İÇİN DEĞİŞKENLER BUNLAR:
#define MotorAkimSinir 0.96f // VStall (1.2a) dan biraz küçük normal çalışma(0.3a) akımından büyük.
// IPROPI pininden toprağa bağladığınız direncin Ohm cinsinden değeri.
// Datasheet'teki örnekte 8.45 kΩ kullanılmış, siz kendi devrenizdeki değeri yazmalısınız.
#define R_IPROPI 6675.0f // Kendi devrenizdeki R_IPROPI direncini Ohm cinsinden yazın. 6675ohm
// Motor akımı kazanç faktörü (A_IPROPI).
// Bu değer, donanımınızdaki GAINSEL pininin nasıl bağlandığına göre seçilmelidir.
// Datasheet Tablo 8-3'e göre aşağıdaki 3 seçenekten SADECE BİRİNİ seçin.
// Diğer ikisini yoruma alın.
// Seçenek 1: GAINSEL pini LOW (GND) ise (350mA - 2A arası akımlar için önerilir)
//#define A_IPROPI 0.000205f // 205 µA/A
// Seçenek 2: GAINSEL pini FLOATING (boşta) ise (60mA - 350mA arası akımlar için önerilir)
//#define A_IPROPI 0.001050f // 1050 µA/A
// Seçenek 3: GAINSEL pini HIGH (VCC) ise (10mA - 60mA arası akımlar için önerilir)
#define A_IPROPI 0.004900f // 4900 µA/A
#define adc_max_value 16383.0f // 14-bit ADC için maksimum değer
#define vref 0.6f // referans voltaj
#define GAIN (1.0f / 4.0f)
static int led_acikMi=0; //led başlangıçta kapalı
static struct k_timer led_timer;
// IN1 VE IN2 İÇİN TANIMLAMA
#define PWM_IN1_NODE DT_NODELABEL(motor)
#define PWM_IN2_NODE DT_NODELABEL(motor2)
static const struct pwm_dt_spec pwm_in1 = PWM_DT_SPEC_GET(PWM_IN1_NODE);
static const struct pwm_dt_spec pwm_in2 = PWM_DT_SPEC_GET(PWM_IN2_NODE);
int current_direction = 0; // 0: dur, 1: ileri, 2: geri
static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user)); //adc için kanal tanımlama
#define SLEEP_TIME_MS 50 //adc okuma aralığı
/* Buton alias’ları */
#define BTN0_NODE DT_ALIAS(sw0)
#define BTN1_NODE DT_ALIAS(sw1)
#define BTN2_NODE DT_ALIAS(sw2)
#define BTN3_NODE DT_ALIAS(sw3)
static const struct gpio_dt_spec buttons[] = {
GPIO_DT_SPEC_GET_OR(BTN0_NODE, gpios, {0}),
GPIO_DT_SPEC_GET_OR(BTN1_NODE, gpios, {0}),
GPIO_DT_SPEC_GET_OR(BTN2_NODE, gpios, {0}),
GPIO_DT_SPEC_GET_OR(BTN3_NODE, gpios, {0}),
};
static struct gpio_callback button_cb_data[4];
/* --- Prototipler (buton görevleri) --- */
void button1_action(void);
void button2_action(void);
void button3_action(void);
void button4_action(void);
void led_timer_handler(struct k_timer *timer_id); // Forward declaration for led_timer_handler
void led_timer_stop_handler(struct k_timer *timer_id);
// BUNLAR HEP Mikro SANİYE CİNSİNDEN
#define PWM_PERIOD_USEC 10 // 1/25KHZ = 40us periyot 1/100khz = 10us periyot
#define PWM_PULSE_LOW 0
#define PWM_PULSE_HIGH 5 // %50 duty cycle BU DEĞİŞECEK.
//25khz için statik atadım.
#define PWM_MIN_PULSE_WIDTH 2 // %10 doluluk µs
#define PWM_MAX_PULSE_WIDTH 9 // %90 doluluk oldu µs
int pwm_pulse_high = PWM_PULSE_HIGH; // global değişken yaptık ki butonlara göre değiştirebilelim.
/* --- Tek handler: hangi buton basıldıysa ilgili fonksiyonu çağırır --- */
void button_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
for (int i = 0; i < 4; i++) {
if (pins & BIT(buttons[i].pin)) {
LOG_INF("Buton %d basıldı\n", i );
switch (i) {
case 0: button1_action(); break;
case 1: button2_action(); break;
case 2: button3_action(); break;
case 3: button4_action(); break;
default: break;
}
}
}
}
/*
Bu ledi adc ile okuma yaparken kullanacağım. */
static struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios, {0});
int main(void)
{ LOG_INF("Başladı");
LOG_INF("ADC device: %s, channel: %d\n", adc_channel.dev->name, adc_channel.channel_id);
int ret;
int err;
int16_t buffer; //adc okuma bufferı
//adc hazır mı bak.
if (!adc_is_ready_dt(&adc_channel)) {
LOG_ERR("ADC Kontrol Cihazı %s hazır değil.", adc_channel.dev->name);
return 0;
}
err = adc_channel_setup_dt(&adc_channel); //channel ayarla
if (err < 0) {
LOG_ERR("Adc kanali ayarlanmadi = #%d (%d)", 0, err);
return 0;
}
struct adc_sequence sequence = {
.buffer = &buffer, //burası adc sayısal değerini tutacak bunu çevireceğim.
// buffer size in bytes,
.buffer_size = sizeof(buffer),
.channels = BIT(adc_channel.channel_id),
};
LOG_INF("ADC Hazır. Kanal %d\n", adc_channel.channel_id);
k_timer_init(&led_timer, led_timer_handler,led_timer_stop_handler); //led timerı başlat
// PWM cihazlarının hazır olup olmadığını kontrol et
if (!pwm_is_ready_dt(&pwm_in1) || !pwm_is_ready_dt(&pwm_in2)) {
LOG_INF("PWM'ler hazır değil!\n");
return 0;
}
/* PWM çıkışlarını başlangıçta sıfırla */
pwm_set_pulse_dt(&pwm_in1, 0);
pwm_set_pulse_dt(&pwm_in2, 0);
// Tüm Butonların interruptlarını ayarla
for (int i = 0; i < 4; i++) {
if (!gpio_is_ready_dt(&buttons[i])) {
LOG_INF("Buton %d hazır değil!\n", i);
continue;
}
ret = gpio_pin_configure_dt(&buttons[i], GPIO_INPUT);
if (ret) {
LOG_INF("Buton %d yapılandırma hatası (%d)\n", i, ret);
continue;
}
ret = gpio_pin_interrupt_configure_dt(&buttons[i], GPIO_INT_EDGE_TO_ACTIVE);
if (ret) {
LOG_INF("Buton %d interrupt hatası (%d)\n", i, ret);
continue;
}
//HER FONKSİYONA CALLBACKLER ATANDI.
gpio_init_callback(&button_cb_data[i], button_handler, BIT(buttons[i].pin));
gpio_add_callback(buttons[i].port, &button_cb_data[i]);
LOG_INF("Buton %d -> %s pin %d\n", i , buttons[i].port->name, buttons[i].pin);
}
LOG_INF("Kurulum tamamlandı. 4 buton aktif.\n");
while (1) {
err = adc_read(adc_channel.dev, &sequence);
k_msleep(5); //küçük bir bekleme ekledim adc patlamasın diye
if (err < 0) {
LOG_ERR("Gerilimi Okumadi: (%d)", err);
continue;
}
int16_t adc_raw = buffer;
float v_ipropi = ((adc_raw / adc_max_value) * vref) / GAIN; //formül böyle gerilim için
// 2. Datasheet'teki formülü kullanarak motor akımını hesapla.
// I_MOTOR = V_IPROPI / (R_IPROPI * A_IPROPI)
float motor_current = v_ipropi / (R_IPROPI * A_IPROPI); //oranlı direnç V=IR den formül
if(motor_current > MotorAkimSinir){
k_timer_start(&led_timer, K_MSEC(0), K_MSEC(100)); // 100ms yansön yapcak
}
else{
k_timer_stop(&led_timer);
gpio_pin_set_dt(&led, 0); //ledi kapat.
}
k_sleep(K_MSEC(SLEEP_TIME_MS)); //50ms de bir okuma yapacak
}
}
/* Buton Fonksiyonları */
void button1_action(void)
{
//SSOLA DÖNSÜN IN1 AKTİF
current_direction = 1; // sola dönüyor
pwm_set_dt(&pwm_in1, PWM_USEC(PWM_PERIOD_USEC), PWM_USEC(pwm_pulse_high)); //IN1 İÇİN
pwm_set_dt(&pwm_in2, PWM_USEC(PWM_PERIOD_USEC), PWM_USEC(PWM_PULSE_LOW)); //IN2 İÇİN
LOG_INF("Sola git (IN1 aktif)\n");
}
void button2_action(void)
{
current_direction = 2; // sağa dönüyor
pwm_set_dt(&pwm_in1, PWM_USEC(PWM_PERIOD_USEC), PWM_USEC(PWM_PULSE_LOW)); //IN1 İÇİN LOW
pwm_set_dt(&pwm_in2, PWM_USEC(PWM_PERIOD_USEC), PWM_USEC(pwm_pulse_high)); //IN2 İÇİN HIGH VERECEK Kİ SAĞA DÖNSÜN
LOG_INF("Sağa git (IN2 aktif)\n");
}
void button3_action(void)
{
//YAVAŞLATMA
LOG_INF("Button 3 YAVAŞLAMA\n");
pwm_pulse_high -= 1;
if (pwm_pulse_high < PWM_MIN_PULSE_WIDTH) {
pwm_pulse_high = PWM_MIN_PULSE_WIDTH; // Minimum değerden aşmasın
}
// Değişikliği anında uygula
if (current_direction == 1) { // Eğer sola dönüyorsa
pwm_set_pulse_dt(&pwm_in1, PWM_USEC(pwm_pulse_high));
} else if (current_direction == 2) { // Eğer sağa dönüyorsa
pwm_set_pulse_dt(&pwm_in2, PWM_USEC(pwm_pulse_high));
}
}
void button4_action(void)
{
//HIZLANDIRMA
LOG_INF("Button 4 HIZLANMA\n");
pwm_pulse_high += 1;
if (pwm_pulse_high > PWM_MAX_PULSE_WIDTH) {
pwm_pulse_high = PWM_MAX_PULSE_WIDTH; // Maksimum değeri aşmasın
}
// Değişikliği anında uygula
if (current_direction == 1) { // Eğer sola dönüyorsa
pwm_set_pulse_dt(&pwm_in1, PWM_USEC(pwm_pulse_high));
} else if (current_direction == 2) { // Eğer sağa dönüyorsa
pwm_set_pulse_dt(&pwm_in2, PWM_USEC(pwm_pulse_high));
}
}
void led_timer_handler(struct k_timer *timer_id)
{
led_acikMi = !led_acikMi; //led durumunu değiştir
gpio_pin_set_dt(&led, led_acikMi); //ledi aç/kapa
}
void led_timer_stop_handler(struct k_timer *timer_id)
{
led_acikMi = 0; //ledi kapatır.
gpio_pin_set_dt(&led, 0); // LED'i kapat
}
And thats my .overlay file:
// To get started, press Ctrl+Space to bring up the completion menu and view the available nodes.
// You can also use the buttons in the sidebar to perform actions on nodes.
// Actions currently available include:
// * Enabling / disabling the node
// * Adding the bus to a bus
// * Removing the node
// * Connecting ADC channels
// For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html
// You can also visit the nRF DeviceTree extension documentation at https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/guides/ncs_configure_app.html#devicetree-support-in-the-extension
//adc için de ekleme yaptık.
//nodeismi motor oldu. pwm-motor ile compatible, pwm21 ve pwm22 ile sürülecek. 20ms periyodu var, max ve min pulseları da 1ms ve 2ms
/ {
motor: motor {
compatible = "pwm-motor";
pwms = <&pwm21 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
min-pulse = <PWM_USEC(1000)>;
max-pulse = <PWM_USEC(2000)>;
}; //buradan almıyorum min ve max pulse
motor2: motor2 {
compatible = "pwm-motor";
pwms = <&pwm22 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
min-pulse = <PWM_USEC(1000)>;
max-pulse = <PWM_USEC(2000)>;
};
zephyr,user{
// io-channels hem ADC'yi hem de kullanılacak pini belirtir.
io-channels = <&adc 7>;
// Tüm kanal ayarlarını buraya taşıyoruz:
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <14>;
status = "okay";
};
};
&adc {
status = "okay";
};
// PWM 21 P1.11 İÇİN:
&pwm21 {
status = "okay";
pinctrl-0 = <&pwm21_custom_motor>;
pinctrl-1 = <&pwm21_csleep_motor>;
pinctrl-names = "default", "sleep";
};
// PWM 22 P1.12 İÇİN:
&pwm22 {
status = "okay";
pinctrl-0 = <&pwm22_custom_motor2>;
pinctrl-1 = <&pwm22_csleep_motor2>;
pinctrl-names = "default", "sleep";
};
&pinctrl {
pwm21_custom_motor: pwm21_custom_motor {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 11)>;
nordic,invert;
};
};
pwm21_csleep_motor: pwm21_csleep_motor {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 11)>;
low-power-enable;
};
};
pwm22_custom_motor2: pwm22_custom_motor2 {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 12)>;
nordic,invert;
};
};
pwm22_csleep_motor2: pwm22_csleep_motor2 {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 12)>;
low-power-enable;
};
};
};
// Çakışan aygıtları devre dışı bırakıyoruz.
&led2 {
status = "disabled";
};
&led3 {
status = "disabled";
};
&led1 {
status = "disabled";
};
I always get :
*** Booting nRF Connect SDK v3.0.2-89ba1294ac9b ***
*** Using Zephyr OS v4.0.99-f791c49f492c ***
[00:00:00.002,005] <dbg> os: k_sched_unlock: scheduler unlocked (0x200007b0:0)
[00:00:00.002,010] <inf> main: Başladı
[00:00:00.002,023] <inf> main: ADC device: adc@d5000, channel: 7
[00:00:00.002,028] <err> main: Adc kanali ayarlanmadi = #0 (-134) this output that says ADC CHANNEL DIDNT GET SET!
Can you please help me with that?
I already tried changing the pin and gain but it didn't work.