I have a Nordic nRF52840DK board that needs to be connected to an LSM6DSL accelerometer. I am using the Zephyr RTOS and the sample code from Zephyr is shown below:
/*
* Copyright (c) 2018 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <device.h>
#include <drivers/sensor.h>
#include <stdio.h>
#include <sys/util.h>
static inline float out_ev(struct sensor_value *val)
{
return (val->val1 + (float)val->val2 / 1000000);
}
static int print_samples;
static int lsm6dsl_trig_cnt;
static struct sensor_value accel_x_out, accel_y_out, accel_z_out;
static struct sensor_value gyro_x_out, gyro_y_out, gyro_z_out;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
static struct sensor_value magn_x_out, magn_y_out, magn_z_out;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
static struct sensor_value press_out, temp_out;
#endif
#ifdef CONFIG_LSM6DSL_TRIGGER
static void lsm6dsl_trigger_handler(const struct device *dev,
struct sensor_trigger *trig)
{
static struct sensor_value accel_x, accel_y, accel_z;
static struct sensor_value gyro_x, gyro_y, gyro_z;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
static struct sensor_value magn_x, magn_y, magn_z;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
static struct sensor_value press, temp;
#endif
lsm6dsl_trig_cnt++;
sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_X, &accel_x);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Y, &accel_y);
sensor_channel_get(dev, SENSOR_CHAN_ACCEL_Z, &accel_z);
/* lsm6dsl gyro */
sensor_sample_fetch_chan(dev, SENSOR_CHAN_GYRO_XYZ);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_X, &gyro_x);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_Y, &gyro_y);
sensor_channel_get(dev, SENSOR_CHAN_GYRO_Z, &gyro_z);
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
/* lsm6dsl external magn */
sensor_sample_fetch_chan(dev, SENSOR_CHAN_MAGN_XYZ);
sensor_channel_get(dev, SENSOR_CHAN_MAGN_X, &magn_x);
sensor_channel_get(dev, SENSOR_CHAN_MAGN_Y, &magn_y);
sensor_channel_get(dev, SENSOR_CHAN_MAGN_Z, &magn_z);
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
/* lsm6dsl external press/temp */
sensor_sample_fetch_chan(dev, SENSOR_CHAN_PRESS);
sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press);
sensor_sample_fetch_chan(dev, SENSOR_CHAN_AMBIENT_TEMP);
sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
#endif
if (print_samples) {
print_samples = 0;
accel_x_out = accel_x;
accel_y_out = accel_y;
accel_z_out = accel_z;
gyro_x_out = gyro_x;
gyro_y_out = gyro_y;
gyro_z_out = gyro_z;
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
magn_x_out = magn_x;
magn_y_out = magn_y;
magn_z_out = magn_z;
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
press_out = press;
temp_out = temp;
#endif
}
}
#endif
void main(void)
{
int cnt = 0;
char out_str[64];
struct sensor_value odr_attr;
const struct device *lsm6dsl_dev = device_get_binding(DT_LABEL(DT_INST(0, st_lsm6dsl)));
if (lsm6dsl_dev == NULL) {
printk("Could not get LSM6DSL device\n");
return;
}
/* set accel/gyro sampling frequency to 104 Hz */
odr_attr.val1 = 104;
odr_attr.val2 = 0;
if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_ACCEL_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
printk("Cannot set sampling frequency for accelerometer.\n");
return;
}
if (sensor_attr_set(lsm6dsl_dev, SENSOR_CHAN_GYRO_XYZ,
SENSOR_ATTR_SAMPLING_FREQUENCY, &odr_attr) < 0) {
printk("Cannot set sampling frequency for gyro.\n");
return;
}
#ifdef CONFIG_LSM6DSL_TRIGGER
struct sensor_trigger trig;
trig.type = SENSOR_TRIG_DATA_READY;
trig.chan = SENSOR_CHAN_ACCEL_XYZ;
if (sensor_trigger_set(lsm6dsl_dev, &trig, lsm6dsl_trigger_handler) != 0) {
printk("Could not set sensor type and channel\n");
return;
}
#endif
if (sensor_sample_fetch(lsm6dsl_dev) < 0) {
printk("Sensor sample update error\n");
return;
}
while (1) {
/* Erase previous */
printk("\0033\014");
printf("LSM6DSL sensor samples:\n\n");
/* lsm6dsl accel */
sprintf(out_str, "accel x:%f ms/2 y:%f ms/2 z:%f ms/2",
out_ev(&accel_x_out),
out_ev(&accel_y_out),
out_ev(&accel_z_out));
printk("%s\n", out_str);
/* lsm6dsl gyro */
sprintf(out_str, "gyro x:%f dps y:%f dps z:%f dps",
out_ev(&gyro_x_out),
out_ev(&gyro_y_out),
out_ev(&gyro_z_out));
printk("%s\n", out_str);
#if defined(CONFIG_LSM6DSL_EXT0_LIS2MDL)
/* lsm6dsl external magn */
sprintf(out_str, "magn x:%f gauss y:%f gauss z:%f gauss",
out_ev(&magn_x_out),
out_ev(&magn_y_out),
out_ev(&magn_z_out));
printk("%s\n", out_str);
#endif
#if defined(CONFIG_LSM6DSL_EXT0_LPS22HB)
/* lsm6dsl external press/temp */
sprintf(out_str, "press: %f kPa - temp: %f deg",
out_ev(&press_out), out_ev(&temp_out));
printk("%s\n", out_str);
#endif
printk("loop:%d trig_cnt:%d\n\n", ++cnt, lsm6dsl_trig_cnt);
print_samples = 1;
k_sleep(K_MSEC(2000));
}
}
From my previous experience working with Arduino, you need to define the pins before the main function, but there isn't any definition on pins in this code example. So my question is: How does the board know which pins to use when connected to the sensor?
Thanks in advance!
In embedded C (bare metal and some RTOS), the developer is expected to know what is connected where and which registers to use to access the connected hardware by driving the right pins.
In the specific case of Zephyr (as well as Das U-Boot and Linux on some architectures), the system configuration (among other things pins, but not only) is described by a device-tree. It is a "a data structure for describing hardware", which allows the firmware code to become independent of the actual hardware.
The device-tree will describe many things, the most important being:
the device's memory map
the list of CPUs and their properties (frequency, caches, ...)
the peripherals, as well as their configuration
Zephyr does have a very good introduction to device-tree.
In the code you posted, Zephyr does retrieve the device information from the device-tree when executing the following line:
const struct device *lsm6dsl_dev = device_get_binding(DT_LABEL(DT_INST(0, st_lsm6dsl)));
Related
Short Version
I want to write a Linux Driver for a custom USB device. Before writing the driver I used libusb-1.0 to test the device. With the following function call, I could read out a uin16_t value from the device:
status = libusb_control_transfer(handle, /* Device Handle */
0x80, /* bRequestType */
0x10, /* bRequest */
value, /* wValue */
0x0, /* wIndex */
((uint8_t *) &value), /* data */
2, /* wLength */
100); /* timeout */
After this call, I got a new value in the value variable.
Now I want to accomplish the same call in my Driver. I have tried the following in the probe function of my USB driver:
status = usb_control_msg(data->udev, usb_rcvctrlpipe(data->udev, 0), 0x10, USB_DIR_IN, 0, 0, (u8*) &my_data, 2, 100);
All I get is the return value -11 and on my device I don't see anything.
The only thing I am doing before this call, is calling data->udev = interface_to_usbdev(intf); to get the USB device from my interface.
Does anyone know, if I am missing something or if I am doing something wrong?
Long version
I want to learn how to write USB Drivers in Linux. As a DUT for which I can write a driver, I choose a Raspberry Pi Pico and the dev_lowlevel USB example. I adapt the code a little bit, so I can use a control transfer with bRequest 0x10 and bRequestType 0x0 (USB_DIR_OUT) to turn the Pico's onboard LED on or off and a control transfer with bRequest 0x10 and bRequestType 0x80 (USB_DIR_IN) to read back the current value of the LED.
With a user space program and the following code I can read out the value of the LED and turn it on or off:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <libusb-1.0/libusb.h>
#define VID 0x0000
#define DID 0x0001
int main(int argc, char **argv) {
int status, len;
libusb_device_handle *handle = NULL;
/* Init Libusb */
status = libusb_init(NULL);
if(status < 0) {
printf("Error init USB!\n");
return status;
}
handle = libusb_open_device_with_vid_pid(NULL, VID, DID);
if(!handle) {
printf("No device found with %04x:%04x\n", VID, DID);
libusb_exit(NULL);
return -1;
}
if(argc > 1)
value = atoi(argv[1]);
else {
/* Do control transfer */
status = libusb_control_transfer(handle, /* Device Handle */
0x80, /* bRequestType */
0x10, /* bRequest */
value, /* wValue */
0x0, /* wIndex */
((uint8_t *) &value), /* data */
2, /* wLength */
100); /* timeout */
if(status < 0) {
printf("Error during control transfer!\n");
libusb_close(handle);
libusb_exit(NULL);
return -1;
}
printf("Got: %d\n", value);
value = (value + 1) & 0x1;
}
/* Do control transfer */
status = libusb_control_transfer(handle, 0x0, 0x10, value, 0x0, NULL, 0, 100);
if(status < 0) {
printf("Error during control transfer!\n");
libusb_close(handle);
libusb_exit(NULL);
return -1;
}
libusb_close(handle);
libusb_exit(NULL);
return 0;
}
Now I want to control my device over a USB Driver. Here is what I got already:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/slab.h>
/* Meta Information */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johannes 4 GNU/Linux");
MODULE_DESCRIPTION("Driver for my custom RPi Pico USB device");
struct pico_usb {
struct usb_device *udev;
};
#define PICO_VID 0x0000
#define PICO_PID 0x0001
static struct usb_device_id pico_usb_table [] = {
{ USB_DEVICE(PICO_VID, PICO_PID) },
{},
};
MODULE_DEVICE_TABLE(usb, pico_usb_table);
static int pico_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) {
struct pico_usb *data;
int status;
int my_data;
printk("pico_usb_drv - Now I am in the Probe function!\n");
data = kzalloc(sizeof(struct pico_usb), GFP_KERNEL);
if(!data) {
printk("pico_usb_drv - Out of memory\n");
return -ENOMEM;
}
data->udev = interface_to_usbdev(intf);
usb_set_intfdata(intf, data);
/* Turn the LED on */
status = usb_control_msg(data->udev, usb_sndctrlpipe(data->udev, 0), 0x10, USB_DIR_OUT, 1, 0, 0, 0, 100);
/* Read LED state */
printk("pico_usb_drv - status USB_DIR_OUT: %d\n", status);
status = usb_control_msg(data->udev, usb_rcvctrlpipe(data->udev, 0), 0x10, USB_DIR_IN, 0, 0, (u8*) &my_data, 2, 100);
printk("pico_usb_drv - status USB_DIR_IN: %d\n", status);
return 0;
}
static void pico_usb_disconnect(struct usb_interface *intf) {
struct pico_usb *data;
printk("pico_usb_drv - Now I am in the Disconnect function!\n");
data = usb_get_intfdata(intf);
kfree(data);
}
static struct usb_driver pico_usb_driver = {
//.owner = THIS_MODULE,
.name = "pico_usb",
.id_table = pico_usb_table,
.probe = pico_usb_probe,
.disconnect = pico_usb_disconnect,
};
/**
* #brief This function is called, when the module is loaded into the kernel
*/
static int __init pico_usb_init(void) {
int result;
printk("pico_usb_drv - Registering the PICO USB device\n");
result = usb_register(&pico_usb_driver);
if(result) {
printk("pico_usb_drv - Error registering the PICO USB device\n");
return -result;
}
return 0;
}
/**
* #brief This function is called, when the module is removed from the kernel
*/
static void __exit pcio_usb_exit(void) {
printk("pico_usb_drv - Unregistering the PICO USB device\n");
usb_deregister(&pico_usb_driver);
}
module_init(pico_usb_init);
module_exit(pcio_usb_exit);
The first control message works and my LED is turned on. But the second control message doesn't do anything, but gives me the error code -11 back.
Does anyone know, if I am missing something or if I am doing something wrong?
Ok, I found the solution. Instead of usb_control_msg I use usb_control_msg_recv now and everything works just fine.
usb_control_msg_recv takes one more argument:
int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *driver_data, __u16 size, int timeout, gfp_t memflags)
As I pass the pointer to a variable and don't want to allocate memory dynamically, I set the memflags argument to 0.
I am first time using function pointers and ran into a weird problem. I am writing a code for STM32G4xx. The main idea is to transmit and receive data through LPUART. I have implemented simple FSM to handle TX and RX. LPUART configured in DMA interrupt mode. I have typedef the function pointer and declared the three function pointer variables (ISR handles) in main.h file as follow:
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* USER CODE BEGIN ET */
typedef void (*_func_clbck)(void);
/* USER CODE END ET */
_func_clbck lpuart_tx_tc_isr_clback;
//_func_clbck lpuart_rx_rne_isr_clback;
_func_clbck lpuart_dma_tx_tc_isr_clback;
_func_clbck lpuart_dma_rx_tc_isr_clback;
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
The definition of the function pointer is in function MX_LPUART1_UART_Init() in the main.c file.
#include "main.h"
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* System interrupt init*/
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
/** Configure the internal voltage reference buffer voltage scale
*/
LL_VREFBUF_SetVoltageScaling(LL_VREFBUF_VOLTAGE_SCALE1);
/** Enable the Internal Voltage Reference buffer
*/
LL_VREFBUF_Enable();
/** Configure the internal voltage reference buffer high impedance mode
*/
LL_VREFBUF_DisableHIZ();
/** Disable the internal Pull-Up in Dead Battery pins of UCPD peripheral
*/
LL_PWR_DisableUCPDDeadBattery();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_LPUART1_UART_Init();
MX_RTC_Init();
/* USER CODE BEGIN 2 */
#ifdef LPUART_TEST
lpuart_init_test();
#endif
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
LL_GPIO_ResetOutputPin(GPIOC,LL_GPIO_PIN_6);
//LL_GPIO_SetOutputPin(GPIOC,LL_GPIO_PIN_6);
LL_mDelay(1);
uint8_t buf[9] ={'a','d','v',' ','l','o','w','\r','\n'};
//uint8_t buf[9] ={1,2,3,4,5,6,7,8,9};
#ifdef LPUART_TEST
uint16_t len_test[19] = {0,16,17,65535, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
for(uint16_t i = 0 ; i<19 ; i++){
set_len_rx_dma_buff(len_test[i]);
lpuart_rx_test();
}
#endif
static uint8_t once = 1;
while (1)
{
lpuart_task();
//delay_ms_DWT(1);
/*if(once){
once = 0;
lpuart_start_tx(buf, 9);
LL_mDelay(100);
}*/
if(1){
//lpuart_start_tx(buf, 9);
if(!lpuart_isTxBusy()){
lpuart_start_tx(buf, 9);
delay_ms_DWT(1);
if(!lpuart_isRxBusy()){
rxOldIndex += rxIndex;
if(rxOldIndex > 255){
rxOldIndex = 0;
rxIndex = 0;
}
rxIndex = RingBuffer_available(&lpuart_RX_ring_buff);
for(i = rxOldIndex ; i < (rxIndex+rxOldIndex) ; i++ ){
rxBuff[i] = RingBuffer_readMeas(&lpuart_RX_ring_buff);
}
}
}
}
//}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
static void MX_LPUART1_UART_Init(void)
{
/* USER CODE BEGIN LPUART1_Init 0 */
/* USER CODE END LPUART1_Init 0 */
LL_LPUART_InitTypeDef LPUART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
/**LPUART1 GPIO Configuration
PB10 ------> LPUART1_RX
PB11 ------> LPUART1_TX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_11;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_8;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* LPUART1 DMA Init */
/* LPUART1_TX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_1, LL_DMAMUX_REQ_LPUART1_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);
/* LPUART1_RX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_LPUART1_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_HIGH);
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_CIRCULAR);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MDATAALIGN_BYTE);
/* LPUART1 interrupt Init */
NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(LPUART1_IRQn);
/* USER CODE BEGIN LPUART1_Init 1 */
/* USER CODE END LPUART1_Init 1 */
LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
LPUART_InitStruct.BaudRate = 9600;
LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
LL_LPUART_DisableFIFO(LPUART1);
LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED);
// LL_LPUART_EnableOverrunDetect(LPUART1);
// LL_LPUART_EnableDMADeactOnRxErr(LPUART1);
/* USER CODE BEGIN WKUPType LPUART1 */
/* USER CODE END WKUPType LPUART1 */
LL_LPUART_Enable(LPUART1);
/* Polling LPUART1 initialisation */
while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))))
{
}
/* USER CODE BEGIN LPUART1_Init 2 */
//LL_LPUART_EnableIT_TXE_TXFNF(LPUART1);
/* Enable TC interrupts for RX */
/* Enable HT & TC interrupts for TX */
// LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
lpuart_tx_tc_isr_clback = LPUART_TX_TC_ISR_CALLBACK;
//lpuart_rx_rne_isr_clback = &LPUART_RX_RXNE_ISR_CALLBACK;
lpuart_dma_tx_tc_isr_clback = LPUART_DMA_TX_TC_CALLBACK;
lpuart_dma_rx_tc_isr_clback = LPUART_DMA_RX_TC_CALLBACK;
//LL_LPUART_EnableDirectionRx(LPUART1);
LL_LPUART_EnableDirectionTx(LPUART1);
//LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
/* USER CODE END LPUART1_Init 2 */
}
In main function, in while loop, I am calling the lpuart_start_tx(buf, 9); function just after the if(!lpuart_isTxBusy()) statement. This causes change of the address of the function pointer lpuart_dma_rx_tc_isr_clback but other two function pointers don't change its addresses and work fine. When an intrrupt raised on the reception of the data, it tried to execute lpuart_dma_rx_tc_isr_clback which causes the hard fault because its address was modified. Note that if I call lpuart_start_tx(buf, 9); before if(!lpuart_isTxBusy()), then everything works fine. I don't understand what could be an issue.
I have checked the CFSR register and every time different flag was raised. I have noticed that out of three flags, such as IBUSERR, IACCVOIL, and INVSTATE, one of them was raised. I have not included the full main.c file.
LPUART FSM implementation is as follow:
lpuart.h file
#ifndef INC_LPUART_H_
#define INC_LPUART_H_
#include "ring_buffer.h"
#ifdef USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
uint8_t lpuart_start_tx(uint8_t* buff, uint16_t len );
uint8_t LPUART_isTxFinished(void);
void LPUART_clear_tx_finishedFlag();
uint8_t lpuart_isRxBusy(void);
uint8_t lpuart_isTxBusy(void);
void lpuart_task();
void LPUART_TX_TC_ISR_CALLBACK(void);
void LPUART_DMA_TX_TC_CALLBACK(void);
void LPUART_DMA_RX_TC_CALLBACK(void);
#endif /* INC_LPUART_H_ */
lpuart.c
#include "lpuart.h"
#include "string.h"
#include "stm32g4xx_ll_lpuart.h"
#include "stm32g4xx_ll_dma.h"
#define SIZE_TX_BUFF 256
#define SIZE_RX_BUFF 256
#define SIZE_DMA_RX_BUFF 64
static uint8_t rxBuf[SIZE_RX_BUFF];
static uint8_t rxDMA_Buf[SIZE_DMA_RX_BUFF];
static uint8_t txBuf[SIZE_TX_BUFF];
RingBuffer lpuart_RX_ring_buff;
static uint8_t* tempTxBuf;
static uint16_t txLen = 0;
static void setTxDataLengthDMA(uint16_t len);
static void lpuart_transmit();
static void lpuart_finished_tx();
static void no_action();
static void lpuart_rx();
static void check_rx_dma_buff();
static uint8_t ev_no_event(void);
static uint8_t ev_txtc(void);
static uint8_t ev_start_tx(void);
static uint8_t ev_dmatxtc(void);
static uint8_t ev_rx_read(void);
static uint8_t ev_buff_full(void);
typedef uint8_t (*t_event_func)(void);
typedef void(*t_action_func)(void);
typedef struct{
uint16_t txtc:1;
uint16_t tx_busy:1;
uint16_t dmatxtc:1;
uint16_t dmarxtc:1;
uint16_t start_tx:1;
uint16_t rx_read:1;
uint16_t buff_full:1;
uint16_t is_fsm_table_init:1;
uint16_t ext_rxBusyFlag:1;
}t_event;
typedef enum {INIT, READY, BUSY, COMPLETE, LPUART_ERROR}t_state;
typedef struct{
t_state present_state;
t_event_func event1;
t_event_func event2;
t_action_func action1;
t_action_func action2;
t_state next_state;
}t_fsm_row;
typedef struct{
t_state current_state;
t_state previous_state;
t_fsm_row stt_row[7];
uint16_t number_of_rows;
}t_fsm_table;
t_event lpuart_event;
uint8_t lpuart_isTxBusy(void){
return lpuart_event.tx_busy;
}
uint8_t lpuart_isRxBusy(){
return lpuart_event.ext_rxBusyFlag;
}
uint8_t ev_no_event(void){
return 1;
}
uint8_t ev_txtc(void){
return lpuart_event.txtc;
}
uint8_t ev_start_tx(void){
return lpuart_event.start_tx;
}
uint8_t ev_dmatxtc(void){
return lpuart_event.dmatxtc;
}
uint8_t ev_rx_read(void){
return lpuart_event.rx_read || lpuart_event.dmarxtc;
}
uint8_t ev_buff_full(void){
return lpuart_event.buff_full;
}
void no_action(){
return;
}
void lpuart_init(void){
memset(rxBuf, 0, (size_t)SIZE_RX_BUFF );
memset(rxDMA_Buf, 0, (size_t)SIZE_DMA_RX_BUFF );
memset(txBuf, 0, (size_t)SIZE_TX_BUFF );
RingBuffer_init(&lpuart_RX_ring_buff,rxBuf , (uint16_t)SIZE_RX_BUFF);
LL_LPUART_DisableIT_TC(LPUART1);
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
LL_LPUART_DisableDirectionRx(LPUART1);
LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); //tx
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); //tx
LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2); //rx
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); //rx
/*TX buffer address attached to DMA channel1 */
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1,
(uint32_t)txBuf,
LL_LPUART_DMA_GetRegAddr(LPUART1, LL_LPUART_DMA_REG_DATA_TRANSMIT),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1));
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, SIZE_TX_BUFF);
/*RX buffer address attached to DMA channel2 */
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2,
LL_LPUART_DMA_GetRegAddr(LPUART1, LL_LPUART_DMA_REG_DATA_RECEIVE),
(uint32_t)rxDMA_Buf,
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2));
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, SIZE_DMA_RX_BUFF);
LL_LPUART_EnableDMAReq_RX(LPUART1);
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2); //rx
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); //rx
LL_LPUART_EnableDirectionRx(LPUART1);
lpuart_event.tx_busy = 0;
}
uint8_t lpuart_start_tx(uint8_t* buff, uint16_t len ){
if(!lpuart_event.tx_busy){
lpuart_event.tx_busy = 1;
if(len < SIZE_TX_BUFF){
lpuart_event.start_tx = 1;
tempTxBuf = buff;
txLen = len;
}else{
/* lpuart_event.start_tx = 0;
lpuart_event.buff_full = 1;*/
return 0;
}
return 1;
}else{
return 0;
}
}
void lpuart_transmit(){
memcpy(txBuf, tempTxBuf, txLen);
setTxDataLengthDMA(txLen);
LL_LPUART_EnableDMAReq_TX(LPUART1);
LL_LPUART_EnableDirectionTx(LPUART1);
LL_LPUART_EnableIT_TC(LPUART1);
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
}
void lpuart_finished_tx(){
LL_LPUART_DisableIT_TC(LPUART1);
LL_LPUART_DisableDirectionTx(LPUART1);
LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_DisableIT_TE(DMA1, LL_DMA_CHANNEL_1);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
lpuart_event.txtc = 0;
lpuart_event.dmatxtc = 0;
lpuart_event.start_tx = 0;
lpuart_event.tx_busy = 0;
}
void setTxDataLengthDMA(uint16_t len){
LL_LPUART_DisableDirectionTx(LPUART1);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
/* Clear all flags */
LL_DMA_ClearFlag_TC1(DMA1);
LL_DMA_ClearFlag_TE1(DMA1);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, len);
}
void check_rx_dma_buff(){
static uint16_t old_len = SIZE_DMA_RX_BUFF;
uint16_t current_len = LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
if(old_len != current_len ){
if(current_len > 0){
lpuart_event.rx_read = 1;
}
}
old_len = current_len;
}
static uint16_t old_pos = 0;
uint16_t pos = 0;
void lpuart_rx(){
lpuart_event.ext_rxBusyFlag = 1;
if(lpuart_event.dmarxtc){
lpuart_event.dmarxtc = 0;
}
if(lpuart_event.rx_read){
lpuart_event.rx_read = 0;
}
pos = (uint16_t)SIZE_DMA_RX_BUFF - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
if(pos > SIZE_DMA_RX_BUFF ){
pos = SIZE_DMA_RX_BUFF;
}
if(pos != old_pos /* || (pos == 0 && old_pos == 0)*/){
if (pos > old_pos) {
RingBuffer_writeMeasBlock(&lpuart_RX_ring_buff,&rxDMA_Buf[old_pos],(pos - old_pos));
}else{
RingBuffer_writeMeasBlock(&lpuart_RX_ring_buff,&rxDMA_Buf[old_pos],((uint16_t)SIZE_DMA_RX_BUFF - old_pos));
if(pos > 0){
RingBuffer_writeMeasBlock(&lpuart_RX_ring_buff,&rxDMA_Buf[0],pos);
}
}
}
old_pos = pos;
lpuart_event.ext_rxBusyFlag = 0;
}
t_fsm_row rowInit = {INIT,ev_no_event, ev_no_event, lpuart_init, no_action, READY };
t_fsm_row rowReady = {READY,ev_start_tx, ev_no_event, lpuart_transmit, no_action, BUSY };
t_fsm_row rowBusy = {BUSY,ev_dmatxtc, ev_txtc, lpuart_finished_tx, no_action, READY};
t_fsm_table lpuart_fsm_table = {INIT,INIT, {}};
void fsm_table_init(){
lpuart_fsm_table.current_state = INIT;
lpuart_fsm_table.stt_row[0] = rowInit;
lpuart_fsm_table.stt_row[1] = rowReady;
lpuart_fsm_table.stt_row[2] = rowBusy;
lpuart_event.is_fsm_table_init = 1;
lpuart_fsm_table.number_of_rows = 3;
}
void lpuar_tx_fsm(){
uint8_t ev = 0;
for(uint16_t rowIndex = 0; rowIndex < lpuart_fsm_table.number_of_rows; rowIndex++){
if(lpuart_fsm_table.current_state == lpuart_fsm_table.stt_row[rowIndex].present_state){
ev = lpuart_fsm_table.stt_row[rowIndex].event1() && lpuart_fsm_table.stt_row[rowIndex].event2();
if(ev){
lpuart_fsm_table.stt_row[rowIndex].action1();
lpuart_fsm_table.stt_row[rowIndex].action2();
lpuart_fsm_table.current_state = lpuart_fsm_table.stt_row[rowIndex].next_state;
lpuart_fsm_table.previous_state = lpuart_fsm_table.stt_row[rowIndex].present_state;
}
}
}
}
void lpuar_read_task(){
check_rx_dma_buff();
if(ev_rx_read()){
lpuart_rx();
}
}
void lpuart_task(){
if(!lpuart_event.is_fsm_table_init){
fsm_table_init();
}
//else{
lpuar_tx_fsm();
lpuar_read_task();
//}
}
void LPUART_TX_TC_ISR_CALLBACK(void){
lpuart_event.txtc = 1;
LL_LPUART_ClearFlag_TC(LPUART1);
}
void LPUART_DMA_TX_TC_CALLBACK(void){
lpuart_event.dmatxtc = 1;
LL_DMA_ClearFlag_TC1(DMA1);
}
void LPUART_DMA_RX_TC_CALLBACK(void){
if(LL_DMA_IsActiveFlag_TC2(DMA1)){
LL_DMA_ClearFlag_TC2(DMA1);
lpuart_event.dmarxtc = 1;
lpuart_rx();
}
if(LL_DMA_IsActiveFlag_TE2(DMA1)){
LL_DMA_ClearFlag_TE2(DMA1);
}
}
Intrrupt routine file stm32g4xx_it.c:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32g4xx_it.h"
#include "lpuart.h"
extern _func_clbck lpuart_tx_tc_isr_clback;
//_func_clbck lpuart_rx_rne_isr_clback = LPUART_RX_RXNE_ISR_CALLBACK;
extern _func_clbck lpuart_dma_tx_tc_isr_clback;
extern _func_clbck lpuart_dma_rx_tc_isr_clback;
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
void DMA1_Channel2_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel2_IRQn 0 */
lpuart_dma_rx_tc_isr_clback();
//LPUART_DMA_RX_TC_CALLBACK();
/* USER CODE END DMA1_Channel2_IRQn 0 */
/* USER CODE BEGIN DMA1_Channel2_IRQn 1 */
/* USER CODE END DMA1_Channel2_IRQn 1 */
}
Some of the code I have ommitted here. I am sure that I have not fully understood how function pointers work. Could you please give me suggestions or a solution.
I am using:
language: C11
Compiler/build tools: GNU tools for STM32 (7-2018-q2-update)
IDE: STM32CubeIDE
Please let me know if you require more info. Thanks in advance.
As per #Lundin's suggestion, I have put a watchpoint on lpuart_dma_rx_tc_isr_clback function pointer variable. It exposed the out of index bug in my code. The bug is inside while loop in main.c.
rxIndex = RingBuffer_available(&lpuart_RX_ring_buff);
for(i = rxOldIndex ; i < (rxIndex+rxOldIndex) ; i++ ){
rxBuff[i] = RingBuffer_readMeas(&lpuart_RX_ring_buff);
....
}
It was also pointed out that function pointer variable shouldn't be in flash.
I am currently working on a project where I try to send data from a FPGA to an ARM-processor(STM32F407). I am trying to do this by using FSMC, where I simulate the FPGA as a nor flash memory seen from the processor. I need to use FSMC because the FPGA samples a lot of data from sensors and from two GPS.
When i use the function HAL_NOR_Read() the code crashes into HardFault_Handler
FSMC is configured for 8-bit muxed nor-flash memmory.FSMC config
I am using TrueStudio to program STM32F4 and have used the debugger to go through the assembly code before the code crashes. But I don't think I have knowledge to find the fault there.
Maybe someone else have more knowledge who could help me solve the problem.
Below is the code I use.
#include "main.h"
#include "stm32f4xx_hal.h"
SD_HandleTypeDef hsd;
UART_HandleTypeDef huart4;
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart3;
NOR_HandleTypeDef hnor1;
FMC_NORSRAM_TimingTypeDef Timing;
#define NOR_BANK_ADDR ((uint32_t *)0x64000000)
#define BUFFER_SIZE ((uint32_t)16)
uint16_t aRxBuffer[BUFFER_SIZE] = {0};
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_FSMC_Init(void);
static void MX_SDIO_SD_Init(void);
static void MX_UART4_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART3_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_FSMC_Init();
MX_SDIO_SD_Init();
MX_UART4_Init();
MX_USART1_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
HAL_NOR_Init(&hnor1,&Timing,&Timing);
HAL_NOR_Read(&hnor1,NOR_BANK_ADDR,aRxBuffer);
while (1)
{
}
}
This is the parameters for FSMC
static void MX_FSMC_Init(void)
{
FSMC_NORSRAM_TimingTypeDef Timing;
/** Perform the NOR1 memory initialization sequence
*/
hnor1.Instance = FSMC_NORSRAM_DEVICE;
hnor1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
/* hnor1.Init */
hnor1.Init.NSBank = FSMC_NORSRAM_BANK1;
hnor1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_ENABLE;
hnor1.Init.MemoryType = FSMC_MEMORY_TYPE_NOR;
hnor1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_8;
hnor1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_ENABLE;
hnor1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
hnor1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
hnor1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
hnor1.Init.WriteOperation = FSMC_WRITE_OPERATION_DISABLE;
hnor1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
hnor1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
hnor1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
hnor1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
hnor1.Init.PageSize = FSMC_PAGE_SIZE_NONE;
/* Timing */
Timing.AddressSetupTime = 15;
Timing.AddressHoldTime = 15;
Timing.DataSetupTime = 255;
Timing.BusTurnAroundDuration = 15;
Timing.CLKDivision = 16;
Timing.DataLatency = 17;
Timing.AccessMode = FSMC_ACCESS_MODE_A;
/* ExtTiming */
if (HAL_NOR_Init(&hnor1, &Timing, NULL) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
I modified device tree file and enable spi using 4 GPIO pins, which support pinmux and switch from gpio to spi function.
But in Linux kernel code, how does the code know which spi bus/pins is used?
For example, I find a Linux kernel driver: max1111.c, which drives a spi ADC chip. But I checked its code, and don't find where the spi bus/pins is specified.
I paste max1111.c below.
/*
* max1111.c - +2.7V, Low-Power, Multichannel, Serial 8-bit ADCs
*
* Based on arch/arm/mach-pxa/corgi_ssp.c
*
* Copyright (C) 2004-2005 Richard Purdie
*
* Copyright (C) 2008 Marvell International Ltd.
* Eric Miao <eric.miao#marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* publishhed by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
enum chips { max1110, max1111, max1112, max1113 };
#define MAX1111_TX_BUF_SIZE 1
#define MAX1111_RX_BUF_SIZE 2
/* MAX1111 Commands */
#define MAX1111_CTRL_PD0 (1u << 0)
#define MAX1111_CTRL_PD1 (1u << 1)
#define MAX1111_CTRL_SGL (1u << 2)
#define MAX1111_CTRL_UNI (1u << 3)
#define MAX1110_CTRL_SEL_SH (4)
#define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */
#define MAX1111_CTRL_STR (1u << 7)
struct max1111_data {
struct spi_device *spi;
struct device *hwmon_dev;
struct spi_message msg;
struct spi_transfer xfer[2];
uint8_t tx_buf[MAX1111_TX_BUF_SIZE];
uint8_t rx_buf[MAX1111_RX_BUF_SIZE];
struct mutex drvdata_lock;
/* protect msg, xfer and buffers from multiple access */
int sel_sh;
int lsb;
};
static int max1111_read(struct device *dev, int channel)
{
struct max1111_data *data = dev_get_drvdata(dev);
uint8_t v1, v2;
int err;
/* writing to drvdata struct is not thread safe, wait on mutex */
mutex_lock(&data->drvdata_lock);
data->tx_buf[0] = (channel << data->sel_sh) |
MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 |
MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR;
err = spi_sync(data->spi, &data->msg);
if (err < 0) {
dev_err(dev, "spi_sync failed with %d\n", err);
mutex_unlock(&data->drvdata_lock);
return err;
}
v1 = data->rx_buf[0];
v2 = data->rx_buf[1];
mutex_unlock(&data->drvdata_lock);
if ((v1 & 0xc0) || (v2 & 0x3f))
return -EINVAL;
return (v1 << 2) | (v2 >> 6);
}
#ifdef CONFIG_SHARPSL_PM
static struct max1111_data *the_max1111;
int max1111_read_channel(int channel)
{
return max1111_read(&the_max1111->spi->dev, channel);
}
EXPORT_SYMBOL(max1111_read_channel);
#endif
/*
* NOTE: SPI devices do not have a default 'name' attribute, which is
* likely to be used by hwmon applications to distinguish between
* different devices, explicitly add a name attribute here.
*/
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static ssize_t show_adc(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct max1111_data *data = dev_get_drvdata(dev);
int channel = to_sensor_dev_attr(attr)->index;
int ret;
ret = max1111_read(dev, channel);
if (ret < 0)
return ret;
/*
* Assume the reference voltage to be 2.048V or 4.096V, with an 8-bit
* sample. The LSB weight is 8mV or 16mV depending on the chip type.
*/
return sprintf(buf, "%d\n", ret * data->lsb);
}
#define MAX1111_ADC_ATTR(_id) \
SENSOR_DEVICE_ATTR(in##_id##_input, S_IRUGO, show_adc, NULL, _id)
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static MAX1111_ADC_ATTR(0);
static MAX1111_ADC_ATTR(1);
static MAX1111_ADC_ATTR(2);
static MAX1111_ADC_ATTR(3);
static MAX1111_ADC_ATTR(4);
static MAX1111_ADC_ATTR(5);
static MAX1111_ADC_ATTR(6);
static MAX1111_ADC_ATTR(7);
static struct attribute *max1111_attributes[] = {
&dev_attr_name.attr,
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
NULL,
};
static const struct attribute_group max1111_attr_group = {
.attrs = max1111_attributes,
};
static struct attribute *max1110_attributes[] = {
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
NULL,
};
static const struct attribute_group max1110_attr_group = {
.attrs = max1110_attributes,
};
static int setup_transfer(struct max1111_data *data)
{
struct spi_message *m;
struct spi_transfer *x;
m = &data->msg;
x = &data->xfer[0];
spi_message_init(m);
x->tx_buf = &data->tx_buf[0];
x->len = MAX1111_TX_BUF_SIZE;
spi_message_add_tail(x, m);
x++;
x->rx_buf = &data->rx_buf[0];
x->len = MAX1111_RX_BUF_SIZE;
spi_message_add_tail(x, m);
return 0;
}
static int max1111_probe(struct spi_device *spi)
{
enum chips chip = spi_get_device_id(spi)->driver_data;
struct max1111_data *data;
int err;
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
err = spi_setup(spi);
if (err < 0)
return err;
data = devm_kzalloc(&spi->dev, sizeof(struct max1111_data), GFP_KERNEL);
if (data == NULL) {
dev_err(&spi->dev, "failed to allocate memory\n");
return -ENOMEM;
}
switch (chip) {
case max1110:
data->lsb = 8;
data->sel_sh = MAX1110_CTRL_SEL_SH;
break;
case max1111:
data->lsb = 8;
data->sel_sh = MAX1111_CTRL_SEL_SH;
break;
case max1112:
data->lsb = 16;
data->sel_sh = MAX1110_CTRL_SEL_SH;
break;
case max1113:
data->lsb = 16;
data->sel_sh = MAX1111_CTRL_SEL_SH;
break;
}
err = setup_transfer(data);
if (err)
return err;
mutex_init(&data->drvdata_lock);
data->spi = spi;
spi_set_drvdata(spi, data);
err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group);
if (err) {
dev_err(&spi->dev, "failed to create attribute group\n");
return err;
}
if (chip == max1110 || chip == max1112) {
err = sysfs_create_group(&spi->dev.kobj, &max1110_attr_group);
if (err) {
dev_err(&spi->dev,
"failed to create extended attribute group\n");
goto err_remove;
}
}
data->hwmon_dev = hwmon_device_register(&spi->dev);
if (IS_ERR(data->hwmon_dev)) {
dev_err(&spi->dev, "failed to create hwmon device\n");
err = PTR_ERR(data->hwmon_dev);
goto err_remove;
}
#ifdef CONFIG_SHARPSL_PM
the_max1111 = data;
#endif
return 0;
err_remove:
sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
return err;
}
static int max1111_remove(struct spi_device *spi)
{
struct max1111_data *data = spi_get_drvdata(spi);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
mutex_destroy(&data->drvdata_lock);
return 0;
}
static const struct spi_device_id max1111_ids[] = {
{ "max1110", max1110 },
{ "max1111", max1111 },
{ "max1112", max1112 },
{ "max1113", max1113 },
{ },
};
MODULE_DEVICE_TABLE(spi, max1111_ids);
static struct spi_driver max1111_driver = {
.driver = {
.name = "max1111",
.owner = THIS_MODULE,
},
.id_table = max1111_ids,
.probe = max1111_probe,
.remove = max1111_remove,
};
module_spi_driver(max1111_driver);
MODULE_AUTHOR("Eric Miao <eric.miao#marvell.com>");
MODULE_DESCRIPTION("MAX1110/MAX1111/MAX1112/MAX1113 ADC Driver");
MODULE_LICENSE("GPL");
SPI device driver (max1111 in your case) get pointer to underlying SPI-controller (struct spi_device *spi) during probe stage (max1111_probe). Driver should use it send requests to controller (using spi_sync, for example). Driver doesn't know about what PINS SPI-controller use.
What SPI-controller is passed to SPI device driver? SPI device, should be
declared in the DTS-file inside SPI-controller node. The controller initialized from SPI-controller node is passed to device probe.
SPI-controller can be hardware (specific to SoC), or SPI-GPIO. In case of hardware SPI, pins usually dedicated and specified in SoC TRM. In case of SPI-GPIO, GPIO names are specified inside DTS-properties of SPI-GPIO. The properties names are: gpio-sck, gpio-miso, gpio-mosi, num-chipselects and cs-gpios (list).
The device tree file where the SPI device(max1111) is listed as child will have the corresponding base address of SPI module as shown in below example, this base address is related to the SPI bus not device.
spix#ABCDXYZ {
compatible = "busdriver,variant";
/*This naming convention depends on the device tree*/
mosi = <gpioA> /*replace gpioA/B/C/D with your gpios*/
miso = <gpioB>
gpio-clk = <gpioC>
gpio-cs0 = <gpioD>
spi-max-frequency = <freq1>;
;
;
;
max1111#0 {
compatible = "max1111";
reg = <0>;
spi-max-frequency = <freq2>;
;
;
;
};
;
;
;
}
In the above device tree file you can see the device node "max1111" listed as child under the bus node "spix" whose register address range starts at address #ABCDXYZ. You need to refer userguide of MCU you are using to know this base address.
The driver file max1111.c, is the device driver file for one of the SPI devices sitting on the spix bus.
The driver max1111.c is agnostic to the SPI lines that it is sitting on & just enjoys the APIs (Ex: data transfer & chip select handling) provided by the SPI bus driver associated with "busdriver,variant". The SPI bus driver will take care of configuring/multiplexing the MCU pins into SPI mode (handled explicitly in machine/board specific initialisation code).
So all you have to do is to,
Identify which SPI bus max1111 device is using.
Then go to the device tree file corresponding to you board.
Add your device driver details (+ SPI parameters) as part of child to that bus node
include your max1111.c into the kernel biuld directory (mostly drivers/spi folder)
that is it.
Note: here the assumption is your machine is already having that particular SPI bus driver enabled.
I am working on a project where I have to establish a communication between my microprocessor and a Bluetooth device. I established a communication, but no matter what I send, I only get 0 when I print it. Thanks for the help !
#include <asf.h>
#include <string.h>
#include <stdio.h>
#include "usart.h"
/**
* Configure serial console.
*/
static void configure_console(void)
{
const usart_serial_options_t uart_serial_options = {
.baudrate = CONF_UART_BAUDRATE,
#ifdef CONF_UART_CHAR_LENGTH
.charlength = CONF_UART_CHAR_LENGTH,
#endif
.paritytype = CONF_UART_PARITY,
#ifdef CONF_UART_STOP_BITS
.stopbits = CONF_UART_STOP_BITS,
#endif
};
/* Configure console. */
stdio_serial_init(CONF_UART, &uart_serial_options);
}
volatile uint32_t ul_ms_ticks = 0;
static void mdelay(uint32_t ul_dly_ticks)
{
uint32_t ul_cur_ticks;
ul_cur_ticks = ul_ms_ticks;
while ((ul_ms_ticks - ul_cur_ticks) < ul_dly_ticks) {
}
}
void SysTick_Handler(void)
{
ul_ms_ticks++;
}
#define USART_SERIAL USART1
#define USART_SERIAL_ID ID_USART1 //USART1 for sam4l
#define USART_SERIAL_BAUDRATE 9600
#define USART_SERIAL_CHAR_LENGTH US_MR_CHRL_8_BIT
#define USART_SERIAL_PARITY US_MR_PAR_NO
#define USART_SERIAL_STOP_BIT US_MR_NBSTOP_1
int main(void)
{
/* Initialize the SAM system */
sysclk_init();
board_init();
/* Initialize the console uart */
configure_console();
uint32_t a= 1234;
int bit_rx = 6;
if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
while (1) { /* Capture error */
}
}
const sam_usart_opt_t usart_console_settings = {
USART_SERIAL_BAUDRATE,
USART_SERIAL_CHAR_LENGTH,
USART_SERIAL_PARITY,
USART_SERIAL_STOP_BIT,
US_MR_CHMODE_NORMAL
};
#if SAM4L
sysclk_enable_peripheral_clock(USART_SERIAL);
#else
sysclk_enable_peripheral_clock(USART_SERIAL_ID);
#endif
usart_enable_tx(USART_SERIAL);
usart_enable_rx(USART_SERIAL);
while (1) {
printf("%d \n", bit_rx );
usart_getchar(USART_SERIAL, &a);
bit_rx = a;
printf("%d \n",bit_rx );
if (a != 0) {
ioport_set_pin_level(LED_0_PIN, LED_0_ACTIVE);
} else {
ioport_set_pin_level(LED_0_PIN, !LED_0_ACTIVE);
}
}
}
Perhaps you shouldn't call usart_getchar() until usart_is_rx_ready() returns 1.
Thanks everyone !
I tried to cast the received variable first : bit_rx = (int) a;
And then I found that I needed this to make it work :
static usart_serial_options_t usart_options = {
.baudrate = USART_SERIAL_BAUDRATE,
.charlength = USART_SERIAL_CHAR_LENGTH,
.paritytype = USART_SERIAL_PARITY,
.stopbits = USART_SERIAL_STOP_BIT
};
usart_serial_init(USART_SERIAL, &usart_options);
And I disabled the stop bit, because my bluetooth didn't have one :
#define USART_SERIAL_STOP_BIT false
Now it works like a charm. Thanks all for your help !