Photon CAN Bus [Completed]

ok, so I made a fork and committed the files I had. I am not sure how to “switch” the existing checkout to my fork so I tried to ensure I got all my changes copied over to the forked copy. I am a subversion user… this git stuff is just wierd…lol

Anyways my code is here:

Can someone familiar with particle driver development take a look and tell me if I am headed in the correct direction.

2 Likes

haha! I used to also be an avid subversion user. But once you eventually internalize how git works, you’ll wonder why all CVSs weren’t built like that! I wouldn’t go back to SVN or any other non-distributed CVS now.

Who is the best person to review my driver layout?

I know it won’t work yet but I am not familiar with the particle architecture.

@Bspranger I’d love to help you out but I’m not familiar with it yet either. I’ve got some big plans for this so I’ll let you know when my photon arrives and I get started with it. I think you are probably more familiar with CAN then I am anyway but I’m keen to get this up and running in the near future so hopefully we can collaborate and I can help you where possible to finish off the libraries.

My biggest weakness is available time. I only get small fragments of time to work on it. But I need this CAN bus driver, so I will get it done…

3 Likes

So which file should I be adding the actual CAN interrupts into. I know that they need to be defined as weak so that the driver can replace the definitions with the real interrupt routines. However, things don’t seem to line up with the reference manual.

from core_hal.c

79 [x] CAN2_TX_IRQHandler                // CAN2 TX
80 [x] CAN2_RX0_IRQHandler               // CAN2 RX0
81 [x] CAN2_RX1_IRQHandler               // CAN2 RX1
82 [x] CAN2_SCE_IRQHandler               // CAN2 SCE

from the STM32F2xx Reference Manual.

should they be added to the list below (from stm32_it.c):

//HAL Interrupt Handlers defined in xxx_hal.c files
void HAL_EXTI_Handler(uint8_t EXTI_Line) __attribute__ ((weak));
void HAL_SysTick_Handler(void) __attribute__ ((weak));
void HAL_RTC_Handler(void) __attribute__ ((weak));
void HAL_RTCAlarm_Handler(void) __attribute__ ((weak));
void HAL_I2C1_EV_Handler(void) __attribute__ ((weak));
void HAL_I2C1_ER_Handler(void) __attribute__ ((weak));
void HAL_SPI1_Handler(void) __attribute__ ((weak));
void HAL_USART1_Handler(void) __attribute__ ((weak));
void HAL_USART2_Handler(void) __attribute__ ((weak));

I am still looking for someone to look over my CANbus driver architecture. The code is located here

The driver will not work yet. I still have to get more wrapped up on the interrupts. But it might initialize the CAN correctly. I don’t know… LOL

Don’t give up on this. If I ever work out how to do what you’re doing, I’d help. But I will certainly help testing it with real can bus devices.

@Bspranger I’ve looked over your approach and at a high level it looks great! (I don’t have a lot of time to completely scrutinize it though and the devil is in the details). If it’s compiling as is, you’re doing good. If you need any special GPIO configuration an edit to the gpio_hal.c’s HAL_Pin_Mode() may be necessary. For the interrupts… the interrupt vector table for the Photon has been named by the WICED SDK, as it has configured the startup file in C. The names are slightly different than the default STM32 startup file done in assembly. Here’s a listing of the function names:

 *               Function Declarations
 ******************************************************/

extern void NMIException           ( void );  // Non Maskable Interrupt
extern void HardFaultException     ( void );  // Hard Fault interrupt
extern void MemManageException     ( void );  // Memory Management Fault interrupt
extern void BusFaultException      ( void );  // Bus Fault interrupt
extern void UsageFaultException    ( void );  // Usage Fault interrupt
extern void SVC_irq                ( void );  // SVC interrupt
extern void DebugMonitor           ( void );  // Debug Monitor interrupt
extern void PENDSV_irq             ( void );  // PendSV interrupt
extern void SYSTICK_irq            ( void );  // Sys Tick Interrupt
extern void WWDG_irq               ( void );  // Window WatchDog
extern void PVD_irq                ( void );  // PVD through EXTI Line detection
extern void TAMP_STAMP_irq         ( void );  // Tamper and TimeStamps through the EXTI line
extern void RTC_WKUP_irq           ( void );  // RTC Wakeup through the EXTI line
extern void FLASH_irq              ( void );  // FLASH
extern void RCC_irq                ( void );  // RCC
extern void EXTI0_irq              ( void );  // EXTI Line0
extern void EXTI1_irq              ( void );  // EXTI Line1
extern void EXTI2_irq              ( void );  // EXTI Line2
extern void EXTI3_irq              ( void );  // EXTI Line3
extern void EXTI4_irq              ( void );  // EXTI Line4
extern void DMA1_Stream0_irq       ( void );  // DMA1 Stream 0
extern void DMA1_Stream1_irq       ( void );  // DMA1 Stream 1
extern void DMA1_Stream2_irq       ( void );  // DMA1 Stream 2
extern void DMA1_Stream3_irq       ( void );  // DMA1 Stream 3
extern void DMA1_Stream4_irq       ( void );  // DMA1 Stream 4
extern void DMA1_Stream5_irq       ( void );  // DMA1 Stream 5
extern void DMA1_Stream6_irq       ( void );  // DMA1 Stream 6
extern void ADC_irq                ( void );  // ADC1, ADC2 and ADC3s
extern void CAN1_TX_irq            ( void );  // CAN1 TX
extern void CAN1_RX0_irq           ( void );  // CAN1 RX0
extern void CAN1_RX1_irq           ( void );  // CAN1 RX1
extern void CAN1_SCE_irq           ( void );  // CAN1 SCE
extern void EXTI9_5_irq            ( void );  // External Line[9:5]s
extern void TIM1_BRK_TIM9_irq      ( void );  // TIM1 Break and TIM9
extern void TIM1_UP_TIM10_irq      ( void );  // TIM1 Update and TIM10
extern void TIM1_TRG_COM_TIM11_irq ( void );  // TIM1 Trigger and Commutation and TIM11
extern void TIM1_CC_irq            ( void );  // TIM1 Capture Compare
extern void TIM2_irq               ( void );  // TIM2
extern void TIM3_irq               ( void );  // TIM3
extern void TIM4_irq               ( void );  // TIM4
extern void I2C1_EV_irq            ( void );  // I2C1 Event
extern void I2C1_ER_irq            ( void );  // I2C1 Error
extern void I2C2_EV_irq            ( void );  // I2C2 Event
extern void I2C2_ER_irq            ( void );  // I2C2 Error
extern void SPI1_irq               ( void );  // SPI1
extern void SPI2_irq               ( void );  // SPI2
extern void USART1_irq             ( void );  // USART1
extern void USART2_irq             ( void );  // USART2
extern void USART3_irq             ( void );  // USART3
extern void EXTI15_10_irq          ( void );  // External Line[15:10]s
extern void RTC_Alarm_irq          ( void );  // RTC Alarm (A and B) through EXTI Line
extern void OTG_FS_WKUP_irq        ( void );  // USB OTG FS Wakeup through EXTI line
extern void TIM8_BRK_TIM12_irq     ( void );  // TIM8 Break and TIM12
extern void TIM8_UP_TIM13_irq      ( void );  // TIM8 Update and TIM13
extern void TIM8_TRG_COM_TIM14_irq ( void );  // TIM8 Trigger and Commutation and TIM14
extern void TIM8_CC_irq            ( void );  // TIM8 Capture Compare
extern void DMA1_Stream7_irq       ( void );  // DMA1 Stream7
extern void FSMC_irq               ( void );  // FSMC
extern void SDIO_irq               ( void );  // SDIO
extern void TIM5_irq               ( void );  // TIM5
extern void SPI3_irq               ( void );  // SPI3
extern void UART4_irq              ( void );  // UART4
extern void UART5_irq              ( void );  // UART5
extern void TIM6_DAC_irq           ( void );  // TIM6 and DAC1&2 underrun errors
extern void TIM7_irq               ( void );  // TIM7
extern void DMA2_Stream0_irq       ( void );  // DMA2 Stream 0
extern void DMA2_Stream1_irq       ( void );  // DMA2 Stream 1
extern void DMA2_Stream2_irq       ( void );  // DMA2 Stream 2
extern void DMA2_Stream3_irq       ( void );  // DMA2 Stream 3
extern void DMA2_Stream4_irq       ( void );  // DMA2 Stream 4
extern void ETH_irq                ( void );  // Ethernet
extern void ETH_WKUP_irq           ( void );  // Ethernet Wakeup through EXTI line
extern void CAN2_TX_irq            ( void );  // CAN2 TX
extern void CAN2_RX0_irq           ( void );  // CAN2 RX0
extern void CAN2_RX1_irq           ( void );  // CAN2 RX1
extern void CAN2_SCE_irq           ( void );  // CAN2 SCE
extern void OTG_FS_irq             ( void );  // USB OTG FS
extern void DMA2_Stream5_irq       ( void );  // DMA2 Stream 5
extern void DMA2_Stream6_irq       ( void );  // DMA2 Stream 6
extern void DMA2_Stream7_irq       ( void );  // DMA2 Stream 7
extern void USART6_irq             ( void );  // USART6
extern void I2C3_EV_irq            ( void );  // I2C3 event
extern void I2C3_ER_irq            ( void );  // I2C3 error
extern void OTG_HS_EP1_OUT_irq     ( void );  // USB OTG HS End Point 1 Out
extern void OTG_HS_EP1_IN_irq      ( void );  // USB OTG HS End Point 1 In
extern void OTG_HS_WKUP_irq        ( void );  // USB OTG HS Wakeup through EXTI
extern void OTG_HS_irq             ( void );  // USB OTG HS
extern void DCMI_irq               ( void );  // DCMI
extern void CRYP_irq               ( void );  // CRYP crypto
extern void HASH_RNG_irq           ( void );  // Hash and Rng

If you use these names (recommended)… they should just work. You can include the handler right in your can_hal.c file.

Specifically: CAN2_TX_irq, CAN2_RX0_irq, CAN2_RX1_irq, and CAN2_SCE_irq

And you’ll need to rename this to match:

If you want to rename the handler, you need to override the function pointer here:

2 Likes

So I was building via netbeans and I am not sure it was actually compiling everything. So I started to compile via command prompts and I am getting an error.

src/photon/hal_dynalib_export.c
In file included from ./inc/hal_dynalib_concurrent.h:25:0,
             from src/photon/hal_dynalib_export.c:35:   ./inc/concurrent_hal.h:189:23: fatal error: bits/gthr.h: No such file or directory #include <bits/gthr.h>

I have modified hal_dynalib_export.c to add #include “can_hal.h”, but I don’t think this is causing the issue.

any suggestions?

I commented out the include for bits/gthr.h, and I was able to compile.

My can drivers are compiling via command line because I had a few errors that I was able to clean up.

I added in the interrupt handlers, they build but are currently empty. That will be the next step.

Sure takes a long time to implement a driver when you get 1-2 hours a week.

3 Likes

So… I might have the driver mostly implemented however I haven’t tried it on hardware yet, so there could be a bunch of testing yet.

I have a really dumb question and need help. Now that I have the kernel (my fork) compiling with the CAN driver, how do I actually build an application so that it uses my “new” kernel?

I have been building my applications via particle cli.

Can some genius point me in the right direction?

I’d love to help, but I’m not sure how to answer that. Maybe you could post your code to a github repo so we can take a look and advise on how to build an app around it?

@Bspranger I think what you are asking is how to compile locally with a test application that uses your “CAN” fork of the firmware repo…

Here’s the way to ensure a clean build for the Photon, with modular firmware (Photon in DFU mode):

~code/firmware/modules $ make clean all -s PLATFORM=photon COMPILE_LTO=n APP=tinker program-dfu PARTICLE_DEVELOP=1

You can replace APP=tinker with a folder of your choice sitting along side of

firmware/user/applications/tinker/

For example, add your files here:

firmware/user/applications/can/
firmware/user/applications/can/myCanApp.cpp (main application with setup() and loop() )
firmware/user/applications/can/myLibrary.cpp (optional)
firmware/user/applications/can/myLibrary.h (optional)

Make sure to include these things in your myCanApp.cpp (these are normally taken care of by the preprocessor in the Build IDE):

#include "application.h"

// include all prototypes for new functions

Build with:

~code/firmware/modules $ make clean all -s PLATFORM=photon COMPILE_LTO=n APP=can program-dfu PARTICLE_DEVELOP=1

To iterate more quickly you can build the system and user application as a monolithic firmware image that gets programmed in one shot. This speeds up compile and flashing times by a factor of 2. You will not be able to iterate on just the user application after programming as monolithic though.

Here’s the way to ensure a clean build for the Photon, with monolithic firmware (Photon in DFU mode):

~code/firmware/main $ make clean all -s PLATFORM=photon COMPILE_LTO=n APP=tinker program-dfu MODULAR=n PARTICLE_DEVELOP=1

@BDub these are the directions I was EXACTLY looking for! Thanks so much. Now I can actually try to get this to run on hardware.

by adding program-dfu, is this telling the build system to use the particle flash --usb command?

1 Like

It ends up being the same as that CLI command, but the low level event is that both methods invoke dfu-util with the correct memory addresses needed to place the system and user modules in the correct place in flash.

After you get the modular system firmware running on your Photon, you can iterated just the user application in modular form with this command:

~code/firmware/main $ make clean all -s PLATFORM=photon COMPILE_LTO=n APP=tinker program-dfu PARTICLE_DEVELOP=1

But I tend to avoid it because if I forget that I'm tweaking something in system firmware, the change won't be pulled in by just recompiling and flashing the user application. So either wait for a clean build of all modules from the modules directory, or build as monolithic from the main directory (as mentioned in the previous post) :smile:

@BDub when I used the command

make clean all -s PLATFORM=photon COMPILE_LTO=n APP=can program-dfu MODULAR=n PARTICLE_DEVELOP=1

I was getting linking errors. It turns out that both of the files below were in conflict.

firmware\hal\src\stm32f2xx\hal_dynalib_export.cpp
firmware\hal\src\photon\hal_dynalib_export.c

The error I got was:

 ../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_concurrent+0x0): multiple definition of `dynalib_hal_concurrent'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_concurrent+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_wlan+0x0): multiple definition of `dynalib_hal_wlan'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_wlan+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_usart+0x0): multiple definition of `dynalib_hal_usart'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_usart+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_spi+0x0): multiple definition of `dynalib_hal_spi'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_spi+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_socket+0x0): multiple definition of `dynalib_hal_socket'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_socket+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_peripherals+0x0): multiple definition of `dynalib_hal_peripherals'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_peripherals+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_ota+0x0): multiple definition of `dynalib_hal_ota'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_ota+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_i2c+0x0): multiple definition of `dynalib_hal_i2c'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_i2c+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_gpio+0x0): multiple definition of `dynalib_hal_gpio'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_gpio+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_core+0x0): multiple definition of `dynalib_hal_core'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal_core+0x0): first defined here
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal+0x0): multiple definition of `dynalib_hal'
../build/target/hal/platform-6n/\libhal.a(hal_dynalib_export.o):(.rodata.dynalib_hal+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
make: *** [../build/target/main/platform-6/can.elf] Error 1

I removed the file:

firmware\hal\src\photon\hal_dynalib_export.c

and then it built. So I am not sure if this was the correct thing to do but I get an executable…

I still haven’t had a chance to get this on hardware but I have now been able to compile an application with the kernel modifications. So one step closer, until I actually try it on the hardware… lol

1 Like

Interesting… you must have started working on this before modular for electron was implemented. The file firmware\hal\src\photon\hal_dynalib_export.c was moved to firmware\hal\src\stm32f2xx\hal_dynalib_export.c and later renamed to .cpp

I’m not sure why you would still have both files present, but something is not updating in your fork correctly. I would try to rebase your commits against the latest develop branch, but it might be easier to just manually edit a fresh clone of develop to ensure all of your code is there.

I had an hour to try running the CAN drivers on hardware.

It was transmitting messages, but the data was wrong. So I need to look into that. But to see a message is a huge step forward.

It did not appear to be receiving anything. So I think I need to look into the Rx interrupt.

2 Likes

I had another hour on hardware today but I didn’t make anymore progress. Still not receiving messages (I suspect the pin configuration for the rx, but it looks correct).

TX messages look wrong. I double checked myour code and didn’t see anything.

Need a bigger block of consecutive time and also need to get JTAG running so I can verify the registers.

I have used eclipse with the stm32f2xx along time ago (openocd ), what is everyone using here?