Nordic SDK printing to serial - c

I have an Arduino device that runs on the Nordic SDK (it's a Red Bear Lab BLE nano). I want to be able to do a serial print into GTKTerm to be able to debug what is going on in my code. To do this I have the following code:
/*
* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is confidential property of Nordic Semiconductor. The use,
* copying, transfer or disclosure of such information is prohibited except by express written
* agreement with Nordic Semiconductor.
*
*/
/**
* #brief BLE Heart Rate Collector application main file.
*
* This file contains the source code for a sample heart rate collector.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nordic_common.h"
#include "nrf_sdm.h"
#include "ble.h"
#include "ble_hci.h"
#include "ble_db_discovery.h"
#include "softdevice_handler.h"
#include "app_util.h"
#include "app_error.h"
#include "boards.h"
#include "nrf_gpio.h"
#include "pstorage.h"
#include "device_manager.h"
#include "app_trace.h"
#include "ble_hrs_c.h"
#include "ble_bas_c.h"
#include "app_util.h"
#include "app_timer.h"
#include "bsp.h"
#include "bsp_btn_ble.h"
#define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 1 /**< UART RX buffer size. */
#define STRING_BUFFER_LEN 50
#define BOND_DELETE_ALL_BUTTON_ID 0 /**< Button used for deleting all bonded centrals during startup. */
#define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */
#define APP_TIMER_MAX_TIMERS (2+BSP_APP_TIMERS_NUMBER) /**< Maximum number of simultaneously created timers. */
#define APP_TIMER_OP_QUEUE_SIZE 2 /**< Size of timer operation queues. */
#define APPL_LOG app_trace_log /**< Debug logger macro that will be used in this file to do logging of debug information over UART. */
#define SEC_PARAM_BOND 1 /**< Perform bonding. */
#define SEC_PARAM_MITM 1 /**< Man In The Middle protection not required. */
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /**< No I/O capabilities. */
#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */
#define SCAN_INTERVAL 0x00A0 /**< Determines scan interval in units of 0.625 millisecond. */
#define SCAN_WINDOW 0x0050 /**< Determines scan window in units of 0.625 millisecond. */
#define MIN_CONNECTION_INTERVAL MSEC_TO_UNITS(7.5, UNIT_1_25_MS) /**< Determines minimum connection interval in millisecond. */
#define MAX_CONNECTION_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /**< Determines maximum connection interval in millisecond. */
#define SLAVE_LATENCY 0 /**< Determines slave latency in counts of connection events. */
#define SUPERVISION_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Determines supervision time-out in units of 10 millisecond. */
#define TARGET_UUID 0x180D /**< Target device name that application is looking for. */
#define MAX_PEER_COUNT DEVICE_MANAGER_MAX_CONNECTIONS /**< Maximum number of peer's application intends to manage. */
#define UUID16_SIZE 2 /**< Size of 16 bit UUID */
/**#breif Macro to unpack 16bit unsigned UUID from octet stream. */
#define UUID16_EXTRACT(DST, SRC) \
do \
{ \
(*(DST)) = (SRC)[1]; \
(*(DST)) <<= 8; \
(*(DST)) |= (SRC)[0]; \
} while (0)
/**#brief Variable length data encapsulation in terms of length and pointer to data */
typedef struct
{
uint8_t * p_data; /**< Pointer to data. */
uint16_t data_len; /**< Length of data. */
}data_t;
typedef enum
{
BLE_NO_SCAN, /**< No advertising running. */
BLE_WHITELIST_SCAN, /**< Advertising with whitelist. */
BLE_FAST_SCAN, /**< Fast advertising running. */
} ble_scan_mode_t;
static ble_db_discovery_t m_ble_db_discovery; /**< Structure used to identify the DB Discovery module. */
static ble_hrs_c_t m_ble_hrs_c; /**< Structure used to identify the heart rate client module. */
static ble_bas_c_t m_ble_bas_c; /**< Structure used to identify the Battery Service client module. */
static ble_gap_scan_params_t m_scan_param; /**< Scan parameters requested for scanning and connection. */
static dm_application_instance_t m_dm_app_id; /**< Application identifier. */
static dm_handle_t m_dm_device_handle; /**< Device Identifier identifier. */
static uint8_t m_peer_count = 0; /**< Number of peer's connected. */
static ble_scan_mode_t m_scan_mode = BLE_FAST_SCAN; /**< Scan mode used by application. */
static uint16_t m_conn_handle; /**< Current connection handle. */
static volatile bool m_whitelist_temporarily_disabled = false; /**< True if whitelist has been temporarily disabled. */
static bool m_memory_access_in_progress = false; /**< Flag to keep track of ongoing operations on persistent memory. */
/**
* #brief Connection parameters requested for connection.
*/
static const ble_gap_conn_params_t m_connection_param =
{
(uint16_t)MIN_CONNECTION_INTERVAL, // Minimum connection
(uint16_t)MAX_CONNECTION_INTERVAL, // Maximum connection
0, // Slave latency
(uint16_t)SUPERVISION_TIMEOUT // Supervision time-out
};
static void scan_start(void);
#define APPL_LOG app_trace_log /**< Debug logger macro that will be used in this file to do logging of debug information over UART. */
/**#brief Function for initializing the UART.
*/
static void uart_init(void)
{
uint32_t err_code;
const app_uart_comm_params_t comm_params =
{
RX_PIN_NUMBER,
TX_PIN_NUMBER,
RTS_PIN_NUMBER,
CTS_PIN_NUMBER,
APP_UART_FLOW_CONTROL_ENABLED,
false,
UART_BAUDRATE_BAUDRATE_Baud38400
};
APP_UART_FIFO_INIT(&comm_params,
UART_RX_BUF_SIZE,
UART_TX_BUF_SIZE,
uart_error_handle,
APP_IRQ_PRIORITY_LOW,
err_code);
APP_ERROR_CHECK(err_code);
app_trace_init();
}
/** #brief Function for the Power manager.
*/
static void power_manage(void)
{
uint32_t err_code = sd_app_evt_wait();
APP_ERROR_CHECK(err_code);
}
int main(void)
{
bool erase_bonds;
// Initialize.
uart_init();
printf("Heart rate collector example (this is a custom log)\r\n");
for (;; )
{
power_manage();
}
}
The problem I am having is that only sometimes I will see an output in GTKterm. I am unable to find a pattern for when it works and when it doesn't. How would I go about debugging this?

How would I go about debugging this?
Some suggestions for starters:
Ensure that your terminal software is asserting the DTR signal. That was the solution found here.
Temporarily remove the call to power_manage() to ensure that is not part of the problem.
Change APP_UART_FLOW_CONTROL_ENABLED for APP_UART_FLOW_CONTROL_DISABLED to determine whether it is a flow control issue. You will not need flow control for output to a PC in any case. It may be needed if you are inputting to the device (especially with a buffer length of 1) or if you are sending to data to a slow device with limited buffering.
Verify ERR_CODE after calling APP_UART_FIFO_INIT to ensure no problems occurred at that stage. Possible error codes are defined here.

Related

Cannot run a large structure filled with arrays win C

I'm building a SAE J1939 library for embedded systems such as Arduino, STM32, AVR etc.
But I have an issue I don't understand. First when I compile. No error!
But when I run, I get this assembler error. It's all about the J1939 struct.
Why does this happen? Is the heap to small?
Can't find a source file at
"C:\mingw810\i686-810-posix-dwarf-rt_v6-rev0\build\gcc-8.1.0\i686-w64-mingw32\libgcc/../../../../../src/gcc-8.1.0/libgcc/config/i386/cygwin.S"
Locate the file or edit the source lookup path to include its
location.
Run this code below to reproduce the error:
#include <stdio.h>
#include <stdlib.h>
#include "stdint.h"
/* PGN: 0x00E800 - Storing the Acknowledgement from the reading process */
struct Acknowledgement {
uint8_t control_byte; /* This indicates the status of the requested information about PGN: */
uint8_t group_function_value; /* The function code that specify cause of the control byte e.g time out or aborted */
uint8_t address; /* Address from the ECU where the acknowledgement is comming from */
uint32_t PGN_of_requested_info; /* Information request about the PGN */
};
/* PGN: 0x00EC00 - Storing the Transport Protocol Connection Management from the reading process */
struct TP_CM {
uint8_t control_byte; /* What type of message are we going to send */
uint16_t total_message_size; /* Total bytes our complete message includes */
uint8_t number_of_packages; /* How many times we are going to send packages via TP_DT */
uint32_t PGN_of_the_packeted_message; /* Our message is going to activate a PGN */
};
/* PGN: 0x00EB00 - Storing the Transport Protocol Data Transfer from the reading process */
struct TP_DT {
uint8_t sequence_number; /* When this sequence number is the same as number_of_packages from TP_CM, then we have our complete message */
uint8_t data[7][256]; /* Package data of 2D array where first index is data0 -> data6 and second index is sequence of the data */
};
/* PGN: 0x00EE00 - Storing the Address claimed from the reading process */
struct Name {
uint32_t identity_number; /* Specify the ECU serial ID - 0 to 2097151 */
uint16_t manufacturer_code; /* Specify the ECU manufacturer code - 0 to 2047 */
uint8_t function_instance; /* Specify the ECU function number - 0 to 31 */
uint8_t ECU_instance; /* Specify the ECU number - 0 to 7 */
uint8_t function; /* Specify the ECU function - 0 to 255 */
uint8_t vehicle_system; /* Specify the type of vehicle where ECU is located - 0 to 127 */
uint8_t arbitrary_address_capable; /* Specify if the ECU have right to change address if addresses conflicts - 0 to 1 */
uint8_t industry_group; /* Specify the group where this ECU is located - 0 to 7 */
uint8_t vehicle_system_instance; /* Specify the vehicle system number - 0 to 15 */
};
/* PGN: 0x00FECA - Storing the DM1 Active diagnostic trouble codes from the reading process */
struct DM1 {
/* These are SAE lamps can have 1 = ON and 0 = OFF */
uint8_t SAE_lamp_status_malfunction_indicator;
uint8_t SAE_lamp_status_red_stop;
uint8_t SAE_lamp_status_amber_warning;
uint8_t SAE_lamp_status_protect_lamp;
uint8_t SAE_flash_lamp_malfunction_indicator;
uint8_t SAE_flash_lamp_red_stop;
uint8_t SAE_flash_lamp_amber_warning;
uint8_t SAE_flash_lamp_protect_lamp;
/* Fault location, problem and codes */
uint32_t SPN; /* Location where the fault exist */
uint8_t FMI; /* Type of problem */
uint8_t SPN_conversion_method; /* If SPN_conversion_method = 1 that means Diagnostics Trouble Code are aligned using a newer conversion method. If SPN_conversion_method = 0 means one of the three Diagnostics Trouble Code conversion methods is used and ECU manufacture shall know which of the three methods is used */
uint8_t occurence_count; /* This tells how many times failure has occurred. Every time fault goes from inactive to active, the occurence_count is incremented by 1. If fault becomes active for more than 126 times the occurence_count remains 126 */
};
/* PGN: 0x00D800 - Storing the DM15 response from the reading process */
struct DM15 {
uint16_t number_of_allowed_bytes; /* How many bytes we are allowed to write or read to */
uint8_t status; /* Status of the response */
uint32_t EDC_parameter; /* Status code */
uint8_t EDCP_extention; /* Describe how we should interpret the EDC parameter as a status code or error code */
uint16_t seed; /* Response of the key if we need more key or no key at all */
};
/* PGN: 0x00D700 - Storing the DM16 binary data transfer from the reading process */
struct DM16 {
uint8_t number_of_occurences; /* How many bytes we have sent */
uint8_t raw_binary_data[256]; /* Here we store the bytes */
};
/* Storing the error codes from the reading process */
struct DM {
uint8_t errors_dm1_active; /* How many errors of DM1 we have right now */
uint8_t errors_dm2_active; /* How many errors of DM2 is active */
struct DM1 dm1[256]; /* dm1 can contains multiple error messages */
struct DM1 dm2[256]; /* dm2 contains previously active errors from dm1 */
struct DM15 dm15; /* dm15 is the memory access response from DM14 memory request */
struct DM16 dm16; /* dm16 is the binary data transfer after DM15 memory response (if it was proceeded) */
/* Add more DM here */
};
/* PGN: 0x00FEDA - Storing the software identification from the reading process */
struct Software_identification {
uint8_t length_of_each_identification; /* The length of each identification - Not part of J1939 standard */
uint8_t number_of_fields; /* How many numbers contains in the identifications array */
uint8_t identifications[256]; /* This can be for example ASCII */
};
/* PGN: 0x00FDC5 - Storing the ECU identification from the reading process */
struct ECU_identification {
uint8_t length_of_each_field; /* The real length of the fields - Not part of J1939 standard */
uint8_t ecu_part_number[256]; /* ASCII field */
uint8_t ecu_serial_number[256]; /* ASCII field */
uint8_t ecu_location[256]; /* ASCII field */
uint8_t ecu_type[256]; /* ASCII field */
uint8_t ecu_manufacturer[256]; /* ASCII field */
uint8_t ecu_hardware_version[256]; /* ASCII field */
};
/* PGN: 0x00FEEB - Storing the component identification from the reading process */
struct Component_identification {
uint8_t length_of_each_field; /* The real length of the fields - Not part of J1939 standard */
uint8_t component_product_date[256]; /* ASCII field */
uint8_t component_model_name[256]; /* ASCII field */
uint8_t component_serial_number[256]; /* ASCII field */
uint8_t component_unit_name[256]; /* ASCII field */
};
/* PGN: 0x00FE30 (65072) to 0x00FE3F (65087) */
struct Auxiliary_valve_command {
uint8_t standard_flow; /* Command flow */
uint8_t fail_safe_mode; /* If the user want the valve to go to neutral */
uint8_t valve_state; /* Retract, Extend, Neutral, Init, Error etc */
};
/* PGN: 0x00FE10 (65040) to 0x00FE1F (65055) */
struct Auxiliary_valve_estimated_flow {
uint8_t extend_estimated_flow_standard; /* A measurement */
uint8_t retract_estimated_flow_standard; /* A measurement */
uint8_t valve_state; /* Retract, Extend, Neutral, Init, Error etc */
uint8_t fail_safe_mode; /* The mode if we are going to use fail safe mode or not */
uint8_t limit; /* Enter a limit code */
};
/* PGN: 0x00C400 (50176) */
struct General_purpose_valve_command {
uint8_t standard_flow; /* Command flow */
uint8_t fail_safe_mode; /* If the user want the valve to go to neutral */
uint8_t valve_state; /* Retract, Extend, Neutral, Init, Error etc */
uint16_t extended_flow; /* Another command flow */
};
/* PGN: 0x00C600 (50688) */
struct General_purpose_valve_estimated_flow {
uint8_t extend_estimated_flow_standard; /* A measurement */
uint8_t retract_estimated_flow_standard; /* A measurement */
uint8_t valve_state; /* Retract, Extend, Neutral, Init, Error etc */
uint8_t fail_safe_mode; /* The mode if we are going to use fail safe mode or not */
uint8_t limit; /* Enter a limit code */
uint16_t extend_estimated_flow_extended; /* A measurement */
uint16_t retract_estimated_flow_extended; /* A measurement */
};
struct Auxiliary_valve_measured_position {
uint16_t measured_position_procent; /* Procent position */
uint8_t valve_state; /* Retract, Extend, Neutral, Init, Error etc */
uint16_t measured_position_micrometer; /* Micrometer position */
};
typedef struct {
/* For information about other ECU */
uint8_t number_of_ECU;
uint8_t number_of_cannot_claim_address;
uint8_t ECU_address[256];
struct Acknowledgement acknowledgement[256];
struct TP_CM tp_cm[256];
struct TP_DT tp_dt[256];
struct Name name[256];
struct DM dm[256];
struct Software_identification software_identification[256];
struct ECU_identification ecu_identification[256];
struct Component_identification component_identification[256];
struct Auxiliary_valve_estimated_flow auxiliary_valve_estimated_flow[256][16];
struct General_purpose_valve_estimated_flow general_purpose_valve_estimated_flow[256];
struct Auxiliary_valve_measured_position auxiliary_valve_measured_position[256][16];
/* For information about this ECU */
struct Name this_name;
uint8_t this_ECU_address;
struct DM this_dm;
struct Software_identification this_software_identification;
struct ECU_identification this_ecu_identification;
struct Component_identification this_component_identification;
struct Auxiliary_valve_command this_auxiliary_valve_command[16];
struct General_purpose_valve_command this_general_purpose_valve_command;
} J1939;
int main(void) {
J1939 j1939;
puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */
return EXIT_SUCCESS;
}
This information is not a compilation error. You execute a debugger and try to debug a part of program without located sources. This could happen if your program stops in a compiled library. Then we can use a assembler instruction only. As mentioned above try to set breakpoint into your main() funcion.

Get USB Host Controller parameters in Linux programmatically

I need to get some parameters related to USB Host Controllers in Linux. I made a lot of searches in the internet and the only way that I found is using pciutils lib. Each PCI device is in the structure pci_dev in pci.h:
struct pci_dev {
struct pci_dev *next; /* Next device in the chain */
u16 domain_16; /* 16-bit version of the PCI domain for backward compatibility */
/* 0xffff if the real domain doesn't fit in 16 bits */
u8 bus, dev, func; /* Bus inside domain, device and function */
/* These fields are set by pci_fill_info() */
int known_fields; /* Set of info fields already known */
u16 vendor_id, device_id; /* Identity of the device */
u16 device_class; /* PCI device class */
int irq; /* IRQ number */
pciaddr_t base_addr[6]; /* Base addresses including flags in lower bits */
pciaddr_t size[6]; /* Region sizes */
pciaddr_t rom_base_addr; /* Expansion ROM base address */
pciaddr_t rom_size; /* Expansion ROM size */
struct pci_cap *first_cap; /* List of capabilities */
char *phy_slot; /* Physical slot */
char *module_alias; /* Linux kernel module alias */
char *label; /* Device name as exported by BIOS */
int numa_node; /* NUMA node */
pciaddr_t flags[6]; /* PCI_IORESOURCE_* flags for regions */
pciaddr_t rom_flags; /* PCI_IORESOURCE_* flags for expansion ROM */
int domain; /* PCI domain (host bridge) */
/* Fields used internally */
struct pci_access *access;
struct pci_methods *methods;
u8 *cache; /* Cached config registers */
int cache_len;
int hdrtype; /* Cached low 7 bits of header type, -1 if unknown */
void *aux; /* Auxiliary data */
struct pci_property *properties; /* A linked list of extra properties */
struct pci_cap *last_cap; /* Last capability in the list */
};
I found also that I can parse pci devices as following:
struct pci_access *pacc1;
struct pci_dev *dev1;
unsigned int c1;
char namebuf1[1024], *name1;
pacc1 = pci_alloc(); /* Get the pci_access structure */
/* Set all options you want -- here we stick with the defaults */
pci_init(pacc1); /* Initialize the PCI library */
pci_scan_bus(pacc1); /* We want to get the list of devices */
for (dev1=pacc1->devices; dev1; dev1=dev1->next) /* Iterate over all devices */
{
pci_fill_info(dev1, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); /* Fill in header info we need */
c1 = pci_read_byte(dev1, PCI_INTERRUPT_PIN); /* Read config register directly */
printf("%04x:%02x:%02x.%d vendor=%04x device=%04x class=%04x irq=%d (pin %d) base0=%lx",
dev1->domain, dev1->bus, dev1->dev, dev1->func, dev1->vendor_id, dev1->device_id,
dev1->device_class, dev1->irq, c1, (long) dev1->base_addr[0]);
/* Look up and print the full name of the device */
name1 = pci_lookup_name(pacc, namebuf1, sizeof(namebuf1), PCI_LOOKUP_DEVICE, dev1->vendor_id, dev1->device_id);
printf(" ***************** %d: (%s)\n", dev1->dev, name1);
}
pci_cleanup(pacc1); /* Close everything */
Is there a way to check in the loop if the corresponding pci device corresponds to an USB Host Controller? Or is there a simpler way to get USB Host Controller infos?

Call different CAN-controllers in one function?

My first question here. I´m no proffesional programmer at all. Just for fun at home so I don´t really know the right terminology for what I´m about to ask.
I want to create a CAN-bus gateway and I have the NXP DEVKIT-MPC5748G. All the CAN-busses are set-up and working for both tx and rx. Now I want to create a function for manipulating the different CAN-controllers. Theres 8 of them so I was hoping of not write 8 equal functions only having what CAN-controller to use differ.
You setup the controllers like this:
CAN_1.CTRL1.B.CLKSRC = 0;
Just an example for setting the clock source.
CAN_1 has a macro like this:
#define CAN_1 (*(volatile struct CAN_1_7_tag *) 0xFBEC0000UL)
In that struct there is a hole lot of unions for accessing all the registers. Now I want to write a function that I can pass a parameter to tell what CAN-controller to use. I can use a switch/case style way of doing it but that code will be long and ugly.
I want to do something like this:
void Tx_Msg("type???" gwport, int mb, uint32_t id) {
gwport.[mb].CS.B.CODE = 0x8; }
But I cant figure out how to do it. Can it be done?
Thankfull for all help in the right direction. :)
Best regards, Joakim
EDIT to clarify
CAN_1_7_tag struct:
struct CAN_1_7_tag {
CAN_MCR_tag MCR; /* Module Configuration Register */
CAN_CTRL1_tag CTRL1; /* Control 1 register */
CAN_TIMER_tag TIMER; /* Free Running Timer */
uint8_t CAN_reserved0[4];
CAN_RXMGMASK_tag RXMGMASK; /* Rx Mailboxes Global Mask Register */
CAN_RX14MASK_tag RX14MASK; /* Rx 14 Mask register */
CAN_RX15MASK_tag RX15MASK; /* Rx 15 Mask register */
CAN_ECR_tag ECR; /* Error Counter */
CAN_ESR1_tag ESR1; /* Error and Status 1 register */
CAN_IMASK2_tag IMASK2; /* Interrupt Masks 2 register */
CAN_IMASK1_tag IMASK1; /* Interrupt Masks 1 register */
CAN_IFLAG2_tag IFLAG2; /* Interrupt Flags 2 register */
CAN_IFLAG1_tag IFLAG1; /* Interrupt Flags 1 register */
CAN_CTRL2_tag CTRL2; /* Control 2 register */
CAN_ESR2_tag ESR2; /* Error and Status 2 register */
uint8_t CAN_reserved1[8];
CAN_CRCR_tag CRCR; /* CRC Register */
CAN_RXFGMASK_tag RXFGMASK; /* Rx FIFO Global Mask register */
CAN_RXFIR_tag RXFIR; /* Rx FIFO Information Register */
CAN_CBT_tag CBT; /* CAN Bit Timing Register */
uint8_t CAN_reserved2[24];
CAN_IMASK3_tag IMASK3; /* Interrupt Masks 3 Register */
uint8_t CAN_reserved3[4];
CAN_IFLAG3_tag IFLAG3; /* Interrupt Flags 3 Register */
uint8_t CAN_reserved4[8];
CAN_MB_tag MB[64];
uint8_t CAN_reserved5[1024];
CAN_RXIMR_tag RXIMR[96]; /* Rx Individual Mask Registers */
uint8_t CAN_reserved6[512];
CAN_FDCTRL_tag FDCTRL; /* CAN FD Control Register */
CAN_FDCBT_tag FDCBT; /* CAN FD Bit Timing Register */
CAN_FDCRC_tag FDCRC; /* CAN FD CRC Register */
};
Example for MCR register. All registers works the same way.
typedef union CAN_MCR_union_tag { /* Module Configuration Register */
vuint32_t R;
struct {
vuint32_t MDIS:1; /* Module Disable */
vuint32_t FRZ:1; /* Freeze Enable */
vuint32_t RFEN:1; /* Rx FIFO Enable */
vuint32_t HALT:1; /* Halt FlexCAN */
vuint32_t NOTRDY:1; /* FlexCAN Not Ready */
vuint32_t WAKMSK:1; /* Wake Up Interrupt Mask */
vuint32_t SOFTRST:1; /* Soft Reset */
vuint32_t FRZACK:1; /* Freeze Mode Acknowledge */
vuint32_t SUPV:1; /* Supervisor Mode */
vuint32_t SLFWAK:1; /* Self Wake Up */
vuint32_t WRNEN:1; /* Warning Interrupt Enable */
vuint32_t LPMACK:1; /* Low-Power Mode Acknowledge */
vuint32_t WAKSRC:1; /* Wake Up Source */
vuint32_t _unused_18:1;
vuint32_t SRXDIS:1; /* Self Reception Disable */
vuint32_t IRMQ:1; /* Individual Rx Masking And Queue Enable */
vuint32_t DMA:1; /* DMA Enable */
vuint32_t _unused_14:1;
vuint32_t LPRIOEN:1; /* Local Priority Enable */
vuint32_t AEN:1; /* Abort Enable */
vuint32_t FDEN:1; /* CAN FD operation enable */
vuint32_t _unused_10:1;
vuint32_t IDAM:2; /* ID Acceptance Mode */
vuint32_t _unused_7:1;
vuint32_t MAXMB:7; /* Number Of The Last Message Buffer */
} B;
} CAN_MCR_tag;
Hope this is what you asked for.
If CAN_1 is define as:
#define CAN_1 (*(volatile struct CAN_1_7_tag *) 0xFBEC0000UL)
Then CAN_1 is a structure of type CAN_1_7_tag which is located at 0xFBEC0000UL.
The volatile qualifier is here to indicate to the compiler that is should not optimize anything relatives to the CAN_1 as it might be changed by other threads.
You can pass the CAN-controller as a pointer:
void Tx_Msg(volatile struct CAN_1_7_tag *p_gwport, int mb, uint32_t id)
{
p_gwport->CTRL1.B.CLKSRC = 0;
p_gwport->MB[mb].CS.B.CODE = 0x8;
}
Then when calling this function to send a message from a specific CAN-controller, you can use:
Tx_Msg(&CAN_1, 12, 25);
Tx_Msg(&CAN_4, 21, 45);
Thanks one more time for the help. The CAN gateway is up and running as a prototype.
This is my "final" code.
/********************************************************************************/
/* Tx function for FlexCAN 1-7 */
/********************************************************************************/
void Tx_Msg_1_7(volatile struct CAN_1_7_tag *port, uint32_t mb, uint32_t dlc, uint32_t id, uint8_t txData[])
{
int i = 0; // Used in for loops
port->MB[mb].CS.B.CODE = 0x8; // MB TX inactive
port->MB[mb].CS.B.DLC = dlc; // Message length max 8 bytes
port->MB[mb].CS.B.RTR = 0; // Remote frame disable
port->MB[mb].CS.B.SRR = 1; // Not used with standard id
if (id > 0x7FF) // CAN id 29 bits
{
port->MB[mb].CS.B.IDE = 1; // EXT CAN id
port->MB[mb].ID.B.ID_STD = id >> 18 & 0x7FF; // CAN id (11 bits)
port->MB[mb].ID.B.ID_EXT = id & 0x3FFFF; // CAN id (18 bits)
}
else // CAN id 11 bits
{
port->MB[mb].CS.B.IDE = 0; // STD CAN id
port->MB[mb].ID.B.ID_STD = id; // CAN id (11 bits)
port->MB[mb].ID.B.ID_EXT = 0; // CAN id (18 bits), always 0
}
for(i = 0; i < dlc; i++)
{
port->MB[mb].DATA.B[i] = txData[i];
}
port->MB[mb].CS.B.CODE = 0xC; // MB once transmit data
}

Converting from C++ language to C (from class to struct) to use HDC1080 sensor with ATTiny841

Today I'm trying to convert this library: Arduino library for ClosedCube_HDC1080 Humidity and Temperature sensor to use it with an ATTiny841 ATTiny841 datasheet, to do it I'm going to use the Fleury's Library to use the ATTiny841 as Master and the HDC1080 as Slave.
My problem is that I have some issues to convert from the C++ language that is used in the HDC1080 library for Arduino to the ATTiny, which use C language, for that I would show you header files to explain myself.
HDC1080_Registers;
class ClosedCube_HDC1080 {
public:
ClosedCube_HDC1080();
void begin(uint8_t address);
uint16_t readManufacturerId(); // 0x5449 ID of Texas Instruments
uint16_t readDeviceId(); // 0x1050 ID of the device
HDC1080_Registers readRegister();
void writeRegister(HDC1080_Registers reg);
void heatUp(uint8_t seconds);
float readTemperature();
float readHumidity();
float readT(); // short-cut for readTemperature
float readH(); // short-cut for readHumidity
private:
uint8_t _address;
uint16_t readData(uint8_t pointer);
};
#endif
This is from the header (.h) file of HDC1080 sensor, I read that class function is not available in C language, so I decide to use struct function, which I saw that is available in C language, but in examples that I saw on Internet, they only use struct function to declare variables, and functions not like:
ClosedCube_HDC1080();
void begin(uint8_t address);
HDC1080_Registers readRegister();
void writeRegister(HDC1080_Registers reg);
void heatUp(uint8_t seconds);
They are define it outside of the struct function, so I get confused with those void's which are inside of the class function, this is reason why I looking for some help. I just need to know:
How to declare those functions to work with the struct
Is going to be much easier to define them later in the C (.c) file?
Thanks for your time and your patience, is my first time converting from one language to another.
Because the only internal state in that class is a single uint8_t, and this code is run on a microcontroller, it is much better to just supply that address as a parameter to each function.
First, note that my suggestions below are derivative of the ClosedCube HDC1080 Arduino library, and therefore licensed under the same license:
/*
Arduino Library for Texas Instruments HDC1080 Digital Humidity and Temperature Sensor
Originally written by AA for ClosedCube; this suggested conversion by Nominal Animal
---
The MIT License (MIT)
Copyright (c) 2016-2017 ClosedCube Limited
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
ATtinys are small 8-bit microcontrollers, so it's best to avoid using the float type. Instead, I suggest you use units of 1/1000 degrees Celsius for temperature (25000 referring to 25 degrees Celsius), and 1/100000 for humidity (50000 referring to 50% of relative humidity).
The interface to the adapted library can be quite simple:
void HDC1080_begin(const uint8_t address);
void HDC1080_heater(const uint8_t address, const uint8_t seconds);
uint16_t HDC1080_manufacturer(const uint8_t address);
uint16_t HDC1080_device_id(const uint8_t address);
int32_t HDC1080_temperature(const uint8_t address);
uint32_t HDC1080_humidity(const uint8_t address);
If we assume we keep temperature and humidity precision at 14 bits (configured by HDC1080_begin(), we don't need the user to set or read the configuration register.
The example code shows that HDC1080_manufacturer() should return 0x5449, and HDC1080_device_id() should return 0x1050.
Since we don't know what kind of interface the "Fleury's Library" has, I shall show the implementation of the above functions with comments (starting with /* I2C:) instead of actual library calls.
The static ones are internal functions only used by the abovementioned functions, not "user" code.
#define HDC1080_TEMPERATURE ((uint8_t)0x00)
#define HDC1080_HUMIDITY ((uint8_t)0x01)
#define HDC1080_CONFIGURATION ((uint8_t)0x02)
#define HDC1080_SERIAL_ID_FIRST ((uint8_t)0xFB)
#define HDC1080_SERIAL_ID_MID ((uint8_t)0xFC)
#define HDC1080_SERIAL_ID_LAST ((uint8_t)0xFD)
#define HDC1080_MANUFACTURER_ID ((uint8_t)0xFE)
#define HDC1080_DEVICE_ID ((uint8_t)0xFF)
/* Bits 15..8 of the HDC1080 configuration.
Heater disabled, 14 bit precision for
both temperature and relative humidity.
See page 15 of
http://www.ti.com/lit/ds/symlink/hdc1080.pdf
for other possibilities.
*/
#define HDC1080_CONFIG_HI ((uint8_t)0)
static uint16_t HDC1080_read(const uint8_t address, const uint8_t item)
{
uint8_t hi, lo;
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits 'item'. */
/* I2C: End transmission. */
/* Delay for 9 ms (0.009 seconds). */
/* I2C: Prepare to read 2 bytes from i2c address 'address'. */
hi = /* I2C: Read byte. */
lo = /* I2C: Read byte. */
return (((uint16_t)hi) << 8) | lo;
}
void HDC1080_begin(const uint8_t address)
{
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits: HDC1080_CONFIGURATION (=0x02). */
/* I2C: Send 8 bits: HDC1080_CONFIG_HI (=0x00). */
/* I2C: Send 8 bits: 0. */
/* I2C: End transmission. */
/* Delay for 10 ms = 0.01 seconds. */
}
void HDC1080_heater(const uint8_t address, const uint8_t seconds)
{
uint8_t s, i;
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits: HDC1080_CONFIGURATION (=0x02). */
/* I2C: Send 8 bits: 48 (=0x30). */
/* I2C: Send 8 bits: 0. */
/* I2C: End transmission. */
/* Delay for 10 ms = 0.01 seconds. */
for (s = 0; s < seconds; s++) {
for (i = 0; i < 66; i++) {
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits 'item'. */
/* I2C: End transmission. */
/* Delay for 20 ms (0.020 seconds). */
/* I2C: Prepare to read 4 bytes from i2c address 'address'. */
/* I2C: Read byte. (Ignore the value.) */
/* I2C: Read byte. (Ignore the value.) */
/* I2C: Read byte. (Ignore the value.) */
/* I2C: Read byte. (Ignore the value.) */
}
}
/* I2C: Prepare to send to i2c address 'address'. */
/* I2C: Send 8 bits: HDC1080_CONFIGURATION (=0x02). */
/* I2C: Send 8 bits: HDC1080_CONFIG_HI (=0x00). */
/* I2C: Send 8 bits: 0. */
/* I2C: End transmission. */
/* Delay for 10 ms = 0.01 seconds. */
}
uint16_t HDC1080_manufacturer(const uint8_t address)
{
return HDC1080_read(address, HDC1080_MANUFACTURER_ID);
}
uint16_t HDC1080_device_id(const uint8_t address)
{
return HDC1080_read(address, HDC1080_DEVICE_ID);
}
int32_t HDC1080_temperature(const uint8_t address)
{
uint16_t temp1 = HDC1080_read(address, HDC1080_TEMPERATURE) >> 2;
return (int_t)(((uint32_t)20625 * temp1 + (uint32_t)1024) >> 11) - (int32_t)40000;
}
uint32_t HDC1080_humidity(const uint8_t address)
{
uint16_t temp1 = HDC1080_read(address, HDC1080_HUMIDITY);
return ((uint32_t)3125 * temp1 + (uint32_t)1024) >> 11;
}
Note that the relative humidity range is 0 to 99998, i.e. 0%RH to 99.998% RH. This is not an error, and the formula is taken from the datasheet and losslessly scaled to the new range. Similarly, the possible temperature range is -40000 to +124989, corresponding to -40°C to +124.989°C, and this too is according to the TI datasheet, losslessly scaled to the new range. The + (uint32_t)1024 term in both ensures correct rounding (to nearest).
You do still need to implementing the /* I2C: and /* Delay comments using whatever library you have at hand.

GameBoy compiler with system registers and interrupts

I have been spending a lot of time learning GameBoy programming, as I was already familiar with Z80 Assembly I wasn't afraid of jumping into using it. I would (of course) find it much more productive to program in C or C++ however cannot find a full compiler for the GameBoy, the compilers I can find manage everything themselves and do not give access to system registers to the programmer and also have some horrible drawbacks such as 100% CPU utilization and no interrupt support.
Would it be possible to address system registers much like Arduino's AVR compiler? being able to address a CPU or system register simply with its name such as DDRD = %10101011
What would I have to do to add interrupts and system registers into a compiler? All but one system register are only one byte memory addresses and interrupts vectors are of course memory locations, the only one system register that isn't a memory address can only be modified with two Assembly instructions EI and DI but that could be inline functions correct?
The usual tactic is to create your own pointers to system registers. I don't know the address of DDRD, but something like this should to the trick:
volatile unsigned char *reg_DDRD = (unsigned char *)0xE000;
*reg_DDRD = 0xAB;
Most C compilers don't support binary constants but you can use them with some macro hackery. And you can use macros to make slightly more intuitive syntax:
#define DDRD (*reg_DDRD)
DDRD = 0xAB;
There's no sense in modifying the compiler when vanilla C code can work just as well.
Handling interrupts comes down to solving 3 problems. The first is to have the interrupt vector address do a jump to a C function. As that is in ROM you'll need to modify the C runtime environment to initialize that. This gets pretty system dependent, but usually what you'll want to do is add an assembly language file that looks like this:
org 38h ; or wherever the gameboy CPU jumps to on interrupt
jp _intr_function
This should cause the CPU to go to intr_function() in your C program. You may or may not need the leading underscore. And you may not be able to set the destination address in the assembler file with org but instead have to fool around with the linker and sections.
The second problem is that the C function won't necessarily save all the registers it should. You can do this by adding in-line assembly to it, along these lines:
void intr_function()
{
asm(" push af");
asm(" push bc");
asm(" push de");
asm(" push hl");
// ... now do what you like here.
asm(" pop hl");
asm(" pop de");
asm(" pop bc");
asm(" pop af");
}
Finally, the interrupt may have to be acknowledged by manipulating a hardware register. But you can do that in the C code so nothing special about that.
I'm not clear about the issue with wait loops. Standard C compilers don't have such a feature built in. They call main() and if you want to loop it is up to you. It is true that the C-like language used in the Arduino SDK has its own built-in infinite loop that calls the functions you write, but that's not normal C.
First off, you can use GBDK, which is a C compiler and library for the Gameboy. It does provide access to the registers in gb/hardware.h (but that isn't listed in the doc file, since there's no comment for each individual register). It also supplies access to interrupts via methods in gb/gb.h: add_VBL, add_LCD, add_TIM, add_SIO, and add_JOY. (There's also remove methods named remove_).
For reference and/or your own use, here's the contents of gb/hardware.h:
#define __REG volatile UINT8 *
#define P1_REG (*(__REG)0xFF00) /* Joystick: 1.1.P15.P14.P13.P12.P11.P10 */
#define SB_REG (*(__REG)0xFF01) /* Serial IO data buffer */
#define SC_REG (*(__REG)0xFF02) /* Serial IO control register */
#define DIV_REG (*(__REG)0xFF04) /* Divider register */
#define TIMA_REG (*(__REG)0xFF05) /* Timer counter */
#define TMA_REG (*(__REG)0xFF06) /* Timer modulo */
#define TAC_REG (*(__REG)0xFF07) /* Timer control */
#define IF_REG (*(__REG)0xFF0F) /* Interrupt flags: 0.0.0.JOY.SIO.TIM.LCD.VBL */
#define NR10_REG (*(__REG)0xFF10) /* Sound register */
#define NR11_REG (*(__REG)0xFF11) /* Sound register */
#define NR12_REG (*(__REG)0xFF12) /* Sound register */
#define NR13_REG (*(__REG)0xFF13) /* Sound register */
#define NR14_REG (*(__REG)0xFF14) /* Sound register */
#define NR21_REG (*(__REG)0xFF16) /* Sound register */
#define NR22_REG (*(__REG)0xFF17) /* Sound register */
#define NR23_REG (*(__REG)0xFF18) /* Sound register */
#define NR24_REG (*(__REG)0xFF19) /* Sound register */
#define NR30_REG (*(__REG)0xFF1A) /* Sound register */
#define NR31_REG (*(__REG)0xFF1B) /* Sound register */
#define NR32_REG (*(__REG)0xFF1C) /* Sound register */
#define NR33_REG (*(__REG)0xFF1D) /* Sound register */
#define NR34_REG (*(__REG)0xFF1E) /* Sound register */
#define NR41_REG (*(__REG)0xFF20) /* Sound register */
#define NR42_REG (*(__REG)0xFF21) /* Sound register */
#define NR43_REG (*(__REG)0xFF22) /* Sound register */
#define NR44_REG (*(__REG)0xFF23) /* Sound register */
#define NR50_REG (*(__REG)0xFF24) /* Sound register */
#define NR51_REG (*(__REG)0xFF25) /* Sound register */
#define NR52_REG (*(__REG)0xFF26) /* Sound register */
#define LCDC_REG (*(__REG)0xFF40) /* LCD control */
#define STAT_REG (*(__REG)0xFF41) /* LCD status */
#define SCY_REG (*(__REG)0xFF42) /* Scroll Y */
#define SCX_REG (*(__REG)0xFF43) /* Scroll X */
#define LY_REG (*(__REG)0xFF44) /* LCDC Y-coordinate */
#define LYC_REG (*(__REG)0xFF45) /* LY compare */
#define DMA_REG (*(__REG)0xFF46) /* DMA transfer */
#define BGP_REG (*(__REG)0xFF47) /* BG palette data */
#define OBP0_REG (*(__REG)0xFF48) /* OBJ palette 0 data */
#define OBP1_REG (*(__REG)0xFF49) /* OBJ palette 1 data */
#define WY_REG (*(__REG)0xFF4A) /* Window Y coordinate */
#define WX_REG (*(__REG)0xFF4B) /* Window X coordinate */
#define KEY1_REG (*(__REG)0xFF4D) /* CPU speed */
#define VBK_REG (*(__REG)0xFF4F) /* VRAM bank */
#define HDMA1_REG (*(__REG)0xFF51) /* DMA control 1 */
#define HDMA2_REG (*(__REG)0xFF52) /* DMA control 2 */
#define HDMA3_REG (*(__REG)0xFF53) /* DMA control 3 */
#define HDMA4_REG (*(__REG)0xFF54) /* DMA control 4 */
#define HDMA5_REG (*(__REG)0xFF55) /* DMA control 5 */
#define RP_REG (*(__REG)0xFF56) /* IR port */
#define BCPS_REG (*(__REG)0xFF68) /* BG color palette specification */
#define BCPD_REG (*(__REG)0xFF69) /* BG color palette data */
#define OCPS_REG (*(__REG)0xFF6A) /* OBJ color palette specification */
#define OCPD_REG (*(__REG)0xFF6B) /* OBJ color palette data */
#define SVBK_REG (*(__REG)0xFF70) /* WRAM bank */
#define IE_REG (*(__REG)0xFFFF) /* Interrupt enable */
These are done in the same way as George Phillips's answer, and thus can be used like normal variables.
The code that is used by GBDK to add and remove interrupts is found in libc\gb\crt0.s, but I don't know enough of assembly to include the relevant sections in this post.
I'm not sure about how to avoid the busy loop either.

Resources