ATSAMC21N從寄存器開發到ASF、FreeRTOS裁剪移植、與AD7779評估版進行SPI通信獲取AD數據採集

聲明

如若轉載,請附轉載地址並聲明原作者。此項目在GitHub託管,所有的資料包括datasheet都在GitHub託管,託管地址。作者還擁有個人公衆號,會寫一些感悟文章,知圈,二維碼如下,歡迎掃描關注:
知圈二維碼

說明

本項目分AD採集模塊和軟件模塊。本文爲軟件模塊,AD採集的硬件模塊請參考該文章

項目需求

一秒一千次採樣以上,30通道以上,24bit以上分辨率。

器件選型

備選:
在這裏插入圖片描述
最終決定:ATSAMC21J18A
併購入了官方的該芯片的demo板: Atmel® SAM C21 Xplained Pro evaluation kit

軟件配置與框架搭建

AtmelStudio環境下的ATSAMC21N開發配置

打開Atmel studio,用usb線把開發板連接至PC,Windows10自動安裝驅動,然後在Atmel studio上面會彈出板子的信息:
在這裏插入圖片描述
上面有關於此板子的信息,asf開發文檔,板子開發文檔等都在裏面。

新建項目,此配置參考Atmel的配置視頻
在這裏插入圖片描述
輸入名字後進入選擇界面,選擇對應的MCU。
在這裏插入圖片描述
生成後的main.c文件如下所示:

/*
 * RoadScannerDataAcquire.c
 *
 * Created: 2019-12-18 15:18:10
 * Author : lenovo
 */ 


#include "sam.h"


int main(void)
{
    /* Initialize the SAM system */
    SystemInit();

    /* Replace with your application code */
    while (1) 
    {
    }
}

爲了熟悉板子,本例用寄存器原始方式、ASF框架、Freertos三種開發形式上都進行實驗。保險起見,先把代碼上傳至Github。

創建Github

打開Github,
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
publish 到雲端賬號
在這裏插入圖片描述
在這裏插入圖片描述

寄存器方式(without ASF)

經查找SAM C21 Xplained Pro board的user guide,板子上有一個LED燈連接在PC的05腳上。本例用更改寄存器的方式實現對此燈的控制。
通過查看芯片的datasheet,找到以下兩個需要更改的寄存器。

端口設置爲輸出:
在這裏插入圖片描述
輸出設置爲低電平:
在這裏插入圖片描述
更改main函數:

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();


    /* Replace with your application code */
	//將所有的PC口設置爲輸出。
	REG_PORT_DIRSET2 = 0xffffffff;
	//將所有的PC口置爲低電平以點亮LED。(LED另一端爲3.3V)
	REG_PORT_OUTCLR2 = 0xffffffff;
    while (1) 
    {

    }
}

後來在官網找到了兄弟板的示例程序。並根據示例程序根據開發板做了更改,現在板子的LED在PC05腳,而示例程序的引腳在PA15腳。加載程序後LED以1hz的頻率閃爍。

#include "sam.h"
// I/O Ports definitions
#define PORTA     (0ul)
#define PORTB     (1ul)
#define PORTC     (2ul)

// LED0 definitions
#define LED0_PORT            PORTC
#define LED0_PIN_NUMBER      (05ul)
#define LED0_PIN             PORT_PC05
/** VARIABLES *****************************************************************/

static uint32_t ul_tickcount=0 ;	// Global state variable for tick count

/** LOCAL PROTOTYPES **********************************************************/

void AppInit(void);					// Application Hardware/Software Initialization

/** main() ********************************************************************/

int main(void){	
    
	/* Initialize the SAM system - auto-generated code */
    SystemInit();
	
	/* Application hardware and software initialization */
	AppInit();

    /* Replace with your application code */
    while(1){
	__NOP();
    }
}

/*******************************************************************************
 * Function:        void AppInit(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This routine takes care of all of the hardware/software
 *                  initialization that is required.
 *
 * Note:
 *
 ******************************************************************************/
void AppInit(void){
	
	/*	Clock initialization (CPU, AHB, APBx)
		The System RC Oscillator (RCSYS) provides the source for the main clock
		at chip startup. It is set to 4 MHz.
		
		Add code here to change the system clock
	*/
	
	// Assign the LED0 pin as OUTPUT
	PORT->Group[LED0_PORT].DIRSET.reg = LED0_PIN ;
	// Set the LED0 pin level, i.e. put to 3.3V -> this turns off the LED
	PORT->Group[LED0_PORT].OUTSET.reg = LED0_PIN ;

	// Configure SysTick to trigger every millisecond using the CPU Clock
	SysTick->CTRL = 0;					// Disable SysTick
	SysTick->LOAD = 3999UL;				// Set reload register for 1mS interrupts
	NVIC_SetPriority(SysTick_IRQn, 3);	// Set interrupt priority to least urgency
	SysTick->VAL = 0;					// Reset the SysTick counter value
	SysTick->CTRL = 0x00000007;			// Enable SysTick, Enable SysTick Exceptions, Use CPU Clock
	NVIC_EnableIRQ(SysTick_IRQn);		// Enable SysTick Interrupt
	
}

/*******************************************************************************
 * Function:        void SysTick_Handler(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This interrupt handler is called on SysTick timer underflow
 *
 * Note:
 *
 ******************************************************************************/
void SysTick_Handler(void){
	
	ul_tickcount++ ;
	// Toggle LEDs every second (i.e. 1000ms)
	if(ul_tickcount % 1000 == 0){
		// Toggle LED pin output level.
		PORT->Group[LED0_PORT].OUTTGL.reg = LED0_PIN ;
	}
}

ASF

閱讀了官方的說明後,ASF4必須用Atmel START在線工具來配置。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述
添加完後改下名字,最後如下所示:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
由於之前solution裏面已經有RoadScannerDataAcquire的project,所示創建失敗,由於之前Github已經配置好了,並不想重新配置,也不想改名,所以在Atmel studio 7裏面右擊project名字,點擊remove,刪除,記得要去在計算機端刪掉文件夾,否則之前的文件會干擾新文件的生成,造成未知的錯誤,然後再用原名稱導入就可以了。
創建完後打開,發現直接用板子上的USB下載口可以進行uart調試。查看手冊後發現引腳爲TX:PB10、 RX:PB11。
在Atmel Studio裏面選擇reconfigure,
在這裏插入圖片描述
打開後找到如下配置,更改引腳。發現驅動用的是同步的,也改爲異步。
在這裏插入圖片描述
更改時發現這兩個引腳PB11和PB10已經被SPI3給佔了,先去SPI配置裏面把這兩個口改爲其它引腳,把這兩個口空出來,然後再返回UART裏面設置這兩個口。
設置完成並重新生成後,找到生成的文件下的示例代碼並打開,
在這裏插入圖片描述
打開後找到如下代碼塊:

/**
 * Example of using USART_3 to write "Hello World" using the IO abstraction.
 */
void USART_3_example(void)
{
	struct io_descriptor *io;
	usart_sync_get_io_descriptor(&USART_3, &io);
	usart_sync_enable(&USART_3);

	io_write(io, (uint8_t *)"Hello World!", 12);
}

因爲還會用到delay函數,同文件裏面找到delay的示例代碼:

void delay_example(void)
{
	delay_ms(5000);
}

打開main.c,裏面生成的代碼如下:

#include <atmel_start.h>
int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();

	/* Replace with your application code */
	while (1) {
	}
}

根據示例程序,將main.c改爲如下:

#include <atmel_start.h>
#include <driver_examples.h>

int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();

	/* Replace with your application code */
	while (1) {
	USART_On_USBPort_example();
	delay_ms(1000);
	}
}

編譯並將代碼下載到板子。
前面改了uart的引腳號,爲的就是直接使用Atmel Studio裏面的Data Visualizer功能直接進行虛擬串口的調試。
打開data visualizer工具:
在這裏插入圖片描述
雙擊serial port:
在這裏插入圖片描述
設置:
在這裏插入圖片描述
點擊上面的connect,會彈出terminal終端,然後我們的板子會按照我們之前設計的每秒發送一次hello word。
在這裏插入圖片描述
ASF4的測試代碼到此結束。

FreeRTOS

再次打開配置:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
點擊下能看到配置:
在這裏插入圖片描述
點擊freertos後,會出現其配置選項,在這裏用默認的即可。
在這裏插入圖片描述
點擊生成:
在這裏插入圖片描述
生成後找到如下rtos的示例代碼:
在這裏插入圖片描述
打開後代碼如下:

/*
 * Code generated from Atmel Start.
 *
 * This file will be overwritten when reconfiguring your Atmel Start project.
 * Please copy examples or other code you want to keep to a separate file or main.c
 * to avoid loosing it when reconfiguring.
 */
#include "atmel_start.h"
#include "rtos_start.h"
#include "driver_examples.h"

#define TASK_EXAMPLE_STACK_SIZE (128 / sizeof(portSTACK_TYPE))
#define TASK_EXAMPLE_STACK_PRIORITY (tskIDLE_PRIORITY + 1)
static TaskHandle_t      xCreatedExampleTask;
static SemaphoreHandle_t disp_mutex;

/**
 * OS example task
 *
 * \param[in] p The void pointer for OS task Standard model.
 *
 */
static void example_task(void *p)
{
	(void)p;
	while (1) {
		if (xSemaphoreTake(disp_mutex, ~0)) {
			/* add your code */
			xSemaphoreGive(disp_mutex);
		}
		os_sleep(500);
	}
}

/*
 * Example
 */
void FREERTOS_V1000_0_example(void)
{
	disp_mutex = xSemaphoreCreateMutex();

	if (disp_mutex == NULL) {
		while (1) {
			;
		}
	}

	if (xTaskCreate(
	        example_task, "Example", TASK_EXAMPLE_STACK_SIZE, NULL, TASK_EXAMPLE_STACK_PRIORITY, xCreatedExampleTask)
	    != pdPASS) {
		while (1) {
			;
		}
	}

	vTaskStartScheduler();

	return;
}

上面的代碼簡單明瞭。在main裏面調用下,FreeRTOS就啓動了。在main函數裏面添加調用,添加了兩句,一句是爲了調用示例函數添加的#include “rtos_start.h”,另一句是調用函數FREERTOS_V1000_0_example(); 添加完這兩句後,原先main裏面的while死循環就永遠不會執行到了,任務由FreeRTOS調度。
如下所示:

#include <atmel_start.h>
#include <driver_examples.h>
#include "rtos_start.h"

int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();
	FREERTOS_V1000_0_example();

	/* Replace with your application code */
	while (1) {
	USART_On_USBPort_example();
	delay_ms(1000);
	}
}

返回rtos_start.c函數,添加#include "driver_examples.h"已引用我們前面的uart測試函數。然後在示例任務中加入串口調用函數,如下:

/**
 * OS example task
 *
 * \param[in] p The void pointer for OS task Standard model.
 *
 */
static void example_task(void *p)
{
	(void)p;
	while (1) {
		if (xSemaphoreTake(disp_mutex, ~0)) {
			/* add your code */
				USART_On_USBPort_example();
//				delay_ms(1000);
			xSemaphoreGive(disp_mutex);
		}
		os_sleep(500);
	}
}

運行後500ms輸出一次hello word。運行結果如下:
在這裏插入圖片描述

與AD7779評估版進行SPI通信獲取AD數據採集

ANALOG DEVICES提供官方的C語言裸機驅動,給開發帶來了極大的便利,全資源可以點擊此處閱讀原文

AD7779評估版配置

評估版配置比較繁瑣,配置過程的說明篇幅很長,作者專門寫了一篇文章記錄,請參見此處

SPI通信模塊添加與配置

再次點擊配置:
在這裏插入圖片描述
點擊添加部件:
在這裏插入圖片描述
找到Drivers:
在這裏插入圖片描述
找到SPI並添加:
在這裏插入圖片描述
返回選中剛纔添加的SPI:
在這裏插入圖片描述
點擊後展開配置選項,然而第一個關於Driver的驅動配置就有近十個,如下:
在這裏插入圖片描述
經搜索,終於在官網上找到其各個不同driver的描述。此描述在官網有PDF版本的文檔,文檔名字:ASF4 API Reference Manual

根據開發板手冊,選用EXT3端口上的腳:
在這裏插入圖片描述
引腳改爲:
在這裏插入圖片描述
時序也需要設置,經翻看文檔,AD7779的DOUT模式時序圖如下:
在這裏插入圖片描述
SPI時序模式如下:
在這裏插入圖片描述
由以上信息,配置SPI參數爲:
在這裏插入圖片描述
再次點擊生成項目:
在這裏插入圖片描述

代碼結構分拆重構

前面測試的過程中,不斷通過Atmel START進行配置添加/更改。每次都需要重新生成代碼,生成代碼的時候會對很多文件進行覆蓋,個人在自動生成的文件上做的更改會被覆蓋掉。需要每個覈對。

由於後續會對文件進行頻繁的更改等,爲從敏捷角度出發,避免不必要的麻煩,現對源代碼進行分拆,分拆後只對main.c在源碼中進行更改(因其名字的不可改變性)。

分拆後的原則:

  • main.c的代碼代碼更改量儘可能的精簡。
  • 將所有用到的需要更改的全部摘出。
  • 敏捷化。

分拆後的代碼示例:

#include <atmel_start.h>
#include <driver_examples.h>
#include "Hui_rtos_start.h"

int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();
	Hui_FREERTOS_V1000_0_example();

	/* Replace with your application code */
	while (1) {
// Since we added the FreeRTOS system to manage the task, this loop will never be executed.
	}
}

/*
 * Hui_USBPort.c
 *
 * Created: 2020-05-13 14:06:50
 *  Author: TomorrowNeverComes
 */ 

#include "driver_init.h"
#include "utils.h"
#include "Hui_USBPort.h"
/**
 * Example of using USART_On_USBPort to write "Hello World" using the IO abstraction.
 *
 * Since the driver is asynchronous we need to use statically allocated memory for string
 * because driver initiates transfer and then returns before the transmission is completed.
 *
 * Once transfer has been completed the tx_cb function will be called.
 */

static void tx_cb_USART_On_USBPort(const struct usart_async_descriptor *const io_descr)
{
	/* Transfer completed */
}

void USART_On_USBPort_Output(uint8_t Data_USART_On_USBPort[], uint8_t arry_size)
{
	struct io_descriptor *io;

	usart_async_register_callback(&USART_On_USBPort, USART_ASYNC_TXC_CB, tx_cb_USART_On_USBPort);
	/*usart_async_register_callback(&USART_On_USBPort, USART_ASYNC_RXC_CB, rx_cb);
	usart_async_register_callback(&USART_On_USBPort, USART_ASYNC_ERROR_CB, err_cb);*/
	usart_async_get_io_descriptor(&USART_On_USBPort, &io);
	usart_async_enable(&USART_On_USBPort);

	io_write(io, Data_USART_On_USBPort, arry_size);
}

/*
 * Hui_USBPort.h
 *
 * Created: 2020-05-13 14:08:45
 *  Author: TomorrowNeverComes
 */ 


#ifndef HUI_USBPORT_H_
#define HUI_USBPORT_H_

#ifdef __cplusplus
extern "C" {
	#endif /* __cplusplus */

void USART_On_USBPort_Output(uint8_t*, uint8_t);

	#ifdef __cplusplus
}
#endif

#endif /* HUI_USBPORT_H_ */
/*
 * Hui_rtos_start.c
 *
 * Created: 2020-05-13 10:28:05
 *  Author: TomorrowNeverComes
 */ 
#include "atmel_start.h"
#include "Hui_rtos_start.h"
#include "Hui_USBPort.h"

#define TASK_EXAMPLE_STACK_SIZE (128 / sizeof(portSTACK_TYPE))
#define TASK_EXAMPLE_STACK_PRIORITY (tskIDLE_PRIORITY + 1)
static TaskHandle_t      xCreatedExampleTask;
static SemaphoreHandle_t disp_mutex;

/**
 * OS example task
 *
 * \param[in] p The void pointer for OS task Standard model.
 *
 */
static void example_task(void *p)
{
	(void)p;
	while (1) {
		if (xSemaphoreTake(disp_mutex, ~0)) {
			/* add your code */
			static uint8_t example_USART_On_USBPort[ ] = "He00000o World!";
				USART_On_USBPort_Output(example_USART_On_USBPort,sizeof(example_USART_On_USBPort)/sizeof(uint8_t));
//				delay_ms(1000);
			xSemaphoreGive(disp_mutex);
		}
		os_sleep(500);
	}
}

/*
 * Example
 */
void Hui_FREERTOS_V1000_0_example(void)
{
	disp_mutex = xSemaphoreCreateMutex();

	if (disp_mutex == NULL) {
		while (1) {
			;
		}
	}

	if (xTaskCreate(
	        example_task, "Example", TASK_EXAMPLE_STACK_SIZE, NULL, TASK_EXAMPLE_STACK_PRIORITY, xCreatedExampleTask)
	    != pdPASS) {
		while (1) {
			;
		}
	}

	vTaskStartScheduler();

	return;
}

/*
 * Hui_rtos_start.h
 *
 * Created: 2020-05-13 10:42:30
 *  Author: TomorrowNeverComes
 */ 


#ifndef HUI_RTOS_START_H_
#define HUI_RTOS_START_H_


#ifdef __cplusplus
extern "C" {
	#endif /* __cplusplus */

	#include <FreeRTOS.h>
	#include <task.h>
	#include <semphr.h>
	#include <hal_rtos.h>

	void Hui_FREERTOS_V1000_0_example(void);

	#ifdef __cplusplus
}
#endif /* __cplusplus */



#endif /* HUI_RTOS_START_H_ */

SPI代碼測試

未完,作者已離職,此項目不再維護。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章