- 2024-12-15
-
发表了主题帖:
【Follow me第二季第3期】EK-RA6M5简易信号发生器设计汇总
本帖最后由 EPTmachine 于 2024-12-15 13:36 编辑
本次Follow Me活动使用的开发板为Renesas的EK_RA6M5开发板。主要实现以下几个任务。
入门任务:搭建环境,下载调试示例程序,Blink,按键;
基础任务:quad-spi flash和octo-spi flash配置及读写速度测试;DAC配置生成波形及性能测试;
进阶任务:示例程序中新增命令打印信息;
扩展任务:设计一个类似信号发生器功能的例程。可在示例程序上修改。通过命令或按键,设置DAC输出波形,可通过flash存储历史波形等信息。
[localvideo]da54068e218459c5756737773eb2c7b5[/localvideo]
# 入门任务:
任务相关的内容[【Follow me第二季第3期】1入门任务:搭建环境,下载调试示例程序,Blink,按键](https://bbs.eeworld.com.cn/thread-1298133-1-1.html)
e2studio的开发环境功能多样,在熟悉各部分功能的使用后,可以借助FSP工具、Deverlop Asssitant代码提示加快开发进度。
# 基础任务:
任务相关的内容[【Follow me第二季第三期】基础任务:quad spi、octo spi、dac和定时器使用]( https://bbs.eeworld.com.cn/thread-1301167-1-1.html)
板载的外设很多,quad spi flash和octo spi flash扩展了开发板的存储空间,可以存储更多的程序和数据,实现复杂的应用,同时FSP配置工具中可以快捷地设定相关外设的驱动代码,方便外设的使用减少开发人员的工作量。
# 进阶任务:
任务相关的内容[【Follow me第二季第三期】进阶任务:通过USB发送指令修改QSPI中的数据 ](https://bbs.eeworld.com.cn/thread-1301875-1-1.html)
USB设备可以设定端点类型为CDC设备,从而实现与电脑的通讯,建立通讯后后,通过控制台向开发板发送数据,从而实现数据交互和指令控制。
根据接受到的数据的不同,提示用户输入相关的输入,从而实现对qspi flash中存储的数据的修改。
# 扩展任务:
任务相关的内容[【Follow me第二季第三期】扩展任务:设计一个类似信号发生器功能的例程](https://bbs.eeworld.com.cn/thread-1301877-1-1.html)
结合之前的qspi flash储存和usb 指令任务,添加dds模块后,即可以实现简易的DDS信号发生器。
# 总结
开发板的硬件资源和示例代码很多,方便开发者理解如何使用芯片的功能。同时e2studio的开发工具辅助开发者快速搭建应用程序,加快开发进度。
任务代码:
-
上传了资料:
FollowMeEK-RA6M5
-
发表了主题帖:
【Follow me第二季第三期】扩展任务:设计一个类似信号发生器功能的例程
本帖最后由 EPTmachine 于 2024-12-15 12:12 编辑
设计程序能发出不同的波形,频率范围1——1kHz,支持正弦波、方波和三角波。
## 程序设计
在MCU上实现信号发生器的功能,参考以下两篇博客
[STM32片上DAC实现DDS](https://www.emoe.xyz/stm32-dac-direct-digital-synthesis/)
[STM32口袋仪器设计](https://www.eetree.cn/project/546)
采用其中的DDS模块实现信号的输出。结合之前任务中使用到的USB、QSPI Flash外设即可实现类似信号发生器的功能。
## 程序实现
在FSP中添加以下的模块
涉及到DDS功能的模块有dac、gpt和dma模块。其中dac模块的参数设置如下
gpt模块设置如下
dma模块设置如下
DMA的数据搬运由定时器的溢出事件驱动。瑞萨DMA模块的使用和STM32的类似,在dds.c的DDS_setWaveParams函数中,通过修改DMA的数据源,加载不同的DDS查找表,从而实现不同配置波形的产生。
类似于任务3的Flash存储数据示例,在任务3的基础上添加以下文件,其中的代码实现DDS相关外设的初始化、DDS信号数据计算以及用户选择DDS信号配置的菜单。
添加上述代码,编译完成即可实现DDS信号发生器的功能,同时用户可以编辑信号的类型和频率。
# 程序演示
产生正弦波
产生方波
![alt text](figures/04/dds_squ.jpg)
产生三角波
相关示例代码
-
发表了主题帖:
【Follow me第二季第三期】进阶任务:通过USB发送指令修改QSPI中的数据
本帖最后由 EPTmachine 于 2024-12-15 12:07 编辑
查看开发资料中演示例程的流程图了解控制台指令是如何实现的。开发板通过USB接口连接到电脑后,通过对电脑端发送的字符串进行解析,从而执行不同的功能函数。
# USB传输字符串并解析
USB通讯中,客户端(这里为开发板)可以设定端点(endpoint)的类型,实现不同的功能,本任务中使用的CDC类型的端点,通过usb连接至电脑后,可以实现串口通讯功能,传输数据,实现人机交互。
实现上述功能需要用的到外设有qspi、usb。在FSP的配置界面中添加需要用到的组件、定义的RTOS线程以及信号量等。
USB模块使用USB_PCDC,FSP工具可以生成USB CDC类型端点的代码,简化开发流程。
属性界面中设置CallBack的函数名。
QSPI的配置参考基础任务中的qspi配置。
# 添加新指令
任务目标是添加一条新指令,指令的功能设定为查看和修改存储在QSPI Flash上的DDS数据。存储在QSPI Flash上的数据结构为dds_record,dds_record的定义如下。
```c
typedef struct dds_param{
uint8_t waveType;
uint8_t amp;
int8_t offset;
uint32_t freq;
}dds_param_t;
enum dds_wavetype{
SIN_WAVE=0,
SQU_WAVE=1,
TRI_WAVE=2
};
#define DDS_RECORD_TYPE 0xF5
typedef struct dds_record{
uint8_t type;
dds_param_t dds_items[10];
}dds_record_t;
```
# 程序设计
程序设计参考示例程序的框架,包含的文件中新的文件如图所示。dds_record.c、dds_record.h和dds_record_ctrl_entry.c实现在QSPI Flash中管理数据的功能。menu_dds_record.c、menu_dds_record.h实现指令解析部分。
新文件中的代码为实现新指令的代码。
Flash存储相关的代码会初始化qspi flash,并对需要存储的数据进行管理。
```c
#include "common_init.h"
#include "common_utils.h"
#include "qspi_drv.h"
#define DDS_RECORD_SIZE 1024
static char_t s_print_buffer[BUFFER_LINE_LENGTH] = {};
extern dds_record_t dds_record_ins;
/* DDS_record entry function */
/* pvParameters contains TaskHandle_t */
void dds_record_ctrl_entry(void *pvParameters)
{
FSP_PARAMETER_NOT_USED (pvParameters);
fsp_err_t err;
uint8_t* p_src=(uint8_t *)QSPI_DEVICE_START_ADDRESS;;
uint8_t* p_dest=(uint8_t*)&dds_record_ins;
qpi_init();
memcpy (p_dest, p_src, sizeof(dds_record_t));
if(dds_record_ins.type!=DDS_RECORD_TYPE)
{
/* Erase Flash for one sector */
err = R_QSPI_Erase(&g_qspi_ctrl, p_src, SECTOR_SIZE);
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "R_QSPI_Erase Failed\r\n");
print_to_console((void*)s_print_buffer);
}
}
/* TODO: add your own code here */
while (1)
{
while (pdPASS != xSemaphoreTake(g_store_dds_semaphore, pdMS_TO_TICKS(500u)))
{
vTaskDelay(10);
}
dds_record_ins.type=DDS_RECORD_TYPE;
/* Erase Flash for one sector */
err = R_QSPI_Erase(&g_qspi_ctrl, p_src, SECTOR_SIZE);
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "R_QSPI_Erase Failed\r\n");
print_to_console((void*)s_print_buffer);
}
err = get_flash_status();
err = R_QSPI_Write(&g_qspi_ctrl, (uint8_t*)&dds_record_ins, p_src, sizeof(dds_record_t));
if (FSP_SUCCESS != err)
{
sprintf(s_print_buffer, "R_QSPI_Write Failed\r\n");
print_to_console((void*)s_print_buffer);
}
err = get_flash_status();
sprintf (s_print_buffer, "g_store_dds_semaphore recevied" );
print_to_console((void*)s_print_buffer);
memcpy (p_dest, p_src, sizeof(dds_record_t));
vTaskDelay (10);
}
}
```
上述代码在初始化QSPI Flash后,读取其中的数据,并等待g_store_dds_semaphore信号量,用于更新Flash中存储的内容。
USB CDC模块中,用户需要自定义相关的端点描述符用于设备的枚举。相关的代码参考示例程序在r_usb_pcdc_descriptor.c中。
usb的Callback中转发接受到的事件,用于后续的字符解析。
```c
void usb_console_callback(usb_event_info_t* p_event_info, usb_hdl_t hdl, usb_onoff_t state)
{
/* Void unused args */
FSP_PARAMETER_NOT_USED(hdl);
FSP_PARAMETER_NOT_USED(state);
BaseType_t xHigherPriorityTaskWoken;
memcpy(&s_usb_event_list[s_usb_event_pindex], p_event_info, sizeof(usb_event_info_t));
if (xQueueIsQueueFullFromISR(g_usb_transaction_queue) == pdFALSE)
{
xQueueSendToBackFromISR(g_usb_transaction_queue, &s_usb_event_pindex, &xHigherPriorityTaskWoken);
/* set next available index, cast to maintain sign integrity */
s_usb_event_pindex = (uint8_t)((s_usb_event_pindex + 1) % USB_QUEUE_SIZE);
}
else
{
/* Fatal error - usb message queue is full, this should not occur unless there has been a fatal USB error */
//SYSTEM_ERROR
}
/* Switch context if necessary. */
if (xHigherPriorityTaskWoken)
{
taskYIELD ();
}
}
```
后续对字符串的处理参考示例代码中usb_console_main.c的实现。
USB交互相关的部分则对用户的输入进行解析,修改DDS_record中数据,数据修改完成后,向Flash存储任务发出信号量g_store_dds_semaphore,进行存储数据的更新。主要的代码menu_dds_record.c的dds_record_menu函数中。相关的代码在工程的dds_record_menu函数中。
# 实际测试
工程编译并下载后,效果运行的效果如下所示。能够对Flash中存储的数据进行读写管理。
相关工程代码
- 2024-12-07
-
发表了主题帖:
【Follow me第二季第三期】基础任务:quad spi、octo spi、dac和定时器使用
本帖最后由 EPTmachine 于 2024-12-7 11:09 编辑
基础任务:quad-spi flash和octo-spi flash配置及读写速度测试;DAC配置生成波形及性能测试;
评估板上提供了很多外设可供测试。借助瑞萨提供FSP(Flexiable Software Package)工具,可以配置外设和引脚的参数,生成相应的驱动代码,用户在此基础上能快速开放相应的应用代码开发和调试。本帖对板上的FLASH芯片读写性能和芯片的DAC性能进行测试。
任务涉及的外设有:
- RA6M5的quad spi
- RA6M5的octo spi
- 板载的Quand-SPI flash-MX25L25645G
- 板载的Octo-SPI Flash-MX25LM51245GM
- RA6M5的DAC
- RA6M5的定时器
## quad-spi flash配置及读写速度测试
在官方提供的ek-ra6m5-exampleprojects.zip中有很多例程可供参考,详情见附件。这里参考例程中的gpt和qspi来完成flahs的配置和读写速度测试。
在gpt的例程中,对通用定时器进行设置,配置为周期工作模式,同时配合Segger RTT组件实现上位机与开发板之间的交互。参考例程的设计,
### 添加Segger RTT模块实现上位机和开发板的交互
将gpt例程中的Segger RTT文件夹和common_utils.h头文件
添加到新建工程的src文件夹下,
在其他文件中包含common_utils.h头文件后,就可以使用Segger RTT的功能。
### 使用定时器进行时间测量
使用定时器的周期性中断功能,在中断发生时,进行时间技术,从而实现时间测量功能,用于计算某段代码的执行时间,从而计算出flash的读写速度。
在FSP的Stacks界面中添加gpt组件
设置定时器的参数,包括定时器工作模式、中断函数名、中断使能和优先级(设定优先级后默认使能该中断)
点击生成代码后,在工程中添加gpt_timer.c和gpt_timer.h,其中的代码如下,实现定时器的初始化、启动、Segger_RTT交互功能、定时器回调函数处理。
gpt_timer.h代码如下
```c
#ifndef GPT_TIMER_H_
#define GPT_TIMER_H_
/* Macros definitions */
#define GPT_MAX_PERCENT (100U) /* Max Duty Cycle percentage */
#define BUF_SIZE (16U) /* Size of buffer for RTT input data */
#define PERIODIC_MODE_TIMER (1U) /* To perform GPT Timer in Periodic mode */
#define INITIAL_VALUE ('\0')
#define TIMER_UNITS_MILLISECONDS (1000U) /* timer unit in millisecond */
#define CLOCK_TYPE_SPECIFIER (1ULL) /* type specifier */
/* GPT Timer Pin for boards */
#define TIMER_PIN (GPT_IO_PIN_GTIOCB)
#if defined (BOARD_RA2A1_EK) || defined (BOARD_RA4W1_EK)
#define GPT_MAX_PERIOD_COUNT (0XFFFF) /* Max Period Count for 16-bit Timer*/
#else
#define GPT_MAX_PERIOD_COUNT (0XFFFFFFFF) /* Max Period Count for 32-bit Timer*/
#endif
#define START_GPT (1U) /* To Set the period of GPT */
#define CLOSE_GPT (2U) /* To stop of GPT Timer */
#define QSPIWRITE_MODE (3U) /* To test the qspi flash write function*/
#define QSPIREAD_MODE (4U) /* To test the qspi flash read function*/
/* Function declaration */
fsp_err_t init_gpt_timer(timer_ctrl_t * const p_timer_ctl, timer_cfg_t const * const p_timer_cfg, uint8_t timer_mode);
fsp_err_t start_gpt_timer (timer_ctrl_t * const p_timer_ctl);
uint32_t process_input_data(void);
void deinit_gpt_timer(timer_ctrl_t * const p_timer_ctl);
void print_timer_menu(void);
uint32_t timer_GetRunTime(void);
#endif /* GPT_TIMER_H_ */
```
gpt_timer.c代码如下
```c
__IO uint32_t g_iRunTime = 0;
/* Store Timer open state*/
uint8_t g_timer_open_state = RESET_VALUE;
extern bsp_leds_t g_bsp_leds;
/* Holds level to set for pins */
bsp_io_level_t pin_level = BSP_IO_LEVEL_LOW;
/*******************************************************************************************************************//**
* @addtogroup r_gpt_ep
* @{
**********************************************************************************************************************/
/*****************************************************************************************************************
* @brief Initialize GPT timer.
* @param[in] p_timer_ctl Timer instance control structure
* @param[in] p_timer_cfg Timer instance Configuration structure
* @param[in] timer_mode Mode of GPT Timer
* @retval FSP_SUCCESS Upon successful open of timer.
* @retval Any Other Error code apart from FSP_SUCCES on Unsuccessful open .
****************************************************************************************************************/
fsp_err_t init_gpt_timer(timer_ctrl_t * const p_timer_ctl, timer_cfg_t const * const p_timer_cfg, uint8_t timer_mode)
{
fsp_err_t err = FSP_SUCCESS;
/* Initialize GPT Timer */
err = R_GPT_Open(p_timer_ctl, p_timer_cfg);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT ("\r\n ** R_GPT_TimerOpen FAILED ** \r\n");
return err;
}
if(PERIODIC_MODE_TIMER == timer_mode)
{
g_timer_open_state = START_GPT;
}
return err;
}
/*****************************************************************************************************************
* @brief Start GPT timers in periodic, one shot, PWM mode.
* @param[in] p_timer_ctl Timer instance control structure
* @retval FSP_SUCCESS Upon successful start of timer.
* @retval Any Other Error code apart from FSP_SUCCES on Unsuccessful start .
****************************************************************************************************************/
fsp_err_t start_gpt_timer (timer_ctrl_t * const p_timer_ctl)
{
fsp_err_t err = FSP_SUCCESS;
/* Starts GPT timer */
err = R_GPT_Start(p_timer_ctl);
if (FSP_SUCCESS != err)
{
/* In case of GPT_open is successful and start fails, requires a immediate cleanup.
* Since, cleanup for GPT open is done in start_gpt_timer,Hence cleanup is not required */
APP_ERR_PRINT ("\r\n ** R_GPT_Start API failed ** \r\n");
}
return err;
}
/*****************************************************************************************************************
* @brief Process input string to integer value
* @param[in] None
* @retval integer value of input string.
****************************************************************************************************************/
uint32_t process_input_data(void)
{
unsigned char buf[BUF_SIZE] = {INITIAL_VALUE};
uint32_t num_bytes = RESET_VALUE;
uint32_t value = RESET_VALUE;
while (RESET_VALUE == num_bytes)
{
if (APP_CHECK_DATA)
{
num_bytes = APP_READ(buf);
if (RESET_VALUE == num_bytes)
{
APP_PRINT("\r\nInvalid Input\r\n");
}
}
}
/* Conversion from input string to integer value */
value = (uint32_t) (atoi((char *)buf));
return value;
}
/*****************************************************************************************************************
* @brief Close the GPT HAL driver.
* @param[in] p_timer_ctl Timer instance control structure
* @retval None
****************************************************************************************************************/
void deinit_gpt_timer(timer_ctrl_t * const p_timer_ctl)
{
fsp_err_t err = FSP_SUCCESS;
/* Timer Close API call*/
err = R_GPT_Close(p_timer_ctl);
if (FSP_SUCCESS != err)
{
/* GPT Close failure message */
APP_ERR_PRINT ("\r\n ** R_GPT_Close FAILED ** \r\n");
}
}
/*****************************************************************************************************************
* @brief Print GPT Timer menu option.
* @param[in] None
* @retval None
****************************************************************************************************************/
void print_timer_menu(void)
{
APP_PRINT ("\r\nMenu Options");
APP_PRINT ("\r\nEnter 1 for Start and Set the GPT timer");
APP_PRINT ("\r\nEnter 2 for Stop the GPT timer");
APP_PRINT ("\r\nEnter 3 for Writing Data to QSPI Flash test");
APP_PRINT ("\r\nEnter 4 for Reading Data from QSPI Flash test");
APP_PRINT ("\r\nUser Input: ");
}
uint32_t timer_GetRunTime(void)
{
uint32_t runtime;
DISABLE_INT(); /* */
runtime = g_iRunTime; /* */
ENABLE_INT(); /* */
return runtime;
}
void timer_tick(timer_callback_args_t *p_args)
{
/* TODO: add your own code here */
if(NULL != p_args)
{
if (TIMER_EVENT_CYCLE_END == p_args->event)
{
R_IOPORT_PinWrite(&g_ioport_ctrl, (bsp_io_port_pin_t)g_bsp_leds.p_leds[1], pin_level);
if (BSP_IO_LEVEL_LOW == pin_level)
{
pin_level = BSP_IO_LEVEL_HIGH;
}
else
{
pin_level = BSP_IO_LEVEL_LOW;
}
g_iRunTime++;
if (g_iRunTime == 0x7FFFFFFF) /* the variable g_iRunTime expire 0x7FFFFFFF ,set it to 0*/
{
g_iRunTime = 0;
}
}
}
}
```
编译并烧写后完成后,在上位机打开J-Link RTT Viewer。在File->Connect界面设定参数如下所示:
其中RTT Control Block中地址需要在工程的Debug目录中的xxxx.map文件中寻找`_SEGGER_RTT`字段。
点击连接后可以在J-Link RTT Viewer的界面中看到提示用户输入的信息。
在菜单栏中设定输入的发送方式为按下Enter后发送
根据提示输入,即可调用不同的程序
### Quad Flash设置
在开发板上通过qspi接口连接了QSPI Flash。
在FSP的Stacks中添加qspi模块。
qspi模块的参数设置如下所示
通过查看芯片的用户手册可知,读模式设置为"Fast Read Quad I/O"后,QSPI控制器会自动生成相应的控制指令。这样节省了用户编程工作量,可以直接使用内存操作函数,memcpy、memcmp等,提高读写性能。
!
参考例程中的qspi flash初始化和数据操作,添加qspi flash的驱动代码如下。实现对Flash的初始化、存储块数据擦除、数据写入和读出操作。
qspi_drv.h代码如下:
```c
#ifndef QSPI_DRV_H_
#define QSPI_DRV_H_
#include "common_utils.h"
#include "hal_data.h"
/* QSPI flash page number to be written */
#define PAGE_FIRST (0U)
/* QSPI flash page Size */
#define PAGE_WRITE_SIZE (256U)
#define TEST_WRITE_SIZE (256U)
/* QSPI flash address through page*/
#define QSPI_FLASH_ADDRESS(page_no) ((uint8_t *) (QSPI_DEVICE_START_ADDRESS + ((page_no) * PAGE_WRITE_SIZE)))
/* Status register pay-load */
#define STATUS_REG_PAYLOAD {0x01,0x40,0x00}
/* data written to status register */
#define SET_SREG_VALUE (0x40)
/* sector size of QSPI flash device */
#define SECTOR_SIZE (4096U)
/* one byte data transfer */
#define ONE_BYTE (0x01)
/* SREG pay-load size */
#define SREG_SIZE (0x03)
/* default memory value */
#define DEFAULT_MEM_VAL (0xFF)
/* QPI mode exit command */
#define QSPI_MX25L_CMD_EXIT_QPI_MODE (0xF5)
/* QPI mode entry command */
#define QSPI_MX25L_CMD_ENTER_QPI_MODE (0x35)
//
#define RDID (0x9F)
#define MX_MANUFACTURER_ID (0xC2)
#define DEVICE_ID (0x18)
#define MEMORY_TYPE (0x20)
#define MEMORY_DENSITY (0x19)
//function declaration
void QSPI_FlashInit(void);
void QSPI_EraseSector(uint8_t * address);
fsp_err_t QSPI_WritePage(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize);
void QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize);
uint32_t QSPI_ReadID(void);
#endif /* QSPI_DRV_H_ */
```
qspi_drv.c的代码如下
```c
#include "qspi_drv.h"
/*******************************************************************************************************************//**
* @brief wait for QSPI flash device status register to get idle till operation is in progress
* @param[IN] None
* @retval FSP_SUCCESS or any other possible error codes
**********************************************************************************************************************/
static fsp_err_t get_flash_status(void)
{
spi_flash_status_t status = {.write_in_progress = true};
int32_t time_out = (INT32_MAX);
fsp_err_t err = FSP_SUCCESS;
do
{
/* Get status from QSPI flash device */
err = R_QSPI_StatusGet(&g_qspi_ctrl, &status);
if (FSP_SUCCESS!= err)
{
APP_ERR_PRINT("R_QSPI_StatusGet Failed\r\n");
return err;
}
/* Decrement time out to avoid infinite loop in case of consistent failure */
--time_out;
if ( RESET_VALUE >= time_out)
{
APP_PRINT("\r\n ** Timeout : No result from QSPI flash status register ** \r\n");
return FSP_ERR_TIMEOUT;
}
}while (false != status.write_in_progress);
return err;
}
/*******************************************************************************************************************//**
* @brief set QPI Mode in flash device and MCU
* @param[IN] none
* @retval FSP_SUCCESS or any other possible error codes
**********************************************************************************************************************/
static fsp_err_t qpi_mode_set(void)
{
fsp_err_t err = FSP_SUCCESS;
uint8_t data_qpi_en = QSPI_MX25L_CMD_ENTER_QPI_MODE;
APP_PRINT ("\r\n ** setting QPI mode: sending QPI enabling command byte to flash ** \r\n");
/* write enable once again section 9-1 states that
* we should do it before sending 0x35 to flash device
*/
err = R_QSPI_DirectWrite(&g_qspi_ctrl, &(g_qspi_cfg.write_enable_command), ONE_BYTE, false);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_DirectWrite Failed\r\n");
return err;
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("Failed to get status for QSPI operation\r\n");
return err;
}
}
/* send QPI mode enable command in flash device
* Note - no status register read after this operation
* because flash device has gone in QPI mode
* and MCU at this point is in extended SPI mode only.
* vice versa same is applicable while exiting QPI mode too.
*/
err = R_QSPI_DirectWrite(&g_qspi_ctrl, &data_qpi_en, ONE_BYTE, false);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_DirectWrite Failed\r\n");
return err;
}
APP_PRINT ("\r\n ** setting QPI mode: setting QPI mode in MCU ** \r\n");
/* Command byte transferred to flash-> NOW set the QPI protocol in MCU run time */
err = R_QSPI_SpiProtocolSet(&g_qspi_ctrl, SPI_FLASH_PROTOCOL_QPI);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_SpiProtocolSet Failed\r\n");
}
return err;
}
/*******************************************************************************************************************//**
* @brief Close QSPI module
* @param[IN] spi_protocol mode
* @retval None
**********************************************************************************************************************/
static void deinit_qspi(const spi_flash_protocol_t spi_protocol_mode)
{
fsp_err_t error = FSP_SUCCESS;
/* if QPI is active mode then Exit QPI mode from flash device before QSPI close */
if (SPI_FLASH_PROTOCOL_QPI == spi_protocol_mode)
{
uint8_t data_exit_qpi = QSPI_MX25L_CMD_EXIT_QPI_MODE;
APP_PRINT ("\r\n ** Exit QPI mode before Closing QSPI module ** \r\n");
error = R_QSPI_DirectWrite(&g_qspi_ctrl, &data_exit_qpi, ONE_BYTE, false);
if (FSP_SUCCESS != error)
{
APP_ERR_PRINT("R_QSPI_DirectWrite Failed\r\n");
}
}
APP_PRINT ("\r\n ** Closing QSPI module ** \r\n");
/* close QSPI module */
error = R_QSPI_Close(&g_qspi_ctrl);
if (FSP_SUCCESS != error)
{
APP_ERR_PRINT("R_QSPI_Close Failed\r\n");
}
APP_PRINT("\r\n\r\n *****############## demo ends here ########## *******\r\n\r\n");
}
void QSPI_FlashInit(void)
{
fsp_err_t err= FSP_SUCCESS;
uint8_t data_sreg[SREG_SIZE] = STATUS_REG_PAYLOAD;
if (SPI_FLASH_PROTOCOL_QPI == g_qspi_cfg.spi_protocol)
{
/*
* this needs to be done since QPI is set by user in configuration
* and it sets QPI only in MCU but not in flash device
* so as a system (MCU + QSPI flash device) QPI mode does not get set by
* simply calling only R_QSPI_Open in QPI mode.
* Rather QPI mode enabling has to be done in Flash device as well
* So opening the driver in extended SPI mode only
* and QPI mode is enabled when qpi_mode_set sub-function is called
*/
spi_flash_cfg_t l_qspi_cfg;
memcpy((spi_flash_cfg_t *)&l_qspi_cfg, (spi_flash_cfg_t *)&g_qspi_cfg, sizeof (spi_flash_cfg_t));
l_qspi_cfg.spi_protocol = SPI_FLASH_PROTOCOL_EXTENDED_SPI;
APP_PRINT ("\r\n ** user selected QPI Mode in RA configuration tool ** \r\n");
/* open QSPI with local configuration */
err = R_QSPI_Open(&g_qspi_ctrl, &l_qspi_cfg);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_Open Failed\r\n");
APP_ERR_TRAP(err);
}
}
else
{
APP_PRINT ("\r\n ** user selected extended SPI Mode in RA Configuration tool ** \r\n");
/* open QSPI in extended SPI mode */
err = R_QSPI_Open(&g_qspi_ctrl, &g_qspi_cfg);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_Open Failed\r\n");
APP_ERR_TRAP(err);
}
}
/* write enable for further operations */
err = R_QSPI_DirectWrite(&g_qspi_ctrl, &(g_qspi_cfg.write_enable_command), ONE_BYTE, false);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_DirectWrite Failed\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("Failed to get status for QSPI operation\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
}
/*
* write QSPI flash status register
* This is required to make sure the device is ready for general
* read write operation,
* This performs settings such as physical reset,WP hardware pin disable,
* block protection lock bits clearing.
* for more details please refer Mx25L data sheet.
*/
err = R_QSPI_DirectWrite(&g_qspi_ctrl, data_sreg, SREG_SIZE, false);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_DirectWrite Failed\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("Failed to get status for QSPI operation\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
}
/*
* Verifying data written to QSPI flash status register
* Step 1: - send command byte - 0x05
* through R_QSPI_DirectWrite with last argument set as true
* Step 2 - read data through R_QSPI_DirectRead
*/
uint8_t sreg_data = RESET_VALUE;
err = R_QSPI_DirectWrite(&g_qspi_ctrl, &(g_qspi_cfg.status_command), ONE_BYTE, true);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_DirectWrite Failed\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
/*
* we should not call function get_flash_status here
* because the CS line should not get interrupted between write read
*
* Also MCU is set as 0 when status register is read
* to resume in ROM access mode hence API direct read returns error as part
* of parameter check itself
*/
err = R_QSPI_DirectRead(&g_qspi_ctrl, &sreg_data, ONE_BYTE);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_DirectRead Failed\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
else
{
/* check for status check operation here */
err = get_flash_status();
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("Failed to get status for QSPI operation\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
}
/* verify read status register data */
if (SET_SREG_VALUE != sreg_data)
{
APP_ERR_PRINT("Failed to get value set in the status register \r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
if (SPI_FLASH_PROTOCOL_QPI == g_qspi_cfg.spi_protocol)
{
/* set QPI mode in flash and MCU device */
err = qpi_mode_set();
if (FSP_SUCCESS!=err)
{
APP_ERR_PRINT ("qpi_mode_set failed\r\n");
/* close QSPI module which is currently in extended SPI mode only */
deinit_qspi(SPI_FLASH_PROTOCOL_EXTENDED_SPI);
APP_ERR_TRAP(err);
}
}
}
void QSPI_EraseSector(uint8_t * address)
{
fsp_err_t err= FSP_SUCCESS;
err = R_QSPI_Erase(&g_qspi_ctrl, address, SECTOR_SIZE);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_Erase Failed\r\n");
deinit_qspi(g_qspi_cfg.spi_protocol);
APP_ERR_TRAP(err);
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("Failed to get status for QSPI operation\r\n");
deinit_qspi(g_qspi_cfg.spi_protocol);
APP_ERR_TRAP(err);
}
}
}
fsp_err_t QSPI_WritePage(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint16_t _usWriteSize)
{
fsp_err_t err= FSP_SUCCESS;
/* Write data to QSPI Flash */
err = R_QSPI_Write(&g_qspi_ctrl, _pBuf, (uint8_t *)QSPI_FLASH_ADDRESS(_uiWriteAddr), _usWriteSize);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("R_QSPI_Write Failed\r\n");
deinit_qspi(g_qspi_cfg.spi_protocol);
APP_ERR_TRAP(err);
}
else
{
err = get_flash_status();
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT("Failed to get status for QSPI operation\r\n");
deinit_qspi(g_qspi_cfg.spi_protocol);
APP_ERR_TRAP(err);
}
}
return err;
}
//Due to the qspi Read Mode is set to Fast Read Quad I/O , the flash is mapped to the memory region at 0x6000000
void QSPI_ReadBuffer(uint8_t * _pBuf, uint32_t _uiReadAddr, uint32_t _uiSize)
{
/* Read data from QSPI memory region */
memcpy(_pBuf, (uint8_t *)QSPI_FLASH_ADDRESS(_uiReadAddr), _uiSize);
}
uint32_t QSPI_ReadID(void)
{
fsp_err_t err = FSP_SUCCESS;
uint8_t write_data;
uint8_t read_data[8];
uint32_t uiID;
write_data = RDID; /* Read ID */
memset(read_data, 0, sizeof(read_data));
err = R_QSPI_DirectWrite(&g_qspi_ctrl, &write_data, 1, true);
if (FSP_SUCCESS != err)
{
return err;
}
err = R_QSPI_DirectRead(&g_qspi_ctrl, read_data, 3);
if (FSP_SUCCESS != err)
{
return err;
}
if ((MX_MANUFACTURER_ID != read_data[0]) || (MEMORY_TYPE != read_data[1]) || (MEMORY_DENSITY != read_data[2]))
{
return FSP_ERR_ASSERTION;
}
uiID=(read_data[0] = (uint32_t)(g_timer_cfg.source_div);
/* Convert period to PCLK counts so it can be set in hardware. */
period_counts = (uint64_t)((gpt_desired_period_ms * (pclkd_freq_hz * CLOCK_TYPE_SPECIFIER)) / TIMER_UNITS_MILLISECONDS);
/* Validate Period Count based on user input (time period in ms) */
if(GPT_MAX_PERIOD_COUNT < period_counts)
{
APP_ERR_PRINT ("\r\n ** INVALID INPUT, DESIRED PERIOD IS OUT OF RANGE. ** \r\n");
}
else
{
/* Check the status of GPT timer in Periodic mode */
if(START_GPT != g_timer_open_state)
{
/*Initialize Periodic Timer */
err = init_gpt_timer(&g_timer_ctrl, &g_timer_cfg, PERIODIC_MODE_TIMER);
if(FSP_SUCCESS != err)
{
APP_ERR_PRINT("** GPT TIMER INIT FAILED ** \r\n");
APP_ERR_TRAP(err);
}
APP_PRINT("Opened Timer in Periodic Mode\r\n");
/* Start Periodic Timer*/
err = start_gpt_timer(&g_timer_ctrl);
if(FSP_SUCCESS != err)
{
APP_ERR_PRINT("** GPT TIMER START FAILED ** \r\n");
/*Close Periodic Timer instance */
deinit_gpt_timer(&g_timer_ctrl);
APP_ERR_TRAP(err);
}
APP_PRINT("Started Timer in Periodic Mode\r\n");
}
else
{
APP_PRINT("Periodic Timer Already Started, Update Timer Period to : %d \r\n",gpt_desired_period_ms);
}
/* Period Set API set the desired period counts on the on-board LED */
err = R_GPT_PeriodSet(&g_timer_ctrl, (uint32_t)period_counts);
if (FSP_SUCCESS != err)
{
/* GPT Timer PeriodSet Failure message */
APP_ERR_PRINT ("\r\n ** R_GPT_PeriodSet API failed ** \r\n");
/*Close Periodic Timer instance */
deinit_gpt_timer(&g_timer_ctrl);
APP_ERR_TRAP(err);
}
}
break;
}
case CLOSE_GPT:
{
deinit_gpt_timer(&g_timer_ctrl);
APP_PRINT("\r\n GPT timer closed,g_iRunTime is %d\r\n",g_iRunTime);
g_iRunTime=0;
break;
}
case QSPIWRITE_MODE:
{
uint32_t iTime1, iTime2;
QSPI_EraseSector(p_mem_addr);
// Fill the data write buffer further to be written in QSPI flash device
for (uint16_t write_index = RESET_VALUE; write_index < TEST_WRITE_SIZE ; write_index++)
{
data_write[write_index] = (uint8_t)write_index;
}
APP_PRINT ("\r\nMeasure the time cost of qspi write\r\n");
iTime1=timer_GetRunTime();
for(uint16_t page_index = RESET_VALUE;page_index
- 2024-11-24
-
回复了主题帖:
【Follow me第二季第3期】1入门任务:搭建环境,下载调试示例程序,Blink,按键
bigbat 发表于 2024-11-24 15:19
UP的原理图能传一份吗?"ek-ra6m5-v1-designpackage.zip",
我的原理图是怎样滴,我也没 ...
好的
- 2024-11-23
-
发表了主题帖:
【Follow me第二季第3期】1入门任务:搭建环境,下载调试示例程序,Blink,按键
本帖最后由 EPTmachine 于 2024-11-24 21:33 编辑
# 入门任务:搭建环境,下载调试示例程序,Blink,按键
本次Follow Me活动的开发板是瑞萨的EK-RA6M5,在官网可以下载到如下的相关的资料。
官方的《EK-RA6M5 Quick Start Guide.pdf》可以帮助开发者快速了解套件的内容和使用方法。
套件包含了以下的内容
## 搭建调试环境
e2studio是瑞萨提供的集成开发环境,包含编译器、代码编辑器、工程管理工具、芯片支持包等。
首先在e2studio的[下载页面](https://www.renesas.cn/zh/software-tool/e-studio)选择和电脑系统匹配的安装包。
下载后解压得到安装包,点击应用程序开始安装。
在安装选项中选择RA系列支持和fsp版本即可。安装过程各个界面的选项如下。
选择安装RA系列支持
点击install开始安装。
安装完成后,就可以编辑和调试代码。
## 示例程序
针对EK-RA6M5,官方提供了丰富的示例代码和帮助文档。EK-RA6M5的产品页面提供了相应的资料和代码下载。
将下载的代码压缩包解压后,其中的示例代码如下所示:
通过菜单栏中的File->Import将工程导入到工作目录中
选择导入存在的工程到工作空间中。
选择工程所在的目录,在"Option"中勾选“Copy projects into workspace”将工程复制到工作空间中。
导入后项目栏中可以看到相应的工程。
在工程的右键选项中选择“Build Project”,完成后即可下载程序。
使用USB线连接电脑和开发板,点击调试按钮即可下载程序进行调试。
## Blink程序
查看“EK-RA6M5 User's Manual.pdf”,板载三个LED灯的相关信息如下图。
在e2studio中创建EK-RA6M5的工程控制LED闪烁。
在菜单栏中选择"File->New->C/C++ Project"
输入工程名
选择对应的开发套件或者根据芯片型号进行工程创建
剩下的选择默认项即可
在开发瑞萨的RA系列芯片时,需要使用FSP配置工具对外设进行参数设置,在工程栏中点击“configuration.xml”即可打开FSP配置界面。
在“FSP Configuration”界面中对硬件外设和软件组件包进行管理。
查看下载资料"ek-ra6m5-v1-designpackage.zip"中的原理图可知,板上的LED的接线如图所示,控制引脚为P006、P007、P008。
在FSP界面的Pins选项卡页面可以查看相关引脚的设置。
在FSP界面的Stacks选项卡页面可以查看应用中涉及到的功能栈。Blink程序只涉及到引脚状态控制,使用r_ioport模块即可实现功能。
点击右上角的"Generate Project Content"生成相关设置的配置代码。
生成的代码中,与配置相关的部分在文件夹ra_cfg中
接下来编写相关的应用的代码。由于FSP配置生成的代码中包含main函数,每次点击"Generate Project Content"生成代码时都会对main进行改写,所以用户无法直接编写main函数的代码,用户代码的
入口就变为,在不使用RTOS实时操作系统的前提下,在src文件夹下的hal_entry.c中的hal_entry函数为用户代码入口。
hal_entry.c添加实现Blink功能的代码如下:
``` c
#include "hal_data.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
extern bsp_leds_t g_bsp_leds;
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
/* Define the units to be used with the software delay function */
const bsp_delay_units_t bsp_delay_units = BSP_DELAY_UNITS_MILLISECONDS;
/* Set the blink frequency (must be channel)
{
g_sw2_press = true;
}
}
```
在hal_entry函数中,通过对全局变量`g_sw1_press`、`g_sw2_press`的状态进行判断,执行相应的处理逻辑,实现按键1控制LED1蓝灯,按键2控制LED2绿灯的闪烁频率。具体代码如下:
```c
#include "button.h"
/* Board's user LED */
extern bsp_leds_t g_bsp_leds;
/* Boolean flag to determine switch is pressed or not.*/
extern volatile bool g_sw1_press;
extern volatile bool g_sw2_press;
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
fsp_err_t err = FSP_SUCCESS;
blink_speed_t feq_level= slow_speed;
/* Define the units to be used with the software delay function */
const bsp_delay_units_t bsp_delay_units = BSP_DELAY_UNITS_MILLISECONDS;
/* the blink frequency,high 5h=Hz,normal 2Hz,slow 1Hz , (must be
-
加入了学习《FollowMe 第二季:3 - EK_RA6M5 开发板入门》,观看 EK-RA6M5 开发板入门
- 2024-11-03
-
加入了学习《【2024 DigiKey创意大赛】- 基于毫米波雷达的生命体征检测及健康监护系统》,观看 【2024 DigiKey创意大赛】- 基于毫米波雷达的生命体征检测及健康监护系统-作品提交
- 2024-03-01
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B技术介绍
-
加入了学习《泰克MSO6B探索营》,观看 MSO6B-360度介绍
- 2024-02-25
-
发表了主题帖:
【得捷Follow me第4期】PiCO_W5500以太网应用
### ****[[ 演示视频 ]](https://training.eeworld.com.cn/video/39289 "[ 演示视频 ]")****
Digikey和EEWorld联合举行的FollowMe第四期,本期使用的板卡是WIZnet推出的RP2040搭配W5500开发板PICO W5500。该评估板可用于以太网应用的开发和测试,在其[产品介绍页面](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico)详细对其板载的芯片种类、芯片数据手册、评估板原理图和应用示例进行了介绍。
在Github上有官方提供的代码示例可供参考:
- C/C++ Example
- [Ethernet Example](https://github.com/Wiznet/RP2040-HAT-C)
- [AWS Example](https://github.com/Wiznet/RP2040-HAT-AWS-C)
- [Azure Example](https://github.com/Wiznet/RP2040-HAT-AZURE-C)
- [Lwip Example](https://github.com/Wiznet/RP2040-HAT-LWIP-C)
- [FreeRTOS Example](https://github.com/Wiznet/RP2040-HAT-FREERTOS-C)
- [CircuitPython Example](https://github.com/Wiznet/RP2040-HAT-CircuitPython)
- [MicroPython Example](https://github.com/Wiznet/RP2040-HAT-MicroPython)
该评估版给出的资源还是十分丰富,从简单的基础示例到复杂的综合应用都有可以借鉴的代码,学习起来还是很方便的。
# 入门任务开发环境搭建、Blink和串口输出任务
本次活动使用MicroPython作为开发语言,使用的软件位Thonny。Thonny软件的安装很简单,安装选项使用默认即可。
完成安装后,在软件的配置选项中选择使用到的代码解释器类型为MicroPython(Raspberry Pi Pico)。
要想在开发板上运行MicroPython的代码,需要将MicroPython的固件烧写到开发板上。首先从[MicroPython W550_EVB_PICO](https://micropython.org/download/W5500_EVB_PICO/)的发布页面下载对应该开发板的MicroPython固件。
按住开发板上的BootSel按键并上电或者在上电状态下,按住BootSel按键的同时,按下Run按键,开发板会进入BootLoader模式,在电脑上会出现一个RPI-RP2的大容量存储设备。
将下载的MicroPython固件发送到该设备,会自动完成固件的烧写。打开Thonny软件,在右下角选择显示出来的开发板。
在Shell中使用`help('modules')`指令可以查看当前固件中支持的函数类。
编写Blink的代码如下,实现对开发板上的LED灯的控制
```python
from machine import Pin
import time
led = Pin(25, Pin.OUT)
def main():
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
if __name__ == "__main__":
main()
```
点击工具栏中的Run按键即可运行上述代码,如果想在上电时就运行上述代码,新建一个`main.py`的文件,并上传到设备(这里指的是的pico w5500开发板)的根目录中,这样上电后程序会自动运行Blink的程序。
可以使用`print`函数向Shell输出信息。以下代码实现发送信息的功能。
```python
from machine import Pin
import time
led = Pin(25, Pin.OUT)
def main():
while True:
led.value(1)
print('LED is on!\n')
time.sleep(1)
led.value(0)
print('LED is off!\n')
time.sleep(1)
if __name__ == "__main__":
main()
```
运行程序后,在Thonny的Shell串口可以看到发出的信息。
# 基础任务一:主控板W55500初始化(静态IP配置),ping指令使用和数据包抓取
通过使用help指令可以查看network中包含的类,其中的`WIZNET5K`实现了WIZnet芯片的接口函数。可以很方面地使用板上的W5500外设,实现互联网应用。
通过`import`指令导入以下库,用于开启socket支持、GPIO驱动、SPI驱动、网络函数以及时间函数
```python
from usocket import socket
from machine import Pin,SPI
import network
import time
```
以下代码为W5500的初始化代码,初始化片上的SPI外设以及W5500芯片后,配置网络接口并测试网咯连接是否成功建立,建立成功后,在Shell中输出网卡的配置信息。
```python
#W5x00 chip init
def w5x00_init():
spi=SPI(0,2_000_000, mosi=Pin(19),miso=Pin(16),sck=Pin(18))
nic = network.WIZNET5K(spi,Pin(17),Pin(20)) #spi,cs,reset pin
nic.active(True)
nic.ifconfig(('192.168.1.20','255.255.255.0','192.168.1.1','8.8.8.8'))
while not nic.isconnected():
time.sleep(1)
print(nic.regs())
print(nic.ifconfig())
```
接下来定义主函数如下,调用w5500芯片输出化函数,同时在main函数的循环中调用延时和GPIO翻转函数,用于提示系统正在运行。
```python
def main():
w5x00_init()
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
if __name__ == "__main__":
main()
```
文章[Python中`if __name__=='__main__':的作用](https://blog.csdn.net/heqiang525/article/details/89879056)对代码中的main函数调用规则进行了说明。
将上述代码整合到一起,在Thonny中打开文件并点击运行,可以看到Shell中输出了当前的开发板的静态IP配置信息。
在Windows中打开PowerShell,使用Ping指令检测与开发板的网络连接是否成功建立。
```Shell
ping 192.168.1.20
```
可以看到有以下的输出,说明W5500芯片正常运行,
使用wireshark抓取PC端的网络数据流,使用过滤条件`ip.addr=192.168.1.20`对抓取到数据进行过滤,可以得到ping指令的数据报,ping指令属于ICMP协议中的功能。
从结果中可以看到ping指令发送方和接收方的IP地址,以及其他的数据包信息。
# 基础任务二:主控板建立TCPIP服务器
该任务选择使用TCP功能进行实现服务器,Http应用使用的就是TCP协议,嵌入式的Web点灯是嵌入式终端作为HTTP Server,响应PC端的HTTP请求,从而实现网页控制板卡上的LED灯状态。
基础任务一实现了初始化W5500的函数和LED控制函数,在此基础上添加一个网页初始化函数,生成用于客户端显示的HTML网页。代码如下:
```python
def web_page():
if led.value()==1:
led_state="ON"
else:
led_state="OFF"
html = """
Raspberry Pi Pico Web server - WIZnet W5100S
Raspberry Pi Pico Web server & WIZnet Ethernet HAT
Control LED
PICO LED state: """ + led_state + """
ON
OFF
"""
return html
```
在主函数中创建一个socket,用于监听IP地址为192.168.1.20(这里为W5500的IP地址)网卡的端口80,在主循环中,在接收到PC端的客户端请求数据后,发送HTML数据,在接收到返回的数据后,检查其中是否有led_on或者led_off按钮对应的响应数据,据此改变板卡上LED灯的状态。
```python
def main():
w5x00_init()
s = socket()
s.bind(('192.168.1.20', 80))
s.listen(5)
while True:
conn, addr = s.accept()
print('Connect from %s' % str(addr))
request = conn.recv(1024)
request = str(request)
#print('Content = %s' % request)
led_on = request.find('/?led=on')
led_off = request.find('/?led=off')
if led_on == 6:
print("LED ON")
led.value(1)
if led_off == 6:
print("LED OFF")
led.value(0)
response = web_page()
conn.send('HTTP/1.1 200 OK\n')
conn.send('Connection: close\n')
conn.send('Content-Type: text/html\n')
conn.send('Content-Length: %s\n\n' % len(response))
conn.send(response)
conn.close()
if __name__ == "__main__":
main()
```
整合上述的代码并运行后,可以看到Shell中输出了相应的配置信息。
在PC端使用浏览器在对话框中输入`192.168.1.20`即可访问控制页面,点击页面上的按钮,即可实现对板卡上的LED灯控制。
使用WireShark捕获网口的通讯数据,添加`ip.addr=192.168.1.20`过滤出其中数据,可以看到传输的数据为TCP协议数据。
# 进阶任务:访问NTP服务器同步时间,获取时间并显示
NTP服务全称为Network Time Protocol,协议中定义了客户端和服务器两个用于数据交互的对象,客户端通过向服务器发送包含符合NTP协议要求的网络数据包,在服务器响应请求并返回相应的数据包之后,根据NTP协议解析其中的数据,即可得到需要的时间信息。
在Shell中输入`help('modules')`指令可以看到固件中已经包含了ntptime库,可以直接调用其中的ntp接口函数来获取当前的时间。
```python
import ntptime
import time
def sync_ntp():
ntptime.host = 'ntp1.aliyun.com'
ntptime.settime()
def main():
now = time.time()
t = time.localtime(now)
year, month, day, hour, minute, second, *_ = t
time_str = f"{year}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}"
print("时间:", time_str)
if __name__ == "__main__":
main()
```
时间的数据效果如下图所示。
# 终极任务:实现FTP服务器
FTP全称为[File Transfer Protocol](https://en.wikipedia.org/wiki/Network_Time_Protocol),用于在网络中实现服务器与客户端的文件传输。
在开发板上实现FTP服务器的应用,借鉴已有的MicroPython实现FTP协议的示例,完成FTP服务器的搭建。ftpServer.py如下所示:
```python
#
# Small ftp server for ESP8266 ans ESP32 Micropython
#
# Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky
#
# The server accepts passive mode only.
# It runs in foreground and quits, when it receives a quit command
# Start the server with:
#
# import ftp
#
# Copyright (c) 2016 Christopher Popp (initial ftp server framework)
# Copyright (c) 2016 Robert Hammelrath (putting the pieces together
# and a few extensions)
# Distributed under MIT License
#
import socket
import network
import uos
import gc
def send_list_data(path, dataclient, full):
try: # whether path is a directory name
for fname in sorted(uos.listdir(path), key=str.lower):
dataclient.sendall(make_description(path, fname, full))
except: # path may be a file name or pattern
pattern = path.split("/")[-1]
path = path[:-(len(pattern) + 1)]
if path == "":
path = "/"
for fname in sorted(uos.listdir(path), key=str.lower):
if fncmp(fname, pattern):
dataclient.sendall(make_description(path, fname, full))
def make_description(path, fname, full):
if full:
stat = uos.stat(get_absolute_path(path, fname))
file_permissions = ("drwxr-xr-x"
if (stat[0] & 0o170000 == 0o040000)
else "-rw-r--r--")
file_size = stat[6]
description = "{} 1 owner group {:>10} Jan 1 2000 {}\r\n".format(
file_permissions, file_size, fname)
else:
description = fname + "\r\n"
return description
def send_file_data(path, dataclient):
with open(path, "rb") as file:
chunk = file.read(512)
while len(chunk) > 0:
dataclient.sendall(chunk)
chunk = file.read(512)
def save_file_data(path, dataclient):
with open(path, "wb") as file:
chunk = dataclient.recv(512)
while len(chunk) > 0:
file.write(chunk)
chunk = dataclient.recv(512)
def get_absolute_path(cwd, payload):
# Just a few special cases "..", "." and ""
# If payload start's with /, set cwd to /
# and consider the remainder a relative path
if payload.startswith('/'):
cwd = "/"
for token in payload.split("/"):
if token == '..':
if cwd != '/':
cwd = '/'.join(cwd.split('/')[:-1])
if cwd == '':
cwd = '/'
elif token != '.' and token != '':
if cwd == '/':
cwd += token
else:
cwd = cwd + '/' + token
return cwd
# compare fname against pattern. Pattern may contain
# wildcards ? and *.
def fncmp(fname, pattern):
pi = 0
si = 0
while pi < len(pattern) and si < len(fname):
if (fname[si] == pattern[pi]) or (pattern[pi] == '?'):
si += 1
pi += 1
else:
if pattern[pi] == '*': # recurse
if (pi + 1) == len(pattern):
return True
while si < len(fname):
if fncmp(fname[si:], pattern[pi+1:]):
return True
else:
si += 1
return False
else:
return False
if pi == len(pattern.rstrip("*")) and si == len(fname):
return True
else:
return False
def ftpserver(net, port=21, timeout=None):
DATA_PORT = 13333
ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ftpsocket.bind(socket.getaddrinfo("0.0.0.0", port)[0][4])
datasocket.bind(socket.getaddrinfo("0.0.0.0", DATA_PORT)[0][4])
ftpsocket.listen(1)
ftpsocket.settimeout(timeout)
datasocket.listen(1)
datasocket.settimeout(None)
msg_250_OK = '250 OK\r\n'
msg_550_fail = '550 Failed\r\n'
addr = net.ifconfig()[0]
print("FTP Server started on ", addr)
try:
dataclient = None
fromname = None
do_run = True
while do_run:
cl, remote_addr = ftpsocket.accept()
cl.settimeout(300)
cwd = '/'
try:
# print("FTP connection from:", remote_addr)
cl.sendall("220 Hello, this is Micropython.\r\n")
while True:
gc.collect()
data = cl.readline().decode("utf-8").rstrip("\r\n")
if len(data) > 8,
DATA_PORT % 256))
dataclient, data_addr = datasocket.accept()
print("FTP Data connection from:", data_addr)
DATA_PORT = 13333
active = False
elif command == "PORT":
items = payload.split(",")
if len(items) >= 6:
data_addr = '.'.join(items[:4])
# replace by command session addr
if data_addr == "127.0.1.1":
data_addr = remote_addr
DATA_PORT = int(items[4]) * 256 + int(items[5])
dataclient = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
dataclient.settimeout(10)
dataclient.connect((data_addr, DATA_PORT))
print("FTP Data connection with:", data_addr)
cl.sendall('200 OK\r\n')
active = True
else:
cl.sendall('504 Fail\r\n')
elif command == "LIST" or command == "NLST":
if not payload.startswith("-"):
place = path
else:
place = cwd
try:
cl.sendall("150 Here comes the directory listing.\r\n")
send_list_data(place, dataclient,
command == "LIST" or payload == "-l")
cl.sendall("226 Listed.\r\n")
except:
cl.sendall(msg_550_fail)
if dataclient is not None:
dataclient.close()
dataclient = None
elif command == "RETR":
try:
cl.sendall("150 Opening data connection.\r\n")
send_file_data(path, dataclient)
cl.sendall("226 Transfer complete.\r\n")
except:
cl.sendall(msg_550_fail)
if dataclient is not None:
dataclient.close()
dataclient = None
elif command == "STOR":
try:
cl.sendall("150 Ok to send data.\r\n")
save_file_data(path, dataclient)
cl.sendall("226 Transfer complete.\r\n")
except:
cl.sendall(msg_550_fail)
if dataclient is not None:
dataclient.close()
dataclient = None
elif command == "DELE":
try:
uos.remove(path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "RMD" or command == "XRMD":
try:
uos.rmdir(path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "MKD" or command == "XMKD":
try:
uos.mkdir(path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
elif command == "RNFR":
fromname = path
cl.sendall("350 Rename from\r\n")
elif command == "RNTO":
if fromname is not None:
try:
uos.rename(fromname, path)
cl.sendall(msg_250_OK)
except:
cl.sendall(msg_550_fail)
else:
cl.sendall(msg_550_fail)
fromname = None
elif command == "MDTM":
try:
tm=localtime(uos.stat(path)[8])
cl.sendall('213 {:04d}{:02d}{:02d}{:02d}{:02d}{:02d}\r\n'.format(*tm[0:6]))
except:
cl.sendall('550 Fail\r\n')
elif command == "STAT":
if payload == "":
cl.sendall("211-Connected to ({})\r\n"
" Data address ({})\r\n"
"211 TYPE: Binary STRU: File MODE:"
" Stream\r\n".format(
remote_addr[0], addr))
else:
cl.sendall("213-Directory listing:\r\n")
send_list_data(path, cl, True)
cl.sendall("213 Done.\r\n")
else:
cl.sendall("502 Unsupported command.\r\n")
print("Unsupported command {} with payload {}".format(
command, payload))
except Exception as err:
print(err)
finally:
cl.close()
cl = None
except Exception as e:
print(e)
finally:
datasocket.close()
ftpsocket.close()
if dataclient is not None:
dataclient.close()
# ftpserver()
```
实际的运行效果,shell端的输出
在FTP客户端可以看到开发板上的文件系统中的文件,并可以对其进行操作。
# 总结
Follow Me本期使用的PICO_W5500开发板主要集中与以太网应用,ping指令学习了解到ICMP协议的作用以及wireshark的使用,HTTP应用了解到TCP协议的应用,NTP获取时间学习到服务器和客户端的数据通讯方式,FTP应用学习到网络中实现文件传输服务的方法。而且使用micropython已经编译好的固件,省去了开发底层驱动的烦恼,可以专注于TCP协议的学习,作为入门和学习的不二之选。
### [代码包](https://download.eeworld.com.cn/detail/EPTmachine/630302 "代码包")
- 2024-02-22
-
加入了学习《 【得捷电子Follow me第4期】》,观看 【得捷Follow me第4期】简易FTP文件服务器
-
加入了学习《 【得捷电子Follow me第4期】》,观看 【得捷Follow me第4期】入门任务:开发环境搭建
- 2024-02-20
-
加入了学习《直播回放: FollowMe 4 W5500-EVB-Pico 使用入门》,观看 W5500-EVB-Pico 使用入门
- 2024-02-03
-
回复了主题帖:
健康守护礼的入围名单:脉搏监测健康仪,测出“不觉得”(测评邀请券主场活动)
个人信息确认无误
- 2023-12-28
-
加入了学习《ADI - 世健 工业嘉年华》,观看 ADI - 世健 工业嘉年华 开幕致辞