2025-01-27 11:54 AM
Hi,
I am learning the CAN communication based on register programming. For now, I dont have anything complicated on my mind, dont have any filters or queues set up for CAN packets, my goal is to start a simple data transfer via CAN between my STM32 and ESP32 acting just as a dummy echo transceiver. I wrote code aiming for 1 Mbps speed (its not crucial, but 1 mbps should not be a problem even on a breadboard), 11 bit identifier, 1 byte data length, no crc, I am using TJA1050 as transceivers for both ESP and STM. My CAN_Setup function is verified with the debugger, it does exactly what its asked for (in terms of bit manipulation). However, problems occur when I try to transmit a byte of data in the CAN_transmit function. Some actual data is generated on the TX line, but its completely wrong (thats not the main problem considering its only development phase.) I can not write any data to the TIR, TDTR and TDLR register (but the mailbox itself is free, its not in the pending state). When I run the program in debug mode, executing those lines does not change anything in the mentioned registers. Likely there could be more problems with my code, but for now thats one thing to hook on. Did my initial research, but could not find the solution, I never experienced something like that programming the registers earlier... Any idea why could the TX mailbox registers appear ,,frozen"? Thank you. If I forgot some crucial details please excuse me, I will provide it as soon as some discrepancies occur.
#include "main.h"
void Core_Clock_Setup (void){
RCC->CR |= RCC_CR_HSEON; //Set the clock source to external crystal/resonator (HSE)
while (!(RCC->CR & RCC_CR_HSEON)); //Wait until clock gets stable
RCC->APB1ENR |= RCC_APB1ENR_PWREN; //Enable power interface clock
PWR->CR1 &= ~(1U << 14);
PWR->CR1 &= ~(1U << 15); //Set internal voltage regulator to is reset value (scale 1)
FLASH->ACR &= ~FLASH_ACR_ARTEN; //Disable ART accelerator
FLASH->ACR &= ~FLASH_ACR_ARTRST; //Reset ART accelerator
FLASH->ACR |= FLASH_ACR_PRFTEN; //Enable prefetch
FLASH->ACR |= FLASH_ACR_LATENCY_6WS; //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)
//@ 25 MHz crystal, 200 MHz core clock configuration down below
RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4);
RCC->CFGR |= (0 << 4); //Core clock division by 1 (core clock is not devided)
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry
RCC->PLLCFGR |= RCC_PLLCFGR_PLLP_0; //PLLP Setting corresponding PLL prescalers (division by 2)
RCC->PLLCFGR &= ~((1 << 6) - 1);
RCC->PLLCFGR |= (16 & ((1 << 6) - 1)); //PLLM Setting corresponding PLL prescalers (division by 16)
RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
RCC->PLLCFGR |= (256 << 6); //PLLN Setting corresponding PLL prescalers ( multiplication by 256)
RCC->CR |= RCC_CR_PLLON; //Enable PLL
while (!(RCC->CR & RCC_CR_PLLRDY)); //Wait until PLL gets stable
RCC->CFGR |= RCC_CFGR_SW_PLL; //PLL is set to be core clock
//RCC->CFGR |= RCC_CFGR_SW_HSE; //HSE is set to be core clock
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}
void GPIO_Setup(void){
RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E
GPIOE->MODER |= (1 << 8); //Bit 9 is zero (by default), bit 8 is one, resulting in 01 combination which represents the digital output pin mode
GPIOE->OTYPER &= ~(1 << 4); //PE4 configured as push-pull
GPIOE->OSPEEDR |= (1 << 8);
GPIOE->OSPEEDR |= (1 << 9); //PE4 configuration for very high speed ( 1 1 )
}
void Timer_Setup(void){
//RCC->DCKCFGR1 &= ~(1 << 24); //TIMxCLK = 2xPCLKx
RCC->DCKCFGR1 |= (1 << 24); //TIMxCLK = 4xPCLKx
RCC->APB1ENR |= (1 << 4); //Enable Timer 6 clock
TIM6->PSC = 99; //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1)
TIM6->ARR = 0xFFFF; //Auto reload max value of 16 bits (0xFFFF)
TIM6->CR1 |= (1 << 0); //Enable Timer 6 counter
while(!(TIM6->SR & (1<<0))); //Wait until timer update bit is set
}
void delay_us (uint16_t us){
TIM6->CNT = 0; //Reset counter
while (TIM6->CNT < us); //Wait until counter reaches desired value
}
void delay_ms (uint16_t ms){
for(uint16_t i = 0; i<ms; i++)
{
TIM6->CNT = 0; //Reset counter
while (TIM6->CNT < 1000); //Wait until counter reaches desired value
}
}
void CAN_Setup (void){
//PD0 CAN_RX_1
//PD1 CAN_TX_1
RCC->APB1ENR |= (1 << 25); //CAN 1 clock enabled, since its APB1 register, CAN clock is derived from peripheral clock 1 (50 MHz), goal is 1 Mbps speed
RCC->AHB1ENR |= (1 << 3); //Enable clock for GPIO bank D
delay_ms(1);
GPIOD->AFR[0] |= (0b1001 << 0); //PD0 set as alternate function AF9 (CAN_RX_1)
GPIOD->AFR[0] |= (0b1001 << 4); //PD1 set as alternate function AF9 (CAN_TX_1)
GPIOD->MODER |= (0b10 << 0); //PD0 set as alternate function
GPIOD->MODER |= (0b10 << 2); //PD1 set as alternate function
GPIOD->OSPEEDR |= (0b11 << 0); //PD0 very high GPIO speed
GPIOD->OSPEEDR |= (0b11 << 2); //PD1 very high GPIO speed
//------------------------------------------------------------------------------------------------------------------
CAN1->MCR &= ~ (1 << 1); //Exit sleep mode
CAN1->MCR |= (1 << 0); //Enter initialization mode
while (!(CAN1->MSR & (1 << 0))); //Wait until CAN enters the initialization mode
CAN1->MCR |= (1 << 2); //Priority driven by the request order (chronologically)
CAN1->MCR |= (1 << 4); //A message will be transmitted only once, independently of the transmission result (successful, error or arbitration lost)
CAN1->MCR &= ~ (1 << 16); //CAN works in debug mode
CAN1->BTR |= (0b0000000100 << 0); //Baud rate prescaler of 4
CAN1->BTR |= (0b0110 << 16); //Time segment 1 set to 6 time quanta
CAN1->BTR |= (0b011 << 20); //Time segment 2 set to 3 time quanta
CAN1->BTR &= ~ (1 << 24); //Resynchronization bits are cleared to produce time quanta of only one
CAN1->BTR &= ~ (1 << 25);
CAN1->MCR &= ~ (1 << 0); //Exit initialization mode
while (CAN1->MSR & (1 << 0)); //Wait until CAN exits initialization mode
}
void CAN_transmit (uint8_t data){
while (!(CAN1->TSR & (1 << 26))); //Wait until mailbox 0 is accessible
CAN1->sTxMailBox[0].TIR &= ~ (1 << 2); //Standard identifier selected
CAN1->sTxMailBox[0].TIR &= ~ (1 << 1); //Data frame
CAN1->sTxMailBox[0].TIR |= (0b10101010101 << 21); //11 identifier bits
CAN1->sTxMailBox[0].TDTR |= (0b0001 << 0); //Data frame is set to one byte
CAN1->sTxMailBox[0].TDLR |= (data << 0); //Writing the first and only data byte to TX register
CAN1->sTxMailBox[0].TIR |= (1 << 0); //Execute transmission
//while (!(CAN1->TSR & (1 << 1))); //Wait until transmission is successful, 1302 Non automatic retransmission, read if there are any problems
}
uint8_t CAN_receive (void){
uint8_t data;
CAN1->sFIFOMailBox[0].RIR &= ~ (1 << 1); //Data frame
CAN1->sFIFOMailBox[0].RIR &= ~ (1 << 2); //Standard identifier
CAN1->sFIFOMailBox[0].RIR |= (0b11001100110 << 21); //11 identifier bits
CAN1->sFIFOMailBox[0].RDTR |= (0b0001 << 0); //Receive data size is one byte
while (!((!(CAN1->RF0R & (1 << 1)))&(CAN1->RF0R & (1 << 0)))); //Wait until FMP bits become 0b01 in the receive register signaling valid message in mailbox 0
data = CAN1->sFIFOMailBox[0].RDLR & (0x000000FF); //Extract the lowest data byte
CAN1->RF0R |= (1 << 5); //Release output mailbox for new reception
return data;
}
int main (void){
Core_Clock_Setup();
Timer_Setup();
GPIO_Setup();
CAN_Setup();
while(1){
delay_ms(500);
CAN_transmit(0x55)
}
}
2025-01-27 11:50 PM
Hello,
I suggest to start an example using HAL at least to validate your hardware and also to inspire from the HAL implementation. After that you can move on with the direct access to the register implementation.