不语arc

  • 2025-02-14
  • 回复了主题帖: 【MCXA156开发板测评】LPI2C pk 模拟I2C,孰强孰弱?

    本帖最后由 不语arc 于 2025-2-14 14:35 编辑 freebsder 2025-2-14 14:23 嗯嗯,对比参考一下。模拟好移植,有些开发板没有硬件i2c,或者设计存在坑,这种情况下使用模拟i2c还是很方便的

  • 回复了主题帖: 【MCXA156开发板测评】LPI2C pk 模拟I2C,孰强孰弱?

    lugl4313820 发表于 2025-2-14 13:44 有个计算fps的程序,然后在线展示,那就牛了。 哈哈哈哈哈,建议不错

  • 回复了主题帖: 【MCXA156开发板测评】LPI2C外设驱动OLED

    本帖最后由 不语arc 于 2025-2-14 08:58 编辑 humancat01 2025-2-14 07:06 оùоо 我也支持国产芯片,这和学习国外优秀的芯片不冲突。参考对比能让技术视野更开阔,也更能督促国产芯片的进步

  • 回复了主题帖: 【MCXA156开发板测评】LPTMR初体验

    Jacktang 2025-2-14 07:48 LPTMR 是的,配置比较常规

  • 2025-02-13
  • 发表了主题帖: 【MCXA156开发板测评】LPI2C pk 模拟I2C,孰强孰弱?

    本帖最后由 不语arc 于 2025-2-14 14:31 编辑 1. 背景 在前面已经分别实现了模拟I2C和LPI2C对oled的驱动,同样使用i2c协议,哪种性能速度更快呢? 在本篇将以oled的刷写速度进行对比。   2. 实验安排 配置好1ms中断,分别安排硬件I2C和模拟I2C执行相同的任务:对oled进行十次刷屏,通过比较总耗时来判断速度。 代码如下: /* * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016-2017 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_debug_console.h" #include "board.h" #include "app.h" #include "peripherals.h" #include "oled.h" #include "fsl_lptmr.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ volatile uint32_t lptmrCounter = 0U; /******************************************************************************* * Code ******************************************************************************/ void LPTMR_LED_HANDLER(void) { LPTMR_ClearStatusFlags(DEMO_LPTMR_BASE, kLPTMR_TimerCompareFlag); lptmrCounter++; /* * Workaround for TWR-KV58: because write buffer is enabled, adding * memory barrier instructions to make sure clearing interrupt flag completed * before go out ISR */ __DSB(); __ISB(); } /*! * @brief Main function */ int main(void) { uint32_t start_cnt, end_cnt; uint32_t total_time_ms = 0; u8 test_rounds = 20; // 测试轮数 /* Board pin, clock, debug console init */ BOARD_InitHardware(); LED_INIT(); BOARD_InitBootPeripherals(); OLED_Init(); OLED_Clear(); PRINTF("Low Power Timer Example\r\n"); u8 color = 1; for (u8 i = 0; i < test_rounds; i++) { start_cnt = lptmrCounter; OLED_FillScreen(color); color = !color; end_cnt = lptmrCounter; total_time_ms += (end_cnt - start_cnt); } PRINTF("total_time_ms = %d\r\n", total_time_ms); while (1) { } } 3. 实验结果     表格解释: 第一列分别对应了硬件I2C和模拟I2C两组实验。 第二列对应了编译优化选项。LPI2C默认使用o1优化,而在模拟i2c中使用o1优化会造成时序错乱。这是一个坑。(GPIO 模拟 I2C 时序需要严格的时间控制。编译器优化可能会改变代码的执行顺序或删除某些看似“无用”的代码(如空循环或延时函数),从而导致时序不准确。) 第三列是i2c频率,在硬件i2c中通过更改配置,可以很方便改变频率。模拟i2c并未找到配置gpio速度的参数(可能存在,这里使用的是默认值) 第四列是执行相同刷屏任务的耗时。单位为ms   结果分析: 在硬件i2c中,从100khz到1.5Mhz,频率的改变可以很明显地发现,耗时几乎成反比。同时多次实验总耗时的具体数值完全不变,这证明了LPI2C模块的精确性! 而硬件i2c的频率设置也存在上限,拉高到1.6Mhz时,与1.5Mhz几户没有变化。而再拉高频率开发板会进入错误状态,无法执行任务。 在同样的编译优化o0情况下,模拟i2c的速度稍微比硬件i2c的400k速度慢一点点。 总之,LPI2C模块非常精准,速度超快,满足绝大多数i2c场景。   4.实验过程展示 o1优化,硬件i2c场景: [localvideo]d35b95cba7425c911d8c41f990cd1bcb[/localvideo] o0优化,模拟i2c场景: [localvideo]8c0c87b88ee326443b422ba236046750[/localvideo]  

  • 发表了主题帖: 【MCXA156开发板测评】LPTMR初体验

    本帖最后由 不语arc 于 2025-2-14 08:52 编辑 参考:【MCXA156开发板测评】-3-LPTMR体验 - NXP MCU - 电子工程世界-论坛 1. LPTMR模块 为了比较oled的刷屏速度,需要用到定时器的计数功能,发现nxp的定时器与PWM两种模块,由于只用到计数功能,因此先体验TPTMR模块,参考上述链接的大佬配置了一下。 LPTMR模块能够以极低的功耗运行,即使在系统处于低功耗模式时也能保持工作状态,这使得它非常适合用于需要定时唤醒或周期性事件触发的应用场景。其配置为一个16位的时间计数器或脉冲计数器。   2. config配置 打开sdk实例程序lptmr,以此作为基础。   时钟配置:   LPTMR模块配置:周期1ms(注意第一个框中的时钟源)、开启中断     更新 自动生成代码: const lptmr_config_t LPTMR0_config = { .timerMode = kLPTMR_TimerModeTimeCounter, .pinSelect = kLPTMR_PinSelectInput_0, .pinPolarity = kLPTMR_PinPolarityActiveHigh, .enableFreeRunning = false, .bypassPrescaler = true, .prescalerClockSource = kLPTMR_PrescalerClock_3, .value = kLPTMR_Prescale_Glitch_0 }; static void LPTMR0_init(void) { /* Initialize the LPTMR */ LPTMR_Init(LPTMR0_PERIPHERAL, &LPTMR0_config); /* Set LPTMR period */ LPTMR_SetTimerPeriod(LPTMR0_PERIPHERAL, LPTMR0_TICKS); /* Configure timer interrupt */ LPTMR_EnableInterrupts(LPTMR0_PERIPHERAL, kLPTMR_TimerInterruptEnable); /* Interrupt vector LPTMR0_IRQn priority settings in the NVIC. */ NVIC_SetPriority(LPTMR0_IRQN, LPTMR0_IRQ_PRIORITY); /* Enable interrupt LPTMR0_IRQN request in the NVIC */ EnableIRQ(LPTMR0_IRQN); /* Start the LPTMR timer */ LPTMR_StartTimer(LPTMR0_PERIPHERAL); } /*********************************************************************************************************************** * Initialization functions **********************************************************************************************************************/ void BOARD_InitPeripherals(void) { /* Initialize components */ LPTMR0_init(); } /*********************************************************************************************************************** * BOARD_InitBootPeripherals function **********************************************************************************************************************/ void BOARD_InitBootPeripherals(void) { BOARD_InitPeripherals(); } 更新源代码后,配置更改在peripherals.c文件中并且不在工程中,因此需要先将该文件添加到工程的board目录下,再在主程序文件lptmr.c的初始化部分调用BOARD_InitBootPeripherals();函数。   3.应用程序 每隔一秒prtinf打印一次,并翻转led /* * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016-2017 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_debug_console.h" #include "board.h" #include "app.h" #include "peripherals.h" #include "fsl_lptmr.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ //volatile uint32_t lptmrCounter = 0U; /******************************************************************************* * Code ******************************************************************************/ void LPTMR_LED_HANDLER(void) { static uint32_t lptmrCounter = 0; LPTMR_ClearStatusFlags(DEMO_LPTMR_BASE, kLPTMR_TimerCompareFlag); lptmrCounter++; if(lptmrCounter%1000 == 0){ PRINTF("lptmrCounter = %d\r\n", lptmrCounter); LED_TOGGLE(); } /* * Workaround for TWR-KV58: because write buffer is enabled, adding * memory barrier instructions to make sure clearing interrupt flag completed * before go out ISR */ __DSB(); __ISB(); } /*! * [url=home.php?mod=space&uid=159083]@brief[/url] Main function */ int main(void) { uint32_t currentCounter = 0U; /* Board pin, clock, debug console init */ BOARD_InitHardware(); LED_INIT(); BOARD_InitBootPeripherals(); PRINTF("Low Power Timer Example\r\n"); while (1) { } } 4. 效果展示 [localvideo]6146161d7c239d1473af692acfc14e40[/localvideo]  

  • 回复了主题帖: 【MCXA156开发板测评】-3-LPTMR体验

    谢谢分享,参考配置了

  • 回复了主题帖: 【MCXN947开发板测评】Coremark内核测试

    微处理器居然也能跑分,学到了

  • 发表了主题帖: 【MCXA156开发板测评】LPI2C外设驱动OLED

    本帖最后由 不语arc 于 2025-2-13 15:02 编辑 1. 硬件I2C 在上一篇分享中,使用GPIO模拟I2C实现了对OLED的驱动,在本篇中,我将利用A156强大的硬件I2C外设完成对OLED的驱动。 MCXA156芯片有四个低功耗I2C模块,分别是LPI2C0,LPI2C1,LPI2C2,LPI2C3。每个LPI2C模块通过一对控制信号和数据信号支持串行I2C通信,并且可以作为控制器或目标设备运行。 在本实验中,选择使用LPI2C0模块。   2. config配置 勾选LPI2C0模块       3. 代码 3.1 核心函数:LPI2C发送一字节数据 /* I2C传出一字节数据函数: Mode : 数据/命令标识符。 0表示命令,1表示数据(OLED的数据地址一般为0x40;指令地址为0x00) *Data : 要写入的数据 */ void OLED_WR_Byte(uint8_t Data, uint8_t Mode) { lpi2c_master_transfer_t xfer = {0}; /*Start Transfer*/ xfer.data = (uint8_t *)&Data; xfer.dataSize = sizeof(Data); xfer.flags = kLPI2C_TransferDefaultFlag; xfer.slaveAddress = SSD1306_ADDRESS; xfer.direction = kLPI2C_Write; if (Mode) { xfer.subaddress = 0x40; } else { xfer.subaddress = 0x00; } xfer.subaddressSize = 1; LPI2C_MasterTransferBlocking(LPI2C0_PERIPHERAL, &xfer); }     3.2 主函数 /* * Copyright 2017 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /* Standard C Included Files */ #include <stdio.h> #include <string.h> #include "board.h" #include "fsl_debug_console.h" #include "fsl_lpi2c.h" #include "app.h" #include "oled.h" /******************************************************************************* * Definitions ******************************************************************************/ #define EXAMPLE_I2C_MASTER ((LPI2C_Type *)EXAMPLE_I2C_MASTER_BASE) #define LPI2C_BAUDRATE 100000U /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ /******************************************************************************* * Code ******************************************************************************/ /*! * [url=home.php?mod=space&uid=159083]@brief[/url] Main function */ int main(void) { lpi2c_master_config_t masterConfig; BOARD_InitHardware(); LPI2C_MasterGetDefaultConfig(&masterConfig); /* Change the default baudrate configuration */ masterConfig.baudRate_Hz = LPI2C_BAUDRATE; /* Initialize the LPI2C master peripheral */ LPI2C_MasterInit(EXAMPLE_I2C_MASTER, &masterConfig, LPI2C_MASTER_CLOCK_FREQUENCY); OLED_Init(); OLED_Clear(); OLED_8x16(0, 0, "hello, eeWorld"); while (1) { } } 3.3 oled.c #include "oled.h" #include "DEV_OLED_8x16.h" /* OLED显存的映射数组,存放格式如下: //[0]0 1 2 3 ... 127 //[1]0 1 2 3 ... 127 //[2]0 1 2 3 ... 127 //[3]0 1 2 3 ... 127 //[4]0 1 2 3 ... 127 //[5]0 1 2 3 ... 127 //[6]0 1 2 3 ... 127 //[7]0 1 2 3 ... 127 */ u8 OLED_GRAM[128][8]; /* I2C传出一字节数据函数: Mode : 数据/命令标识符。 0表示命令,1表示数据(OLED的数据地址一般为0x40;指令地址为0x00) *Data : 要写入的数据 */ void OLED_WR_Byte(uint8_t Data, uint8_t Mode) { lpi2c_master_transfer_t xfer = {0}; /*Start Transfer*/ xfer.data = (uint8_t *)&Data; xfer.dataSize = sizeof(Data); xfer.flags = kLPI2C_TransferDefaultFlag; xfer.slaveAddress = SSD1306_ADDRESS; xfer.direction = kLPI2C_Write; if (Mode) { xfer.subaddress = 0x40; } else { xfer.subaddress = 0x00; } xfer.subaddressSize = 1; LPI2C_MasterTransferBlocking(LPI2C0_PERIPHERAL, &xfer); } //开启OLED显示 void OLED_DisPlay_On(void) { OLED_WR_Byte(0x8D, OLED_CMD);//电荷泵使能 OLED_WR_Byte(0x14, OLED_CMD);//开启电荷泵 OLED_WR_Byte(0xAF, OLED_CMD);//点亮屏幕 } //关闭OLED显示 void OLED_DisPlay_Off(void) { OLED_WR_Byte(0x8D, OLED_CMD);//电荷泵使能 OLED_WR_Byte(0x10, OLED_CMD);//关闭电荷泵 OLED_WR_Byte(0xAE, OLED_CMD);//关闭屏幕 } //缓冲区写法。更新显存到OLED的GRAM中,此函数仅在OLED_Clear()函数中使用了。 void OLED_Refresh(void) { uint8_t i, n; // 遍历所有页 (0-7) for(i = 0; i < 8; i++) { // 设置页地址 OLED_WR_Byte(0xB0 + i, OLED_CMD); // 每次递增页地址 // 设置列地址低字节 (固定为0x00) OLED_WR_Byte(0x00, OLED_CMD); // 设置列地址高字节 (固定为0x10) OLED_WR_Byte(0x10, OLED_CMD); // 发送当前页的数据 for(n = 0; n < 128; n++) { OLED_WR_Byte(OLED_GRAM[n], OLED_DATA); } } } void OLED_Clear(void) { memset(OLED_GRAM, 0, sizeof(OLED_GRAM)); OLED_Refresh(); } //OLED页寻址方式,x表示长(0-127),y表示页(0-7) void OLED_Setxy(u8 x, u8 y) { OLED_WR_Byte(0xb0 + y, OLED_CMD); OLED_WR_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD); OLED_WR_Byte((x & 0x0f) | 0x01, OLED_CMD); } //显示字符串函数 //x:0-127 y:0-7 void OLED_8x16(u8 x, u8 y, u8 ch[]) { u8 c = 0, i = 0, j = 0; while (ch[j] != '\0') { c = ch[j] - 32;//32为space的asiic码,c为偏移量 if (x > 120) //超出边界判断 { x = 0; y++; } OLED_Setxy(x, y); //字符取模方式:先横后纵 for (i = 0; i < 8; i++) //一个字的宽为8个像素 OLED_WR_Byte(F8X16[c * 16 + i], OLED_DATA); OLED_Setxy(x, y + 1); for (i = 0; i < 8; i++) OLED_WR_Byte(F8X16[c * 16 + i + 8], OLED_DATA); x += 8; j++; } } //OLED的初始化 void OLED_Init(void) { OLED_WR_Byte(0xAE, OLED_CMD);//--turn off oled panel OLED_WR_Byte(0x00, OLED_CMD);//---set low column address OLED_WR_Byte(0x10, OLED_CMD);//---set high column address OLED_WR_Byte(0x40, OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F) OLED_WR_Byte(0x81, OLED_CMD);//--set contrast control register OLED_WR_Byte(0xCF, OLED_CMD);// Set SEG Output Current Brightness OLED_WR_Byte(0xA1, OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常 OLED_WR_Byte(0xC8, OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常 OLED_WR_Byte(0xA6, OLED_CMD);//--set normal display OLED_WR_Byte(0xA8, OLED_CMD);//--set multiplex ratio(1 to 64) OLED_WR_Byte(0x3f, OLED_CMD);//--1/64 duty OLED_WR_Byte(0xD3, OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F) OLED_WR_Byte(0x00, OLED_CMD);//-not offset OLED_WR_Byte(0xd5, OLED_CMD);//--set display clock divide ratio/oscillator frequency OLED_WR_Byte(0x80, OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec OLED_WR_Byte(0xD9, OLED_CMD);//--set pre-charge period OLED_WR_Byte(0xF1, OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock OLED_WR_Byte(0xDA, OLED_CMD);//--set com pins hardware configuration OLED_WR_Byte(0x12, OLED_CMD); OLED_WR_Byte(0xDB, OLED_CMD);//--set vcomh OLED_WR_Byte(0x40, OLED_CMD);//Set VCOM Deselect Level OLED_WR_Byte(0x20, OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02) OLED_WR_Byte(0x02, OLED_CMD);// OLED_WR_Byte(0x8D, OLED_CMD);//--set Charge Pump enable/disable OLED_WR_Byte(0x14, OLED_CMD);//--set(0x10) disable OLED_WR_Byte(0xA4, OLED_CMD);// Disable Entire Display On (0xa4/0xa5) OLED_WR_Byte(0xA6, OLED_CMD);// Disable Inverse Display On (0xa6/a7) OLED_Clear(); OLED_WR_Byte(0xAF, OLED_CMD); } 4. 效果演示  

  • 2025-02-12
  • 回复了主题帖: 【MCXA156开发板测评】模拟I2C驱动 OLED屏

    本帖最后由 不语arc 于 2025-2-12 14:39 编辑 jobszheng5 2025-2-12 10:15 I2CЭ鶼IO GPIO好移植,硬件I2C再试试进行对比

  • 2025-02-11
  • 发表了主题帖: 【MCXA156开发板测评】模拟I2C驱动 OLED屏

    本帖最后由 不语arc 于 2025-2-11 20:50 编辑 1. 模拟I2C 模拟I2C也就是使用GPIO管脚来模拟实现I2C时序,完成对I2C设备的驱动。相较于核心板自带的硬件I2C,模拟I2C有利有弊, 优点在于灵活性高,可以在任何支持GPIO的微控制器上实现,移植方便,不依赖硬件I2C外设,可以自己定义时序,适应不同的设备需求,引脚选择也不受限制。 缺点在于占用CPU资源,由于是软件模拟,一方面速度较慢,另一方面时序精度不足,需要手动实现I2C协议的起始、停止、ACK、数据读写增加了代码量工作。   2. I2C协议 I2C使用两条双向开漏线或开集线进行通信:串行数据线(SDA)和串行时钟线(SCL)。使用主从架构:通信由一个或多个主机控制,这些主机启动与从设备的数据传输。每个从设备都有一个唯一的地址,主机通过这个地址来指定要与之通信的从设备。有三种速率:标准模式下速率为100 kbit/s,快速模式下可达400 kbit/s,高速模式下可达3.4 Mbit/s。   时序图:     3. GPIO初始化 分别使用P1_0、P1_2作为SCL和SDA   gpio_pin_config_t gpio1_pin91_config = { .pinDirection = kGPIO_DigitalOutput, .outputLogic = 0U }; /* Initialize GPIO functionality on pin PIO1_0 (pin 91) */ GPIO_PinInit(GPIO1, 0U, &gpio1_pin91_config); gpio_pin_config_t gpio1_pin93_config = { .pinDirection = kGPIO_DigitalOutput, .outputLogic = 0U }; /* Initialize GPIO functionality on pin PIO1_2 (pin 93) */ GPIO_PinInit(GPIO1, 2U, &gpio1_pin93_config); 使用GPIO_PinInit 函数初始化了以下 GPIO 引脚: GPIO1_0 (pin 91):配置为输出,初始状态为低电平。 GPIO1_2 (pin 93):配置为输出,初始状态为低电平。 /* PORT1_0 (pin 91) is configured as P1_0, LPTMR0_ALT3, WUU0_IN6 */ PORT_SetPinMux(PORT1, 0U, kPORT_MuxAlt0); PORT1->PCR[0] = ((PORT1->PCR[0] & /* Mask bits to zero which are setting */ (~(PORT_PCR_PS_MASK | PORT_PCR_PE_MASK | PORT_PCR_ODE_MASK | PORT_PCR_DSE_MASK | PORT_PCR_IBE_MASK))) /* Pull Select: Enables internal pullup resistor. */ | PORT_PCR_PS(PCR_PS_ps1) /* Pull Enable: Enables. */ | PORT_PCR_PE(PCR_PE_pe1) /* Open Drain Enable: Enables. */ | PORT_PCR_ODE(PCR_ODE_ode1) /* Drive Strength Enable: High. */ | PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables. */ | PORT_PCR_IBE(PCR_IBE_ibe1)); /* PORT1_2 (pin 93) is configured as P1_2 */ PORT_SetPinMux(PORT1, 2U, kPORT_MuxAlt0); PORT1->PCR[2] = ((PORT1->PCR[2] & /* Mask bits to zero which are setting */ (~(PORT_PCR_PS_MASK | PORT_PCR_PE_MASK | PORT_PCR_ODE_MASK | PORT_PCR_DSE_MASK | PORT_PCR_IBE_MASK))) /* Pull Select: Enables internal pullup resistor. */ | PORT_PCR_PS(PCR_PS_ps1) /* Pull Enable: Enables. */ | PORT_PCR_PE(PCR_PE_pe1) /* Open Drain Enable: Enables. */ | PORT_PCR_ODE(PCR_ODE_ode1) /* Drive Strength Enable: High. */ | PORT_PCR_DSE(PCR_DSE_dse1) /* Input Buffer Enable: Enables. */ | PORT_PCR_IBE(PCR_IBE_ibe1)); 通过直接操作寄存器 PORT1->PCR[x] 配置了以下特性: 上拉/下拉电阻:启用上拉电阻。 开漏输出:启用开漏输出模式。 驱动强度:配置引脚为高驱动驱动能力。 4. I2C协议实现 4.1 宏定义 /* 定义I2C总线连接的GPIO端口 */ #define OLED_I2C_GPIO_PORT GPIO0 /* GPIO端口 */ #define OLED_I2C_SCL_PIN 0U /* 连接到SCL时钟线的GPIO */ #define OLED_I2C_SDA_PIN 2U /* 连接到SDA数据线的GPIO */ //-----------------OLED引脚宏定义---------------- #define OLED_SCL_Clr() GPIO_PortClear(OLED_I2C_GPIO_PORT,OLED_I2C_SCL_PIN)//SCL #define OLED_SCL_Set() GPIO_PortSet(OLED_I2C_GPIO_PORT,OLED_I2C_SCL_PIN) #define OLED_SDA_Clr() GPIO_PortClear(OLED_I2C_GPIO_PORT,OLED_I2C_SDA_PIN)//SDA #define OLED_SDA_Set() GPIO_PortSet(OLED_I2C_GPIO_PORT,OLED_I2C_SDA_PIN) #define OLED_CMD 0 //指令 #define OLED_DATA 1 //数据 4.2 起始信号 //起始信号 void I2C_Start(void) { OLED_SDA_Set(); OLED_SCL_Set(); IIC_delay(); OLED_SDA_Clr(); IIC_delay(); OLED_SCL_Clr(); IIC_delay(); } 4.3 结束信号 //结束信号 void I2C_Stop(void) { OLED_SDA_Clr(); OLED_SCL_Set(); IIC_delay(); OLED_SDA_Set(); } 4.4 等待ack信号    void I2C_WaitAck(void) { OLED_SDA_Set(); IIC_delay(); OLED_SCL_Set(); IIC_delay(); OLED_SCL_Clr(); IIC_delay(); } 4.5 通过I2C发送一字节函数     //发送一个字节 void Send_Byte(u8 dat) { u8 i; for (i = 0;i < 8;i++) { if (dat & 0x80)//将data的8位依次写入 { OLED_SDA_Set(); } else { OLED_SDA_Clr(); } IIC_delay(); OLED_SCL_Set(); IIC_delay(); OLED_SCL_Clr();//将时钟信号设置为低电平 dat <<= 1; } } 4.6 写一字节数据/命令到OLED中 流程:先发送从机地址(0.96寸oled的主控芯片SSD1306的从机地址为0x78),再发送数据/命令对应的指令,最后发送具体data。 //将一个字节写入OLED中 //mode:数据/命令标识符;0表示命令,1表示数据 void OLED_WR_Byte(u8 dat, u8 mode) { I2C_Start(); Send_Byte(0x78); I2C_WaitAck(); if (mode) { Send_Byte(0x40); } else { Send_Byte(0x00); } I2C_WaitAck(); Send_Byte(dat); I2C_WaitAck(); I2C_Stop(); } 5. 应用函数,在oled上显示hello eeWorld! //OLED页寻址方式,x表示长(0-127),y表示页(0-7) void OLED_Setxy(u8 x, u8 y) { OLED_WR_Byte(0xb0 + y, OLED_CMD); OLED_WR_Byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD); OLED_WR_Byte((x & 0x0f) | 0x01, OLED_CMD); } //显示字符串函数 //x:0-127 y:0-7 void OLED_8x16(u8 x, u8 y, u8 ch[]) { u8 c = 0, i = 0, j = 0; while (ch[j] != '\0') { c = ch[j] - 32;//32为space的asiic码,c为偏移量 if (x > 120) //超出边界判断 { x = 0; y++; } OLED_Setxy(x, y); //字符取模方式:先横后纵 for (i = 0; i < 8; i++) //一个字的宽为8个像素 OLED_WR_Byte(F8X16[c * 16 + i], OLED_DATA); OLED_Setxy(x, y + 1); for (i = 0; i < 8; i++) OLED_WR_Byte(F8X16[c * 16 + i + 8], OLED_DATA); x += 8; j++; } } 在主函数中,while(1)之前,调用函数 OLED_8x16(0, 0, "hello, eeWorld!");   6. 效果演示  

  • 回复了主题帖: 【MCXA156开发板测评】串口通讯及vofa+上位机配置

    秦天qintian0303 发表于 2025-2-11 09:22 MCXA156微控制器拥有五个低功耗通用异步收发器(LPUART)模块:LPUART0、LPUART1、LPUART2、LPUART3和LPUAR ... 是的,引出了不过 LPURAT3和LPUART4被复用成了FlexIO / LCD模块针脚

  • 2025-02-10
  • 发表了主题帖: 【MCXA156开发板测评】串口通讯及vofa+上位机配置

    本帖最后由 不语arc 于 2025-2-10 20:01 编辑  1.LPUART资源 MCXA156微控制器拥有五个低功耗通用异步收发器(LPUART)模块:LPUART0、LPUART1、LPUART2、LPUART3和LPUART4。然而,FRDM-MCXA156开发板仅支持与LPUART0、LPUART1和LPUART2模块之间的通信。下图展示了FRDM-MCXA156的LPUART结构图。   LPUART0模块被称为MCU-Link,可以作为USB到UART桥使用,通过虚拟通信(VCOM)端口调试MCXA156。 LPUART1是一个拥有8个位置的mikroBUS插座连接器,允许插入的mikroBUS click板通过UART连接与MCXA156 MCU进行通信。 LPUART2是一个双排各8个位置的Arduino插座连接器,允许插入的Arduino板通过UART连接与MCXA156 MCU进行通信。   因此选择开发板预留的LPUART0作为串口接口。   2. 代码配置 导入SDK中的frdmmcxa156_lpuart_polling 工程作为基准。 2.1 配置的初始化结构体 lpuart_config_t: /*! @brief LPUART configuration structure. */ typedef struct _lpuart_config { uint32_t baudRate_Bps; /*!< LPUART baud rate */ lpuart_parity_mode_t parityMode; /*!< Parity mode, disabled (default), even, odd */ lpuart_data_bits_t dataBitsCount; /*!< Data bits count, eight (default), seven */ bool isMsb; /*!< Data bits order, LSB (default), MSB */ #if defined(FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT) && FSL_FEATURE_LPUART_HAS_STOP_BIT_CONFIG_SUPPORT lpuart_stop_bit_count_t stopBitCount; /*!< Number of stop bits, 1 stop bit (default) or 2 stop bits */ #endif #if defined(FSL_FEATURE_LPUART_HAS_FIFO) && FSL_FEATURE_LPUART_HAS_FIFO uint8_t txFifoWatermark; /*!< TX FIFO watermark */ uint8_t rxFifoWatermark; /*!< RX FIFO watermark */ #endif #if defined(FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT) && FSL_FEATURE_LPUART_HAS_MODEM_SUPPORT bool enableRxRTS; /*!< RX RTS enable */ bool enableTxCTS; /*!< TX CTS enable */ lpuart_transmit_cts_source_t txCtsSource; /*!< TX CTS source */ lpuart_transmit_cts_config_t txCtsConfig; /*!< TX CTS configure */ #endif lpuart_idle_type_select_t rxIdleType; /*!< RX IDLE type. */ lpuart_idle_config_t rxIdleConfig; /*!< RX IDLE configuration. */ bool enableTx; /*!< Enable TX */ bool enableRx; /*!< Enable RX */ } lpuart_config_t; 关键参数: baudRate_Bps:指定LPUART的波特率,即每秒传输的比特数。 parityMode:定义了奇偶校验模式,可以是无校验(默认)、偶校验或奇校验。 dataBitsCount:指定了数据位的数量,默认为8位,也可以设置为7位。数据位是实际传输的数据部分。 isMsb:确定数据位的顺序,最低有效位(LSB)优先(默认),或者最高有效位(MSB)优先。这决定了数据在传输线上的排列顺序。 enableTx / enableRx:布尔值,用于分别启用或禁用发送和接收功能。 2.2 核心函数:开发板发送数据 传入参数: LPUART 外设基地址的指针、写入的数据起始地址、数据的大小(以字节为单位) status_t LPUART_WriteBlocking(LPUART_Type *base, const uint8_t *data, size_t length) { assert(NULL != data); const uint8_t *dataAddress = data; size_t transferSize = length; #if UART_RETRY_TIMES uint32_t waitTimes; #endif while (0U != transferSize) { #if UART_RETRY_TIMES waitTimes = UART_RETRY_TIMES; while ((0U == (base->STAT & LPUART_STAT_TDRE_MASK)) && (0U != --waitTimes)) #else while (0U == (base->STAT & LPUART_STAT_TDRE_MASK)) #endif { } #if UART_RETRY_TIMES if (0U == waitTimes) { return kStatus_LPUART_Timeout; } #endif base->DATA = *(dataAddress); dataAddress++; transferSize--; } /* Ensure all the data in the transmit buffer are sent out to bus. */ #if UART_RETRY_TIMES waitTimes = UART_RETRY_TIMES; while ((0U == (base->STAT & LPUART_STAT_TC_MASK)) && (0U != --waitTimes)) #else while (0U == (base->STAT & LPUART_STAT_TC_MASK)) #endif { } #if UART_RETRY_TIMES if (0U == waitTimes) { return kStatus_LPUART_Timeout; } #endif return kStatus_Success; } LPUART_WriteBlocking 函数是一个用于通过 LPUART 模块发送数据的阻塞式写入函数。它通过轮询(polling)的方式等待 LPUART 发送寄存器为空或 TX FIFO 有空间,然后将数据写入到发送缓冲区,并继续等待直到所有数据都被发送出去。     3.上位机vofa+ vofa+上位机功能丰富、界面高级,而且免费 ,非常推荐。   vofa+有多种发送格式,具体如下: RawData 协议,适用于那些不需要解析数据、仅仅查看字节流的需求。在这种模式下,VOFA+ 充当一个普通的串口助手,接收到的数据会直接显示出来。 FireWater 协议,是一种非常直观简洁的与 VOFA+ 上位机通信的协议。它的设计灵感来源于 CSV(逗号分隔值)文件格式,通过将print函数重定向即可直接使用,注意数据之间用逗号隔开,并且每帧数据后加上换行符。这种协议非常适合用于打印出简单的数值型数据,并在 VOFA+ 中绘制动态曲线图。 JustFloat 协议,是专门针对需要高效传输大量浮点数数据的情况设计的。该协议采用小端浮点数组的形式进行字节流传输,这样可以节省带宽并且保持较高的准确性。每个数据包后面通常附有一个特定的帧尾标志来标识数据包的结束。   我使用JustFloat发送格式,只需要在包尾发送对应的tail[4]{0x00,0x00,0x80,0x7f}即可上传多个通道的数据。   发送函数:void vofa_JustFloat_output(float s1,float s2,float s3,float s4) {     float data[4];     uint8_t tail[4] = {0x00,0x00,0x80,0x7f};     //发送数据     data[0] = s1;     data[1] = s2;     data[2] = s3;     data[3] = s4;     LPUART_WriteBlocking(DEMO_LPUART,(uint8_t*)data,sizeof(float)* 4); //发送数据     //发送帧尾     LPUART_WriteBlocking(DEMO_LPUART,tail,4); } 4.应用函数测试 编写4个正弦方程测试函数,每个变量的步长随意。 void vofa_FireWater_output_test (void) { static float t1 = 0; static float t2 = 0; static float t3 = 0; static float t4 = 0; vofa_JustFloat_output(sin(t1), cos(t2),sin(2*t3), cos(2*t4)); t1 = t1+0.1; t2 = t2+0.2; t3 = t3+0.5; t4 = t4+0.3; } 在while(1)中测试 while (1) { vofa_FireWater_output_test(); for(ch=0xffff;ch>0;ch--); //延时 } 在vofa+软件中,拖拽xy轴到显示界面,右键选择y轴,再点击all,即可看到四个波形。   选择0.1的步长,可以发现生成的四条正弦波很标准!   总结,LPUART模块非常方便,验证成功

  • 回复了主题帖: 【极海APM32M3514电机通用评估板】ADC电流采样 公式推理与验证

    lugl4313820 2025-2-4 21:43 10 лл

  • 2025-01-07
  • 回复了主题帖: 【极海APM32M3514电机通用评估板】外设篇(求助ADC)

    已解决。 为什么会发生循环执行无法退出ADC中断的情况,是因为ADC中断处理函数 的执行时间 大于 ADC触发周期。因此产生了退不出中断的错觉。

  • 2025-01-06
  • 回复了主题帖: 【极海APM32M3514电机通用评估板】ADC电流采样 公式推理与验证

      破案了,采样的UI是被SUM抬高了的,所以,在运放的反向输入端接SUM也就对应了 减去SUM偏置。所以 求ADC实际采样值,就等效了情况二,直接使用情况二的公式即可。

  • 2024-12-26
  • 发表了主题帖: 【极海APM32M3514电机通用评估板】ADC电流采样 公式推理与验证

    本帖最后由 不语arc 于 2024-12-26 11:39 编辑 1.背景 在上一篇分享中,我提到了一个问题: 为什么U、V两相的运放反相输入端为什么是实际的SUM?官方给出的计算公式,是按照反相输入端接地计算得到的,虽然SUM值不算大。   在这里我对两种情况分别讨论,用实验结果来验证。 2.分组实验 2.1 情况一:U、V两相的运放反相输入端是总电流SUM 重新推理公式,虚短和虚断:     计算得: 因此:其中,Uo为运放输出值,也就是ADC实际采样的值,Uin为采样电阻两端的电压值。     由公式修改ADC采样函数 typedef struct { float Ia; // Phase A current float Ib; // Phase B current float Ic; // Phase C current float Ibus; float Vbus; float VHandle; //0-3.3的范围 uint16_t ADC_Ia; // Phase A Voltage(I*R) uint16_t ADC_Ib; // Phase B Voltage uint16_t ADC_Ibus; // SUM Voltage uint16_t ADC_Ia_offset; // 电流零点偏置 uint16_t ADC_Ib_offset; // 电流零点偏置 uint16_t ADC_Ibus_offset; // 电流零点偏置 uint16_t ADC_Vbus; // Phase bus Voltage uint16_t ADC_Handle; } CURRENT_Def; #define ADC_V_K 0.0008 // 3.3/4096.0 // ADC 与电压转换系数 #define VREF 1.65 //参考电压偏置 #define Voffset 1.605 //偏置电压大小 #define AM_GAIN 4.86 //运放放大倍数 #define R_SENSE 0.02 //Ω 采样电阻 #define V_GAIN 21 //(100+100+10)/10 void GetADCvaule(){ float SUM; current.ADC_Ia = (int16_t)ADC_GetValue(CURR_CHANNEL_U); current.ADC_Ib = (int16_t)ADC_GetValue(CURR_CHANNEL_V); current.ADC_Ibus = (int16_t)ADC_GetValue(IBUS_CHANNEL); current.Ibus = (ADC_V_K*(current.ADC_Ibus-current.ADC_Ibus_offset) - Voffset) / (AM_GAIN * R_SENSE); SUM = current.Ibus * R_SENSE; current.Ia = (ADC_V_K*(current.ADC_Ia-current.ADC_Ia_offset) - Voffset + 4.84*SUM) / (AM_GAIN * R_SENSE); current.Ib = (ADC_V_K*(current.ADC_Ib-current.ADC_Ib_offset) - Voffset + 4.84*SUM) / (AM_GAIN * R_SENSE); // current.Ia = (ADC_V_K*(current.ADC_Ia-current.ADC_Ia_offset) - Voffset) / (AM_GAIN * R_SENSE); // current.Ib = (ADC_V_K*(current.ADC_Ib-current.ADC_Ib_offset) - Voffset) / (AM_GAIN * R_SENSE); current.Ic = current.Ibus - current.Ia - current.Ib; current.ADC_Vbus = (int16_t)ADC_GetValue(VDC_CHANNEL); current.Vbus = ADC_V_K*(current.ADC_Vbus) * V_GAIN; current.ADC_Handle = (int16_t)ADC_GetValue(Handle_CHANNEL); current.VHandle = ADC_V_K*(current.ADC_Handle); //0-3.3的范围 } 对电机进行foc控制,将ADC电流波形通过串口打印出来。测试了几组,稳定时波形差不多是这样。            2.2 情况二:U、V两相的运放反相输入端是GND 计算公式如下:    实际执行上述代码的注释部分,求Ia和Ib。 再次实验采样三相电流,打印输出。波形差不多。       3.结论 SUM为Ibus*0.02,由于当前的转速较慢,SUM值较小,未能根据波形判断出反相端 接入情况。后续将继续调试完成高转速下的波形判断,当Ibus电流变大,两种接入情况将一目了然。  

  • 2024-12-24
  • 发表了主题帖: 【MCXA156开发板测评】环境配置

    1.上手体验 MCXA156采用Arm® Cortex® M33内核,其中运行频率高达96MHz。开发板实物颜值极高,layout布局优雅。在硬件资源上,板载 CMSIS-DAP 减少了外部连线,同时方便调试。 2. ide下载 对恩智浦资料的下载需要注册一个账号。进入官网注册遇到了很大问题,使用了三个邮箱(qq、163、edu)都不太能实时收到注册邮件。等收到邮件的时候,验证码已经过期了。这里我是给官方提单反馈,完成了注册(看别人的贴子,好像谷歌邮箱可以正常注册)。 第一次使用恩智浦的MCU,当然要体验恩智浦完整的开发环境,所以选择MCUXpresso IDE作为编译工具。在官网下载MCUXpresso IDE for NXP MCUs | Linux, Windows and MacOS | NXP Semiconductors | NXP Semiconductors 有Linux、macos、windows、arm64多个平台的安装包,选择对应的默认安装即可。   3. 环境配置 进入IDE之后,选择Import SDK example     在弹出的界面 点击NXP IDE的小标志   在新界面搜索A156,选择对应的开发板即可安装SDK包   4. 例程测试 同上Import SDK example,选择A156开发板,就开到了全部例程,这里选择点灯程序,   点击编译、连接数据线到 CMSIS-DAP接口,点击debug按钮即可完成下载。 灯的闪烁速度比初始更快  [localvideo]9f665b6715c5ff6120657dc7e51cfa0e[/localvideo]  

  • 2024-12-19
  • 回复了主题帖: 测评入围名单: NXP MCX A系列 FRDM-MCXA156开发板

    个人信息无误,确认可以完成测评分享计划

  • 2024-12-17
  • 发表了主题帖: 【极海APM32M3514电机通用评估板】外设篇(求助ADC)

    本帖最后由 不语arc 于 2024-12-17 17:02 编辑 0.前提 官方例程是使用滑模观测器的无感FOC控制,但我无法测得自己的电机的各项参数,调试滑模观测器参数始终无法让电机转动起来(求大佬指点)。所以后续我打算直接使用有感FOC完成对电机的控制。 极海lib中的算法相当于使用uint类型完成浮点计算,也就是标幺化。但使用官方api完成有感控制,需要做大量的移植工作,而且有难度。因此,计划先使用math库,完成有感foc控制。在本篇,我将先完成对各个外设的基本驱动。   1.usart 串口配置,参考:【极海APM32M3514电机通用评估板】 串口输出测试 - 国产芯片交流 - 电子工程世界-论坛 将swd复用为串口,在硬件上与ch340模块的TX管脚相连(用不到RX),这里我使用了波动开关。 swd连ch340时,在main.c中开头加一段延时,如此可以在这个时间中继续使用下载功能。不连ch340时,swd仍可以用于调试,需将初始化USART的代码注释掉。    上位机vofa+用于显示波形非常方便,这里使用JustFloat发送格式,只需要在包尾发送对应的tail[4]{0x00,0x00,0x80,0x7f}即可上传多个通道的数据。   发送函数: //本协议是纯十六进制浮点传输 void vofa_JustFloat_output(float s1,float s2,float s3,float s4) { float data[4]; uint8_t tail[4] = {0x00, 0x00, 0x80, 0x7f}; // 发送数据 data[0] = s1; data[1] = s2; data[2] = s3; data[3] = s4; Usart_SendData(MINI_COM1,(uint8_t*)data,sizeof(float) * 4); //发送数据 // 发送帧尾 Usart_SendData(MINI_COM1,tail,4); } 2.PWM 本身的pwm配置已经配置好了,宏定义如下。pwm时钟频率同样为72Mhz,计数采用中心对齐模式,pwm开关频率为16khz。   3. I2C编码器 见上一篇分享   4.ADC  五路ADC   其中W相电流 = Ibus - Iu - Iv 配置方式不变,注意右对齐也就是低12位存储读取的结果。   将读取结果转成实际值,注意这里的ADC_Ia_offset为电机未转动时,读取的a相偏置电流大小。 实际读取的值是运放 输出的结果,对其进行解算,得到实际值。 typedef struct { float Ia; // Phase A current float Ib; // Phase B current float Ic; // Phase C current float Ibus; float Vbus; float VHandle; //0-3.3的范围 uint16_t ADC_Ia; // Phase A Voltage(I*R) uint16_t ADC_Ib; // Phase B Voltage uint16_t ADC_Ibus; // SUM Voltage uint16_t ADC_Ia_offset; // 电流零点偏置 uint16_t ADC_Ib_offset; // 电流零点偏置 uint16_t ADC_Ibus_offset; // 电流零点偏置 uint16_t ADC_Vbus; // Phase bus Voltage uint16_t ADC_Handle; } CURRENT_Def; #define ADC_V_K 0.0008 // 3.3/4096.0 // ADC 与电压转换系数 #define VREF 1.65 //参考电压偏置 #define AM_GAIN 4.86 //运放放大倍数 #define R_SENSE 0.02 //Ω 采样电阻 #define V_GAIN 21 //(100+100+10)/10 void GetADCvaule(){ current.ADC_Ia = (int16_t)ADC_GetValue(CURR_CHANNEL_U); current.ADC_Ib = (int16_t)ADC_GetValue(CURR_CHANNEL_V); current.ADC_Ibus = (int16_t)ADC_GetValue(IBUS_CHANNEL); current.Ia = (ADC_V_K*(current.ADC_Ia-current.ADC_Ia_offset) - VREF) / (AM_GAIN * R_SENSE); current.Ib = (ADC_V_K*(current.ADC_Ib-current.ADC_Ib_offset) - VREF) / (AM_GAIN * R_SENSE); current.Ibus = (ADC_V_K*(current.ADC_Ibus-current.ADC_Ibus_offset) - VREF) / (AM_GAIN * R_SENSE); current.Ic = current.Ibus - current.Ia - current.Ib; current.ADC_Vbus = (int16_t)ADC_GetValue(VDC_CHANNEL); current.Vbus = ADC_V_K*(current.ADC_Vbus) * V_GAIN; current.ADC_Handle = (int16_t)ADC_GetValue(Handle_CHANNEL); current.VHandle = ADC_V_K*(current.ADC_Handle); //0-3.3的范围 } 在这里提一个问题:为什么U、V两相的运放反相输入端为什么是实际的SUM?计算出的公式,是按照反相输入端接地计算得到的,虽然SUM值不算大。   另外,ADC的中断处理函数好像存在bug,为什么会发生循环执行无法退出ADC中断的情况。具体表现为ADC->STS 无法幅值,中断标志位无法清零。(关闭了看门狗) 两处写法很奇怪,但官方例程居然不怎么出现这个bug    

最近访客

< 1/3 >

统计信息

已有44人来访过

  • 芯积分:48
  • 好友:--
  • 主题:13
  • 回复:28

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言