KevinOConnor / can2040

Software CAN bus implementation for rp2040 micro-controllers
GNU General Public License v3.0
667 stars 66 forks source link

Compiling issue #10

Closed agmui closed 1 year ago

agmui commented 2 years ago

Hey, First, I would like to say how great this project is. I was trying to run this project the other day on a pi pico connected to a Waveshare SN65HVD230 CAN Board. I followed the example code shown but CMake threw this error

[build] /home/agmui/cs/Robomasters/pico/CANbus/temp/can.c:39:31: error: 'PIO0_IRQ_0_IRQn' undeclared (first use in this function); did you mean 'PIO0_IRQ_0'?
[build]    39 |     irq_set_exclusive_handler(PIO0_IRQ_0_IRQn, PIOx_IRQHandler);
[build]       |                               ^~~~~~~~~~~~~~~
[build]       |                               PIO0_IRQ_0
[build] /home/agmui/cs/Robomasters/pico/CANbus/temp/can.c:39:31: note: each undeclared identifier is reported only once for each function it appears in
[build] /home/agmui/cs/Robomasters/pico/CANbus/temp/can.c:40:5: warning: implicit declaration of function 'NVIC_SetPriority' [-Wimplicit-function-declaration]
[build]    40 |     NVIC_SetPriority(PIO0_IRQ_0_IRQn, 1);
[build]       |     ^~~~~~~~~~~~~~~~
[build] /home/agmui/cs/Robomasters/pico/CANbus/temp/can.c:41:5: warning: implicit declaration of function 'NVIC_EnableIRQ' [-Wimplicit-function-declaration]
[build]    41 |     NVIC_EnableIRQ(PIO0_IRQ_0_IRQn);
[build]       |     ^~~~~~~~~~~~~~
[build] make[2]: *** [temp/CMakeFiles/build.dir/build.make:63: temp/CMakeFiles/build.dir/can.c.obj] Error 1
[build] make[1]: *** [CMakeFiles/Makefile2:1805: temp/CMakeFiles/build.dir/all] Error 2
[build] make: *** [Makefile:84: all] Error 2

I saw in the previous issue that you could put PIO0_IRQ_0 and I tried that but when I built it and used an oscilloscope I only got noise. Is this because I am compiling wrong through CMake and not just using the command shown in the guide?

Here is my code:

#include <stdio.h>
// #include <intctrl.h>
#include "pico/stdlib.h"
#include "can2040.h"
// #include "../../pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RaspberryPi/RP2040/Include/RP2040.h" 
// #include "../../pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Core/Include/core_cm0plus.h"

const uint led_pin = 25;
static struct can2040 cbus;

static void
can2040_cb(struct can2040 *cd, uint32_t notify, struct can2040_msg *msg)
{
    // Add message processing code here...
}

static void
PIOx_IRQHandler(void)
{
    can2040_pio_irq_handler(&cbus);
}

void
canbus_setup(void)
{
    uint32_t pio_num = 0;
    uint32_t sys_clock = 125000000, bitrate = 1000000;
    uint32_t gpio_rx = 3, gpio_tx = 5;

    // Setup canbus
    can2040_setup(&cbus, pio_num);
    can2040_callback_config(&cbus, can2040_cb);

    // Enable irqs
    irq_set_exclusive_handler(PIO0_IRQ_0_IRQn, PIOx_IRQHandler);
    NVIC_SetPriority(PIO0_IRQ_0_IRQn, 1);
    NVIC_EnableIRQ(PIO0_IRQ_0_IRQn);

    // Enable irqs
    // irq_set_exclusive_handler(PIO0_IRQ_0, PIOx_IRQHandler);
    // irq_set_priority(PIO0_IRQ_0, 1);
    // irq_set_enabled(PIO0_IRQ_0, true);

    // Start canbus
    can2040_start(&cbus, sys_clock, bitrate, gpio_rx, gpio_tx);
}

void main()
{
    canbus_setup();

    struct can2040_msg msg;
    msg.id = 0x201;//id
    msg.dlc = 8;//msg size
    msg.data[0] = 0x01;
    msg.data[1] = 0xFF;
    msg.data[2] = 0x01;
    msg.data[3] = 0xFF;
    msg.data[4] = 0x01;
    msg.data[5] = 0xFF;
    msg.data[6] = 0x01;
    msg.data[7] = 0xFF;

    // Initialize LED pin
    gpio_init(led_pin);
    gpio_set_dir(led_pin, GPIO_OUT);

    while(true){
        gpio_put(led_pin, true);
        sleep_ms(1000);
        gpio_put(led_pin, false);
        sleep_ms(1000);
        int x = can2040_transmit(&cbus, &msg);
        sleep_ms(100);
    }
}
zwu513 commented 2 years ago

Check your cmake CMakeLists.txt

agmui commented 2 years ago

Okay, so I found out I was just dumb and did not include one of the files correctly and the whole code was compiled. However, the signal coming out of tx was just constant high and I double checked with an oscope to see what was going on. I then copied the code here and the output for some reason was coming out of GPIO 0 and not 5. Also for the ESC I am using it requires that I have a bitrate of 1Mbps but on the oscope the pico it did not seem like it was outputting 1Mbps. In the code I put this bitrate = 1000000 but it did not seem like the signal was the same frequency as another microcontroller that was outputting the correct signal.

zwu513 commented 2 years ago

Copy the setup code on this page. https://github.com/KevinOConnor/can2040/blob/master/docs/API.md

agmui commented 2 years ago

I tried using that one but no output came out of gpio 5 itwas just high like the other code. I don't know if its just me or the board is broken but im semi confident that the code is fine because I just copyed and paisted the example code.

zwu513 commented 2 years ago

What board and what can transceiver?

agmui commented 2 years ago

(blue) pi pico -> SN65HVD230 (yellow) arduino uno -> mcp2515 (purple: only working one) robomasters devlempment board type A

Here are each of the signals on the oscope. I just thought I would include it. image

zwu513 commented 2 years ago

All set to the same bit rate and connected by a single ground plane?

agmui commented 2 years ago

Yea, I put in the code to all be the same gnd, data, id, and bitrate.

Should I post my code?

zwu513 commented 2 years ago

Sure

agmui commented 2 years ago

pico:

#include <stdio.h>
#include "pico/stdlib.h"
#include "can2040.h"

typedef struct can2040 CANHandle;
typedef struct can2040_msg CANMsg;

static CANHandle can1;

static void can2040_cb1(CANHandle *cd, uint32_t notify, CANMsg *msg) {
  printf("can1 callback called. id: &d", notify);
}

static void PIO1_IRQHandler(void) {
  can2040_pio_irq_handler(&can1);
}

void canbus_setup() {
  uint32_t sys_clock = 125000000, bitrate = 1000000;//250000;

  // Setup canbus
  can2040_setup(&can1, 1);
  can2040_callback_config(&can1, can2040_cb1);

  // Enable irqs
  irq_set_exclusive_handler(PIO1_IRQ_0, PIO1_IRQHandler);
  irq_set_priority(PIO1_IRQ_0, 1);
  irq_set_enabled(PIO1_IRQ_0, true);

  // Start canbus
  can2040_start(&can1, sys_clock, bitrate, 8, 10);
}

int main() {
  stdio_init_all();

  sleep_ms(10);
  printf("Startup delay over\n");

  printf("Starting Initialization of CAN\n");
  canbus_setup();
  printf("Initialized CAN1\n");

  sleep_ms(1000);
  while (true) {
    CANMsg msg = {0};
    msg.dlc = 8;
    msg.id = 0x200;
    msg.data[0] = 0x01;
    msg.data[1] = 0x01;
    msg.data[2] = 0x01;
    msg.data[3] = 0x01;
    msg.data[4] = 0x01;
    msg.data[5] = 0x01;
    msg.data[6] = 0x01;
    msg.data[7] = 0x01;
    int res = can2040_transmit(&can1, &msg);
    printf("Sending! returned: %d\n", res);
    // sleep_ms(1000);
    sleep_ms(10);
  }
}

cmake:

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/agmui/cs/Robomasters/pico/pico-sdk")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

project(test C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1
add_executable(test
         can.c
         can2040.c
        )

pico_set_program_name(test "test")
pico_set_program_version(test "0.1")

pico_enable_stdio_uart(test 1)
pico_enable_stdio_usb(test 1)

# Add the standard library to the build
target_link_libraries(test
         pico_stdlib)

# Add the standard include files to the build
target_include_directories(test PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
  /home/agmui/cs/Robomasters/pico/pico-sdk/src/rp2040/
  /home/agmui/cs/Robomasters/pico/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Core/Include/
  /home/agmui/cs/Robomasters/pico/pico-sdk/src/rp2_common/cmsis/stub/CMSIS/Device/RaspberryPi/RP2040/Include/
)

# Add any user requested libraries
target_link_libraries(test
        hardware_spi
        hardware_pio
        )

pico_add_extra_outputs(test)

arduino:

#include <SPI.h>
#include <mcp2515.h>

struct can_frame canMsg1;
struct can_frame canMsg2;
MCP2515 mcp2515(10);

void setup() {
  canMsg1.can_id  = 0x200;
  canMsg1.can_dlc = 8;
  canMsg1.data[0] = 0x01;
  canMsg1.data[1] = 0x01;
  canMsg1.data[2] = 0x01;
  canMsg1.data[3] = 0x01;
  canMsg1.data[4] = 0x01;
  canMsg1.data[5] = 0x01;
  canMsg1.data[6] = 0x01;
  canMsg1.data[7] = 0x01;

  while (!Serial);
  Serial.begin(115200);

  mcp2515.reset();
  mcp2515.setBitrate(CAN_1000KBPS,MCP_20MHZ);
  mcp2515.setNormalMode();

  Serial.println("Example: Write to CAN");
}

void loop() {
  mcp2515.sendMessage(&canMsg1);
  //mcp2515.sendMessage(&canMsg2);

  Serial.println("Messages sent");

  delay(100);
}

I don't remember if I had the same data values as in the picture but it's the id that really matters.

zwu513 commented 2 years ago

Can you set the robomaster to read packets and only the rp2040 to send?

agmui commented 2 years ago

I don't have it all set up at the moment and I will see if I can get that set up but I doubt it would work. When I try to get the rp2040 to talk to the robotmasters ESC nothing happens same with the Arduino. I also tried to get the Arduino to read the rp2040 and nothing happened.

zwu513 commented 2 years ago

Are all the physical connections good? Do you have terminating resistors?

agmui commented 2 years ago

I'm like pretty sure they are all good.

zwu513 commented 2 years ago

Try this

int main() {
canbus_setup();

struct can2040_msg message;
message.id = 161;
message.dlc = 4;

while(1)
{
    for(int i = 0; i < 255; i++)
    {
        int send = -1;

        message.data32[0] = i; 

        while( send != 0)
        {
                send = can2040_transmit(&cbus, &message);
        }

    }
}

}

KevinOConnor commented 2 years ago

If you want to check the software (and/or hardware connectivity) one possibility is to download Klipper, compile it for the rp2040 (via make menuconfig) with "USB to CAN bridge" support, flash the rp2040 with that code, and attach the resulting device to a linux machine. That will cause the device to act as a standard Linux "usb to can" adapter. You can then send and receive messages using the standard linux "candump" and "cansend" commands.

https://www.klipper3d.org/CANBUS.html

https://github.com/Klipper3d/klipper

-Kevin

agmui commented 1 year ago

Ok, so i finally got the usb to can set up on the pico but when I run cansend can0 200#DEADBEEF on the oscilloscope it just shows high on GPIO 5 relative to ground and same for GPIO 4.

KevinOConnor commented 1 year ago

I'm not sure what would cause the issues here. However, there were several fixes recently that could impact testing of the code, in particular with multiple retransmits. (Issue #14 and PR #15 and PR #16.) So, you may wish to update to the latest code.

Also, I've added a page to document some available tools: https://github.com/KevinOConnor/can2040/blob/master/docs/Tools.md . As noted in that document, make sure there is a fully functional canbus (with at least one other device) even for testing.

-Kevin

agmui commented 1 year ago

Thank you so much, I will try that when I get back and see what will happen with the new code.

github-actions[bot] commented 1 year ago

Hello,

It looks like there hasn't been any recent updates on this github ticket. We prefer to only list tickets as "open" if they are actively being worked on. Feel free to provide an update on this ticket. Otherwise the ticket will be automatically closed in a few days.

Best regards,

~ Your friendly GitIssueBot

PS: I'm just an automated script, not a human being.

thestumbler commented 1 year ago

I'm going through similar issues, trying out Klipper in USB to CAN bridge mode. I'm not sure how to specify the pins in menuconfig. I can't find any examples of how to set this string, which seems to also include the CAN bitrate as well.

EDIT: solved this. I had missed one of the menu options in menuconfig. I also figured out the USB to CAN interface being presented was gs_usb and not slcan. Once all that was sorted out, I can send/receive CAN over the RP2040. Next up -- compile my own application.

agmui commented 1 year ago

So I know it has been a while but I had finally figured out what went wrong. Turns out I am just dumb and never thought to wire up the CAN transceiver(SN65HVD230) to the pico. The picture above of the oscilloscope was just the signal from the uart port.

The reason why I plugged the oscilloscope into the uart port was because it was the only pin that was giving off a signal so I thought it was the correct pin. Because at the time pins 4 and 5, the rx and tx pins I set in the code, were only showing high and nothing at all. Only yesterday I thought about connecting the CAN transceiver to the pico did everything finally work properly.

Also regarding the CMake issue I normally exclusively code in python and have little to no experience in c++ so CMake was very new to me. Only when I decided to sit down and properly learn about CMake did everything compile.

I am very new to embedded development do only in hindsight I realized how dumb I was.

Anyways here is my CMake file and example code. Its probably not the best but it is what compiles for me.

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/agmui/cs/pico/pico-sdk")

set(PICO_BOARD pico CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
  message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()

project(rm_pico_dev C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(rm_pico_dev canbus.cpp )

pico_set_program_name(rm_pico_dev "canbus")
pico_set_program_version(rm_pico_dev "0.1")

pico_enable_stdio_uart(rm_pico_dev 0)
pico_enable_stdio_usb(rm_pico_dev 1)

# Add the standard library to the build
target_link_libraries(rm_pico_dev
        pico_stdlib)

# Add the standard include files to the build
target_include_directories(rm_pico_dev PRIVATE
  ${CMAKE_CURRENT_LIST_DIR}
  ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)

add_library(can2040 can2040.c can2040.h)
target_link_libraries(can2040 pico_stdlib
        pico_base_headers
        hardware_dma
        cmsis_core
)
target_link_libraries(rm_pico_dev 
        can2040
        )

# Add any user requested libraries
target_link_libraries(rm_pico_dev 
        hardware_pio

        pico_base_headers
        hardware_dma
        cmsis_core
        )

pico_add_extra_outputs(rm_pico_dev)

c++ code:

This is just the copied code from the example


#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
extern "C"{
#include "can2040.h"
}
#include "RP2040.h"

typedef struct can2040_msg CANMsg; static struct can2040 cbus;

static void can2040_cb(struct can2040 cd, uint32_t notify, struct can2040_msg msg) { // Add message processing code here... }

static void PIOx_IRQHandler(void) { can2040_pio_irq_handler(&cbus); }

void canbus_setup(void) { uint32_t pio_num = 0; uint32_t sys_clock = 125000000, bitrate = 500000; uint32_t gpio_rx = 4, gpio_tx = 5;

// Setup canbus
can2040_setup(&cbus, pio_num);
can2040_callback_config(&cbus, can2040_cb);

// Enable irqs
irq_set_exclusive_handler(PIO0_IRQ_0_IRQn, PIOx_IRQHandler);
NVIC_SetPriority(PIO0_IRQ_0_IRQn, 1);
NVIC_EnableIRQ(PIO0_IRQ_0_IRQn);

// Start canbus
can2040_start(&cbus, sys_clock, bitrate, gpio_rx, gpio_tx);

}

int main() { stdio_init_all(); canbus_setup();

const uint LED_PIN = PICO_DEFAULT_LED_PIN;
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
while (true) {
    CANMsg msg = {0};
    msg.dlc = 8;
    msg.id = 0x200;
    msg.data[0] = 0x01;
    msg.data[1] = 0x01;
    msg.data[2] = 0x01;
    msg.data[3] = 0x01;
    msg.data[4] = 0x01;
    msg.data[5] = 0x01;
    msg.data[6] = 0x01;
    msg.data[7] = 0x01;
    int res = can2040_transmit(&cbus, &msg);
    printf("Sending! returned: %d\n", res);

    printf("LED ON!\n");
    gpio_put(LED_PIN, 1);
    sleep_ms(250);
    printf("LED OFF!\n");
    gpio_put(LED_PIN, 0);
    sleep_ms(250);
    sleep_ms(1000);
}

return 0;

}



Here is the repo with the code:
[repo](https://github.com/agmui/rm_pico_dev/tree/c881497b965e519618b53bb8f6eedbbe90d0aa54)