赞
踩
ARM芯片本身基本介绍,裸机开发基本知识
第2遍看视频,增加截图、代码
我的win10主机通过wifi联网,在 嵌入式Linux视频笔记----Linux基础入门 的 P26 第25讲-安装NFS服务器 这一讲中我自己为了实现nfs,使用网线连接,按照其中设置,导致ubuntu无法联网。
现在将ubuntu联网方式记录如下【参考irtualbox+Ubuntu配置网络(桥接网络)】:
1、桥接网卡改为wifi
2、ubuntu与win10的IP设为同一网段,随后可以互相ping通
3、配置ubuntu DNS【缺点:每次都需要重新配置】
sudo vi /etc/resolv.conf
芯片手册【3619页】、开发板原理图获取方式
启动模式设置:三大模式--熔丝、外部 USB 串口、内部 SD卡 eMMc NAND;内部介质;
接口编号;介质属性;
芯片手册:具体引脚,启动模式设置中涉及的8个引脚
boot ROM内置程序:选择内部启动方式,启动boot ROM程序【芯片出厂前固化,用户无法变更】;初始化时钟、DDR3【从DCD获取信息】、从外部存储介质加载代码【从Boot data获取加载地址和大小】
镜像5要素:空偏移;image vector table IVT--1个结构体【包含Boot data 和DCD的位置,进而找到Boot data 和DCD】;Boot data--镜像在内存中的加载地址和大小;Device configuration data--DCD、寄存器初始化列表;bin文件--程序文件。
自动添加上一节讲的3部分信息:IVT、boot data、DCD
获取NXP官方SDK:官网、Linux平台
Linux中安装【下载,直接执行run文件】
查看readme、详细解读脚本文件
- mkimage.sh
-
- #!/bin/bash
-
- function usage()
- {
- echo "Usage: $0 target"
- echo " target: ram -- the image will be loaded to RAM and run, the application must be built with ram link file"
- echo " target: flash -- the image will be run on flash directly, the application must be build with flash link file"
- echo " target: sd -- the image will be loaded from SD to RAM and run, the application must be build with ram link file"
- echo "Example: $0 ram"
- }
-
- if [ "$#" -ne 1 ]; then # 参数个数不是1个,则给出提示信息
- usage $0
- exit 1
- fi
-
- SYSTEM=`uname -s` # 获取主机类型 Linux
- if [ $SYSTEM == "Linux" ]; then
- DCD_BUILDER=dcdgen.bin # 2个程序赋值
- IMG_BUILDER=imgutil.bin
- else
- DCD_BUILDER=dcdgen.exe
- IMG_BUILDER=imgutil.exe
- fi
-
- ../bin/$DCD_BUILDER dcd.config dcd.bin # 执行 dcdgen.bin 程序,根据dcd.config生成dcd.bin【dcd.config和本脚本在相同路径,主要是ddr配置信息】
- if [ "$1" == "ram" ]; then # 传递给脚本的第1个参数,注意 dcd_file为刚生成的dcd.bin,app_file为用户程序
- ../bin/$IMG_BUILDER --combine base_addr=0x80000000 ivt_offset=0x1000 app_offset=0x2000 dcd_file=dcd.bin app_file=sdk20-app.bin ofile=sdk20-app.img image_entry_point=0x80002000
- elif [ "$1" == "flash" ]; then
- ../bin/$IMG_BUILDER --combine base_addr=0x60000000 ivt_offset=0x1000 app_offset=0x2000 dcd_file=dcd.bin app_file=sdk20-app.bin ofile=sdk20-app.img image_entry_point=0x60002000
- elif [ "$1" == "sd" ]; then
- ../bin/$IMG_BUILDER --combine base_addr=0x80000000 ivt_offset=0x400 app_offset=0x2000 dcd_file=dcd.bin app_file=sdk20-app.bin ofile=sdk20-app.img image_entry_point=0x80002000
- else
- echo "Unsupported target $1"
- usage $0
- fi
野火烧录工具:模仿官方SDK【dcd.config与官方完全一致,DDR与官方相同。重写mkimage.sh】;解压缩 看脚本文件、相比官方增加了烧录功能;演示用法
- mkimage.sh
-
- #!/bin/bash
-
- function usage()
- {
- echo "Usage: $0 file"
- echo " file : the image which you want to burn "
- echo "Example: $0 helloworld.bin"
- }
-
-
-
- cur_user=`env | grep USER | cut -d "=" -f 2` # 从环境变量中提取USER
- echo $cur_user
- if [ $cur_user == "root" ]; then # 当前是root用户则报错
- echo -e "\033[31mThe cur_user is $cur_user. Please run the script with a normal user.\033[0m"
- exit 1
- fi
-
- if [ "$#" -ne 1 ]; then
- usage $0
- exit 1
- fi
-
-
-
-
- SYSTEM=`uname -s`
- if [ $SYSTEM == "Linux" ]; then
- DCD_BUILDER=dcdgen.bin
- IMG_BUILDER=imgutil.bin
- else
- exit 1
- fi
-
-
- cat /proc/partitions
- while true
- do
- read -p "Please Input the card ID [a~z]: (Input 'exit' for quit):" dev_index
- case $dev_index in
- [[:lower:]]) break
- ;;
- exit) exit 0
- ;;
-
- * ) echo -e "\033[31mInvalid parameter!\033[0m"
- echo -e "\033[31mThe parameter should be between a~z, enter 'exit' to quit.\033[0m"
- echo -e "\033[34mUsage: If the SD card device corresponds to /dev/sdd, enter d\033[0m"
- continue
- ;;
-
- esac
- done
- sd_idnex=sd$dev_index # 如果SD卡路径为 /dev/sdb ,$dev_index=b,sd_idnex=sdb
- echo $sd_index
- if [ ! -e /dev/$sd_idnex ]; then # 文件不存在【-e 选项表示检查文件是否存在】
- echo "mkimage : /dev/$sd_idnex : No such file or directory"
- exit 1
- fi
-
- if [ ! -x $DCD_BUILDER ]; then # 如果没有执行权限则增加执行权限【-x 选项表示检查文件是否可执行】
- chmod +x $DCD_BUILDER
- fi
-
- if [ ! -x $IMG_BUILDER ]; then
- chmod +x $IMG_BUILDER
- fi
-
- ./$DCD_BUILDER dcd.config dcd.bin #ivt表基地址0x80000000、bin文件链接地址0x80002000
- ./$IMG_BUILDER --combine base_addr=0x80000000 ivt_offset=0x400 app_offset=0x2000 dcd_file=dcd.bin app_file=$1 ofile=sdk20-app.img image_entry_point=0x80002000
-
- sudo dd if=sdk20-app.img of=/dev/$sd_idnex bs=512 conv=fsync # 使用dd命令将sdk20-app.img文件的内容写入SD卡
-
运行模式:9种模式 用户--资源访问受限;系统--无限制;一般中断--硬件中断;快速中断--高速信号;管理--上电默认、初始化、软中断;数据访问终止--非法访问;未定义指令--跑飞了;用户安全扩展、虚拟化扩展
寄存器组:通用寄存器组r0~r15【sp、lr、pc】;程序状态寄存器cpsr spsr;系统寄存器 cp15
汇编格式、常用段名、常见伪操作、寄存器间数据传输【非通用寄存器操作对应专用指令】、内存与寄存器数据传输、压栈 出栈、跳转、算术运算、逻辑运算
官网下载安装,终端code打开软件
特点--无需新建项目,直接打开文件夹;不同目录文件夹可保存至工作区;默认预览模式【打开后文件名斜体,再打开新文件,旧文件自动关闭】
推荐插件:c/c++【按ctrl后跳转】;Chinese;material theme UI;CodeSpell Checker;Bracket Pair Colorizer;rainbow-highlighter
高效工作:快捷键
VsCode 跳转到函数之后怎么跳转回之前的位置:Ctrl + Alt + '-'【注意不是小键盘的-】
调整vscode字体 monospace、加入汇编插件ARM
对比寄存器逐行写代码
- .global _start #定义全局标号
-
- _start:
-
- @是能GPIO时钟
- ldr r0,=0x20c406c #把立即数【CCM_CCGR1地址】加载到寄存器
- ldr r1,=0xffffffff #本应该是0x3<<26位,对应GPIO1 CLOCK配置【除停止模式外,该外设时钟全程使能】
- str r1,[r0] #寄存器数据写入到内存,[]可以理解为指针
-
-
- @设置引脚复用为GPIO
- ldr r0,=0x20e006c #把立即数【SW_MUX_CTL Register 地址】加载到寄存器
- ldr r1,=5 #0101配置为GPIO1_IO04
- str r1,[r0]
-
-
- @设置引脚属性【上下拉/速率/驱动能力,具体配置参考硬件工程师建议或SDK包的配置】
- ldr r0,=0x20e02f8 #把立即数【SW_PAD_CTL Register 地址】加载到寄存器
- ldr r1,=0x10b0 #具体配置参考SDK包的配置
- str r1,[r0]
-
-
- @控制GPIO引脚输出高低电平
- ldr r0,=0x209c004 #把立即数【GPIOx_GDIR Register 地址】加载到寄存器
- ldr r1,=16 #GPIO的第4位配置为输出
- str r1,[r0]
-
- ldr r0,=0x209c000 #把立即数【GPIOx_DR Register 地址】加载到寄存器
- ldr r1,=0 #GPIO的第4位配置为低电平【简单起见,全为0】
- str r1,[r0]
程序编译:下载裸机gcc编译器 gcc-arm-none-eabi【none指裸机,对应linux】、编译、链接、得到bin文件、添加头信息并烧录、开发板上电验证
sudo apt-get install gcc-arm-none-eabi
arm-none-eabi-gcc -c led.s -o led.o
arm-none-eabi-ld -Ttext 0x80000000 led.o -o led.elf
arm-none-eabi-objcopy -O binary led.elf led.bin
cd ~/workdir/LinuxCode/bare_mental/part_1/download_tool/
./mkimage.sh ~/workdir/embed_linux_tutorial/base_code/bare_metal/part_2_gzc/led.bin
vscode工作区用法:先打开文件夹A,“文件”-->“将工作区另存为”,命名保存,“文件”-->“将文件夹添加到工作区”
使用官方sdk:包含寄存器宏定义,避免反复查datasheet;
使用C:bin文件段;c环境--bss段清0、栈指针设置;裸机控制外设--外设对应寄存器;
链接脚本lds文件;Makefile修改
按下一次灯状态翻转
按键key硬件原理图
程序实现
文件整理:source文件夹按模块文件夹存放文件;source/common存放共用函数;source/project存放main、启动文件;include文件夹存放sdk移植头文件;Makefile修改路径
中断头文件移
ARM有4个版本规范,A7使用V2 GIC-400
GIC结构:信号源--软件中断、私有中断、共享中断 使用最多 多核共享;分发器--中断信号到cpu接口单元;cpu接口单元--分发器到cpu、保存中断ID
获取GIC基地址:芯片手册;cp15协处理器--16个协处理器【1个协处理器=一大类寄存器】、功能需要配置、配置为CBAR SCTLR VBAR
针对ARMv7-A
一级查表:自动跳转指定位置运行;写死在start.s文件中;
二级查表:在sdk的头文件中;
汇编+C语言
中断上下文:内核寄存器进入中断前的值
具体流程:cps设置cpsr进入IRQ模式、初始化栈指针【不同模式栈不同】、push、获取中断编号、执行中断代码、还原现场、返回原程序
三级流水线:取指令、译指令、执行指令
上电先进汇编代码复位中断,随后跳转到C代码main,
C语言内嵌汇编:c语言读cp15协处理器,详细解释几行代码
IO中断相关
程序执行和变量访问方式:pc指针+偏移地址;绝对地址
位置无关码--pc指针+偏移地址、普通代码段、局部变量、可在任意内存运行;
位置相关码--绝对地址、必须在指定运行地址运行。
重定位:利用位置无关码将位置相关码加载到指定位置
bin加载地址:
这条命令使用hexdump工具来查看sdk20-app.img文件的十六进制和ASCII码。-n 50表示只显示文件的前50个字节,-C表示以规范的十六进制+ASCII码格式输出,-s 1024表示从文件的第1024个字节【SD卡镜像有1K的空偏移】开始显示。
输出的前4个字节为IVT header,随后4个字节为bin文件链接地址0x8000 2000。
时钟的4个层次:晶振、PL PFD、PLL选择、根时钟/外设时钟
系统时钟:24MHz、RTC时钟
PLL PFD倍频:7路PLL
PLL选择时钟:分频、选择
根时钟/外设时钟:时钟树
对照上一讲介绍代码【学习阶段不要一个一个查寄存器,把握总体框架即可。除非做项目有需求】
- //4层时钟设置
- #include "clock.h"
-
-
- void system_clock_init(void)
-
- {
- /******************* 第一层时钟设置--晶振时钟***********************/
- if ((CCM->CCSR & (0x01 << 2)) == 0) //CPU 使用的是 ARM PLL
- {
- /*将CPU时钟切换到XTAL (OSC) 时钟*/
- CCM->CCSR &= ~(0x01 << 8); //控制CCSR: step_sel ,选择 osc_clk 作为时钟源
- CCM->CCSR |= (0x01 << 2); //设置GLITCHLESS MUX 选择 step_clk 作为时钟源
- }
-
-
-
- /******************* 第二层时钟设置--PLL时钟***********************/
- /*设置PLL1输出时钟为792MHz,它将作为CPU时钟*/
- CCM_ANALOG->PLL_ARM |= (0x42 << 0);
-
-
- /*将CPU 时钟重新切换到 ARM PLL【初始化好PLL1就不直接用晶振了】*/
- CCM->CCSR &= ~(0x01 << 2);
-
-
- /*设置时钟分频系数为0,即不分频*/
- CCM->CACRR &= ~(0x07 << 0); //清零分频寄存器 不分频
-
- //CCM->CACRR |= (0x07 << 0); // 8分频
-
-
- /*设置PLL2(System PLL) 输出时钟*/
- /* Configure SYS PLL to 528M */
- CCM_ANALOG->PLL_SYS_SS &= ~(0x8000); //使能PLL2 PFD输出
- CCM_ANALOG->PLL_SYS_NUM &= ~(0x3FFFFFFF);//设置分频系数为0,即不分频。
- CCM_ANALOG->PLL_SYS |= (0x2000); //使能PLL2 输出
- CCM_ANALOG->PLL_SYS |= (1 << 0); //设置输出频率为528M
- while ((CCM_ANALOG->PLL_SYS & (0x80000000)) == 0) //等待设置生效
- {
- }
-
- /*设置PLL3(System PLL) 输出时钟*/
- /* Configure USB PLL to 480M */
- CCM_ANALOG->PLL_USB1 |= (0x2000); //使能 PLL3时钟输出
- CCM_ANALOG->PLL_USB1 |= (0x1000); //PLL3上电使能
- CCM_ANALOG->PLL_USB1 |= (0x40); // 使能USBPHYn
- CCM_ANALOG->PLL_USB1 &= ~(0x01 << 0);//设置输出频率为480MHz
- while ((CCM_ANALOG->PLL_SYS & (0x80000000)) == 0)//等待设置生效
- {
- }
-
-
- /*关闭暂时不使用的 PLL4 、PLL5 、PLL6 、PLL7*/
- CCM_ANALOG->PLL_AUDIO = (0x1000); //关闭PLL4
- CCM_ANALOG->PLL_VIDEO = (0x1000); //关闭PLL5
- CCM_ANALOG->PLL_ENET = (0x1000); //关闭PLL6
- CCM_ANALOG->PLL_USB2 = (0x00); //关闭PLL7
-
-
-
- /******************第三层时钟设置--PFD*******************/
- /*禁用PLL2 的所有PFD输出*/
- CCM_ANALOG->PFD_528 |=(0x80U) ; //关闭PLL2 PFD0
- CCM_ANALOG->PFD_528 |=(0x8000U) ; //关闭PLL2 PFD1
- // CCM_ANALOG->PFD_528 |=(0x800000U) ; //关闭PLL2 PFD2 ,DDR使用的是该时钟源,关闭后程序不能运行。暂时不关闭
- CCM_ANALOG->PFD_528 |=(0x80000000U); //关闭PLL2 PFD3
-
-
- /*设置PLL2 的PFD输出频率*/
- CCM_ANALOG->PFD_528 &= ~(0x3FU); //清零PLL2 PFD0 时钟分频
- CCM_ANALOG->PFD_528 &= ~(0x3F00U); //清零PLL2 PFD1 时钟分频
- CCM_ANALOG->PFD_528 &= ~(0x3F00U); //清零PLL2 PFD2 时钟分频
- CCM_ANALOG->PFD_528 &= ~(0x3F00U); //清零PLL2 PFD3 时钟分频
-
-
- CCM_ANALOG->PFD_528 |= (0x1B << 0); //设置PLL2 PFD0 输出频率为 352M
- CCM_ANALOG->PFD_528 |= (0x10 << 8); //设置PLL2 PFD0 输出频率为 594M
- CCM_ANALOG->PFD_528 |= (0x18 << 16); //设置PLL2 PFD0 输出频率为 396M
- CCM_ANALOG->PFD_528 |= (0x30 << 24); //设置PLL2 PFD0 输出频率为 198M
-
-
- /*启用PLL2 的所有PFD输出*/
- CCM_ANALOG->PFD_528 &= ~(0x80U) ; //开启PLL2 PFD0
- CCM_ANALOG->PFD_528 &= ~(0x8000U) ; //开启PLL2 PFD1
- CCM_ANALOG->PFD_528 &= ~(0x800000U) ; //开启PLL2 PFD2
- CCM_ANALOG->PFD_528 &= ~(0x80000000U); //开启PLL2 PFD3
-
-
-
- /*禁用PLL3 的所有PFD输出*/
- CCM_ANALOG->PFD_480 |=(0x80U) ; //关闭PLL3 PFD0
- CCM_ANALOG->PFD_480 |=(0x8000U) ; //关闭PLL3 PFD1
- CCM_ANALOG->PFD_480 |=(0x800000U) ; //关闭PLL3 PFD2
- CCM_ANALOG->PFD_480 |=(0x80000000U); //关闭PLL3 PFD3
-
-
- /*设置PLL3 的PFD输出频率*/
- CCM_ANALOG->PFD_480 &= ~(0x3FU); //清零PLL3 PFD0 时钟分频
- CCM_ANALOG->PFD_480 &= ~(0x3F00U); //清零PLL3 PFD1 时钟分频
- CCM_ANALOG->PFD_480 &= ~(0x3F00U); //清零PLL3 PFD2 时钟分频
- CCM_ANALOG->PFD_480 &= ~(0x3F00U); //清零PLL3 PFD3 时钟分频
-
-
- CCM_ANALOG->PFD_480 |= (0xC << 0); //设置PLL3 PFD0 输出频率为 720M
- CCM_ANALOG->PFD_480 |= (0x10 << 8); //设置PLL3 PFD0 输出频率为 540M
- CCM_ANALOG->PFD_480 |= (0x11 << 16); //设置PLL3 PFD0 输出频率为 508.2M
- CCM_ANALOG->PFD_480 |= (0x13 << 24); //设置PLL3 PFD0 输出频率为 454.7M
-
-
- /*启用PLL3 的所有PFD输出*/
- CCM_ANALOG->PFD_480 &= ~(0x80U) ; //开启PLL3 PFD0
- CCM_ANALOG->PFD_480 &= ~(0x8000U) ; //开启PLL3 PFD1
- CCM_ANALOG->PFD_480 &= ~(0x800000U) ; //开启PLL3 PFD2
- CCM_ANALOG->PFD_480 &= ~(0x80000000U); //开启PLL3 PFD3
-
-
-
- /******************第四层时钟设置--外设****************/
- CCM->CSCDR1 &= ~(0x01 << 6); //设置UART选择 PLL3 / 6 = 80MHz
- CCM->CSCDR1 &= ~(0x3F); //清零
- CCM->CSCDR1 |= ~(0x01 << 0); //设置串口根时钟分频值为1,UART根时钟频率为:80M / (dev + 1) = 40MHz
- }
实验现象:烧2次程序,主频不同,灯闪烁频率不同
- CCM->CACRR &= ~(0x07 << 0); //清零分频寄存器 不分频
- // CCM->CACRR |= (0x07 << 0); // 8分频
硬件原理图:
相关寄存器:串口时钟、UART配置、收发数据
代码【与上述分析一致】:
- #include "uart.h"
-
- void uart_init(void)
- {
- /*时钟初始化,设置 UART 根时钟,并设置为40MHz*/
- CCM->CSCDR1 &= ~(0x01 << 6); //设置UART选择 PLL3 / 6 = 80MHz
- CCM->CSCDR1 &= ~(0x3F); //清零
- CCM->CSCDR1 |= (0x01 << 0); //设置串口根时钟分频值为1,UART根时钟频率为:80M / (dev + 1) = 40MHz
-
- //禁用 UART1
- UART1->UCR1 &= ~UART_UCR1_UARTEN_MASK;
-
- /*软件复位【刚开始工作状态不稳定,复位后就稳定了】*/
- UART1->UCR2 &= ~UART_UCR2_SRST_MASK;
- while ((UART1->UCR2 & UART_UCR2_SRST_MASK) == 0)
- {
- }
-
- /*引脚初始化*/
- IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0);
- IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10b0);
-
- IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);
- IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10b0);
-
- /*******uart初始化******/
- /*设置控制寄存器到默认值*/
- UART1->UCR2 |= (1 << 5); //8位数宽度
- UART1->UCR2 &= ~(1 << 6); //一位停止位
- UART1->UCR2 &= ~(1 << 8); //禁用奇偶校验位
-
- UART1->UCR2 |= (1 << 2); //使能发送
- UART1->UCR2 |= (1 << 1); //使能接收
- UART1->UCR2 |= (1 << 14); //忽略流控
-
- /* For imx family device, UARTs are used in mode, so that this bit should always be set.*/
- UART1->UCR3 |= UART_UCR3_RXDMUXSEL_MASK;
-
- //UART1->UFCR = (UART1->UFCR & ~UART_UFCR_TXTL_MASK) | UART_UFCR_TXTL(1); //设置发送FIFO 阀值
- //UART1->UFCR = (UART1->UFCR & ~UART_UFCR_TXTL_MASK) | UART_UFCR_TXTL(1); //设置接收FIFO 阀值
-
- UART1->UCR1 &= ~UART_UCR1_ADBR_MASK; //禁用可变波特率
-
-
- /*波特率设置方式 1 。 使用官方SDK设置波特率函数*/
- UART_SetBaudRate(UART1, 115200, 40000000);
- #if 0
- /*波特率设置方式 2 。 手动计算,填入寄存器*/
- /*设置串口波特率
- * Ref Freq时钟 40MHz
- * UFCR RFDIV 110 0x06 7分频 5.714MHz
- * BaudRate 115200bps
- * UBMR 31-1 = 0x09
- * UBIR 10-1 = 0x1E
- */
- UART1->UFCR &= ~(0x07 << 7); //清零分频值
- UART1->UFCR |= (0x06 << 7); //设置分频值,40MHz /7 = 5.714MHz
-
- UART1->UBIR = 0x09;
- UART1->UBMR = 0x1E;
- #endif
- /*开启串口*/
- UART1->UCR1 |= UART_UCR1_UARTEN_MASK;
-
- }
-
-
- /*!
- * 功能:官方SDK 串口字符串读取函数
- * @brief Reads the receiver register.
- *
- * This function is used to read data from receiver register.
- * The upper layer must ensure that the receiver register is full or that
- * the RX FIFO has data before calling this function.
- *
- * @param base UART peripheral base address.
- * @return Data read from data register.
- */
- static inline uint8_t UART_ReadByte(UART_Type *base)
- {
- return (uint8_t)((base->URXD & UART_URXD_RX_DATA_MASK) >> UART_URXD_RX_DATA_SHIFT);
- }
-
-
- /*函数功能:串口接收函数
- *参数: base,指定串口。data,保存接收到的数据。 length,要接收的数据长度
- *
- */
- void UART_ReadBlocking(UART_Type *base, uint8_t *data, uint8_t length)
- {
- while (length--)
- {
- /* 等待接收完成 */
- while (!(base->USR2 & UART_USR2_RDR_MASK))
- {
- }
- /*读取接收到的数据 */
- *(data++) = UART_ReadByte(base);
- }
- }
-
-
- /*!
- * 功能:官方SDK 串口发送函数
- * 参数:base,指定串口。data,指定要发送的字节
- * This function is used to write data to transmitter register.
- * The upper layer must ensure that the TX register is empty or that
- * the TX FIFO has room before calling this function.
- */
- static inline void UART_WriteByte(UART_Type *base, uint8_t data)
- {
- base->UTXD = data & UART_UTXD_TX_DATA_MASK;
- }
-
- /*
- *功能:官方SDK 串口字符串发送函数
- *参数说明:
- */
- void UART_WriteBlocking(UART_Type *base, const uint8_t *data, uint8_t length)
- {
-
- while (length--)
- {
- /* Wait for TX fifo valid.
- * This API can only ensure that the data is written into the data buffer but can't
- * ensure all data in the data buffer are sent into the transmit shift buffer.
- */
- while (!(base->USR2 & UART_USR2_TXDC_MASK))
- {
- }
- UART_WriteByte(base, *(data++));
- }
- }
- # include "common.h"
- #include "led.h"
- #include "button.h"
- #include "interrupt.h"
- #include "clock.h"
- #include "uart.h"
- uint8_t button_status=0;
- char g_charA = 'A'; //存储在 .data段
- char g_charB = 'A'; //存储在 .data段
-
- /*提示字符串*/
- uint8_t txbuff[] = "Uart polling example\r\nBoard will send back received characters\r\n";
-
- int main()
- {
-
- //用于暂存串口收到的字符
- uint8_t ch;
- /*系统时钟初始化*/
- system_clock_init();
- /*GIC中断和中断向量表初始化*/
- irq_init();
- /*初始化led灯和按键*/
- rgb_led_init();
- /*串口初始化*/
- uart_init();
- /*发送提示字符串*/
- UART_WriteBlocking(UART1, txbuff, sizeof(txbuff) - 1);
- red_led_on; //红灯亮,提示程序运行中
- while (1)
- {
- UART_ReadBlocking(UART1, &ch, 1);
- UART_WriteBlocking(UART1, &ch, 1);
- }
- return 0;
Makefile修改:gcc除法库
代码效果【上电发送提示字符,随后收什么发什么】:
RGB接口三原色传输
硬件原理图:
基本原理:视频即多帧图片、图片逐行显示
时序:帧同步、行同步、帧开始/结束缓冲时间
参数:看数据手册、RGB888 565、分辨率、像素时钟
相关寄存器:
代码讲解:引脚配置、时钟配置、LCD配置
7种纯色循环显示
- # include "common.h"
- #include "led.h"
- #include "button.h"
- #include "interrupt.h"
- #include "clock.h"
- #include "uart.h"
- #include "lcd.h"
-
- uint8_t button_status=0;
- char g_charA = 'A'; //存储在 .data段
- char g_charB = 'A'; //存储在 .data段
-
- /*提示字符串*/
- uint8_t txbuff[] = "Uart polling example\r\nBoard will send back received characters\r\n";
-
- int main()
- {
-
- //用于暂存串口收到的字符
- uint8_t ch;
- /*lcd显存编号*/
- uint32_t frameBufferIndex = 0;
- /*系统时钟初始化*/
- system_clock_init();
- /*GIC中断和中断向量表初始化*/
- irq_init();
- /*初始化led灯和按键*/
- rgb_led_init();
- /*串口初始化*/
- uart_init();
- /*发送提示字符串*/
- UART_WriteBlocking(UART1, txbuff, sizeof(txbuff) - 1);
- /*初始 lcdif 引脚*/
- lcdif_pin_config();
- /*初始化时钟*/
- lcdif_clock_init();
- /*初始化 lcd属性和中断*/
- lcd_property_Init();
-
- red_led_on;
- while (1)
- {
- frameBufferIndex ^= 1U; //异或,相当于取反。1U表示无符号整数1
- APP_FillFrameBuffer(s_frameBuffer[frameBufferIndex]);
-
- LCDIF->NEXT_BUF = (uint32_t)s_frameBuffer[frameBufferIndex];
-
- /* 等待上一个图片刷新完成 Wait for previous frame complete. */
- while (!s_frameDone);
- s_frameDone = false;
-
- delay(0xFFFF);
- }
-
- return 0;
- #include "lcd.h"
- #include "interrupt.h"
-
- /*定义 elcdf 缓冲区[2--两张图片 APP_IMG_HEIGHT--列像素 APP_IMG_WIDTH--行像素]*/
- uint32_t s_frameBuffer[2][APP_IMG_HEIGHT][APP_IMG_WIDTH];
- uint8_t s_frameDone = false;
-
-
- /* elcdif 显示接口外部引脚初始化
- *
- */
- void lcdif_pin_config(void)
- {
- IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0U);
- IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0xB9);
-
- IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08, 0U);
- IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08,0xB9); /* 背光BL引脚 */
-
- /*设置GPIO1_08为输出模式*/
- GPIO1->GDIR |= (1<<8);
-
- /*设置GPIO1_08输出电平为低电平【不打开背光灯】*/
- GPIO1->DR |= (0<<8);
-
- }
-
-
-
-
-
- /*初始化 elcdf 的时钟
- */
- void lcdif_clock_init(void)
- {
- /*设置 PLL5 的输出时钟*/
- CCM_ANALOG->PLL_VIDEO_NUM &= (0x3 << 30); //清零PLL 分数分频的分子寄存器
- CCM_ANALOG->PLL_VIDEO_DENOM &= (0x3 << 30); //清零PLL 分数分频的分母寄存器
-
- /*
- * 设置时钟分频
- *
- * ------------------------------------------------------------------------
- * | 分频数 | PLL_VIDEO[POST_DIV_SELECT] | MISC2[VIDEO_DIV] |
- * ------------------------------------------------------------------------
- * | 1 | 2 | 0 |
- * ------------------------------------------------------------------------
- * | 2 | 1 | 0 |
- * ------------------------------------------------------------------------
- * | 4 | 2 | 3 |
- * ------------------------------------------------------------------------
- * | 8 | 1 | 3 |
- * ------------------------------------------------------------------------
- * | 16 | 0 | 3 |
- * ------------------------------------------------------------------------
- */
- CCM_ANALOG->PLL_VIDEO = 0;
- CCM_ANALOG->PLL_VIDEO &= ~(0x3 << 19); // 清零PLL_VIDEO[POST_DIV_SELECT]
- CCM_ANALOG->PLL_VIDEO |= (0x01 << 19); //设置分频系数为2
-
- CCM_ANALOG->MISC2 &= ~(0xC0000000); //清零VIDEO_DIV位
- CCM_ANALOG->MISC2 |= (0x3 << 30);// 配合CCM_ANALOG->PLL_VIDEO寄存器设置时钟分频
-
-
- CCM_ANALOG->PLL_VIDEO &= ~(0x7F); // 清零时钟分频
- CCM_ANALOG->PLL_VIDEO |= (0x1F); //设置时钟分频为 31(十进制)
-
- CCM_ANALOG->PLL_VIDEO |= 1 << 13; //使能PLL5时钟输出
-
- /*等待设置生效*/
- while ((CCM_ANALOG->PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_LOCK_MASK) == 0)
- {
- }
-
- /*设置从PLL5 到 elcdf 根时钟所经过的时钟选择和时钟分频寄存器*/
- CCM->CSCDR2 &= ~(0x07 << 15); //清零
- CCM->CSCDR2 |= (0x02 << 15); //设置CSCDR2[LCDIF1_PRE_CLK_SEL] 选择 PLL5 输出时钟
-
- CCM->CSCDR2 &= ~(0x07 << 12); //清零
- CCM->CSCDR2 |= (0x01 << 12); //设置 CSCDR2[LCDIF1_PRED]时钟分频值
-
- CCM->CBCMR &= ~(0x07 << 23); //清零CBCMR[LCDIF1_PODF] 时钟分频值
- CCM->CBCMR |= (0x01 << 23);
-
- CCM->CSCDR2 &= ~(0x07 << 9); //清零
- CCM->CSCDR2 |= (0x00 << 9); //选择 CSCDR2[LCDIF1_CLK_SEL] 选择 PLL5 输出时钟
- }
-
-
- /* 软复位lcd【使用LCD_RST复位则为硬复位】 */
- void ELCDIF_Reset(void)
- {
- LCDIF->CTRL = 1<<31;
-
- delay(100);
-
- LCDIF->CTRL = 0<<31;
-
- /*设置GPIO1_08输出电平为高电平,打开背光*/
- GPIO1->DR |= (1<<8);
-
- }
-
-
- /*将 lcd 初始化为 rgb 888 模式,并设置lcd中断c
- */
- void lcd_property_Init(void)
- {
-
- /* Reset. */
- ELCDIF_Reset();
-
-
- LCDIF->CTRL &= ~(0x300); //根据颜色格式设置 CTRL 寄存器 颜色个事为RGB888
- LCDIF->CTRL |= (0x3 << 8);
-
- LCDIF->CTRL &= ~(0xC00); //设置数据宽度为24位宽
- LCDIF->CTRL |= (0x3 << 10);
-
- LCDIF->CTRL |= (0x20000); // 选择 RGB 模式
- LCDIF->CTRL |= (0x80000); // 选择 RGB 模式 开启显示
- LCDIF->CTRL |= (0x20); //设置elcdf接口为主模式
-
-
-
- LCDIF->CTRL1 &= ~(0xF0000); //清零32位数据有效位
- LCDIF->CTRL1 |= (0x07 << 16); // 设置32位有效位的低24位有效。
-
-
- // LCDIF->TRANSFER_COUNT = 0;//清零分辨率设置寄存器
- LCDIF->TRANSFER_COUNT |= APP_IMG_HEIGHT << 16; //设置一列 像素数 480
- LCDIF->TRANSFER_COUNT |= APP_IMG_WIDTH << 0; //设置一行 像素数 800
-
-
-
- LCDIF->VDCTRL0 |= LCDIF_VDCTRL0_ENABLE_PRESENT_MASK; //生成使能信号
- LCDIF->VDCTRL0 |= LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT_MASK; //设置VSYNC周期 的单位为显示时钟的时钟周期
- LCDIF->VDCTRL0 |= LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT_MASK; //设置VSYNC 脉冲宽度的单位为显示时钟的时钟周期
-
-
- LCDIF->VDCTRL0 |= (1 << 24); //设置 数据使能信号的有效电平为高电平
- LCDIF->VDCTRL0 &= ~(0x8000000); //设置 VSYNC 有效电平为低电平
- LCDIF->VDCTRL0 &= ~(0x4000000); //设置HSYNC有效电平为低电平
- LCDIF->VDCTRL0 |= (0x2000000); // 设置在时钟的下降沿输出数据,在时钟的上升沿捕获数据。
-
- LCDIF->VDCTRL0 |= APP_VSW;
-
-
- // 以显示时钟为单位的周期。
-
- LCDIF->VDCTRL1 = APP_VSW + APP_IMG_HEIGHT + APP_VFP + APP_VBP; //设置VSYNC 信号周期
-
- LCDIF->VDCTRL2 |= (APP_HSW << 18); //HSYNC 信号有效电平长度
- LCDIF->VDCTRL2 |= (APP_HFP + APP_HBP + APP_IMG_WIDTH + APP_HSW); //HSYNC 信号周期
-
- LCDIF->VDCTRL3 |= (APP_HBP + APP_HSW) << 16;
- LCDIF->VDCTRL3 |= (APP_VBP + APP_VSW);
-
- LCDIF->VDCTRL4 |= (0x40000);
- LCDIF->VDCTRL4 |= (APP_IMG_WIDTH << 0);
-
- LCDIF->CUR_BUF = (uint32_t)s_frameBuffer[0];
- LCDIF->NEXT_BUF = (uint32_t)s_frameBuffer[0];
-
- /*注册lcd中断函数*/
- system_register_irqhandler(LCDIF_IRQn, (system_irq_handler_t)(uint32_t)APP_LCDIF_IRQHandler, NULL); // 设置中断服务函数
-
- /*开启中断*/
- GIC_EnableIRQ(LCDIF_IRQn);
-
- /*使能 elcdf 一帧传输完成中断*/
- LCDIF->CTRL1_SET |= (0x2000);
-
- /*开启 elcdf 开始显示*/
- LCDIF->CTRL_SET |= 0x1;
- LCDIF->CTRL_SET |= (1 << 17);
- }
-
- void APP_FillFrameBuffer(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH])
- {
- /* Background color. 【黑色】*/
- static const uint32_t bgColor = 0U;
- /* Foreground color. */
- static uint8_t fgColorIndex = 0U;
- // static uint16_t lowerRightX = (APP_IMG_WIDTH - 1U) / 2U; //例程代码只在屏幕左上角1/4显示
- // static uint16_t lowerRightY = (APP_IMG_HEIGHT - 1U) / 2U;
- static uint16_t lowerRightX = (APP_IMG_WIDTH - 1U); //改为全屏显示
- static uint16_t lowerRightY = (APP_IMG_HEIGHT - 1U);
-
- //对应7种颜色
- static const uint32_t fgColorTable[] = {0x000000FFU, 0x0000FF00U, 0x0000FFFFU, 0x00FF0000U,
- 0x00FF00FFU, 0x00FFFF00U, 0x00FFFFFFU};
- uint32_t fgColor = fgColorTable[fgColorIndex];
-
- uint32_t i, j;
-
- /* Background color. 【全屏黑色】*/
- for (i = 0; i < APP_IMG_HEIGHT; i++){
- for (j = 0; j < APP_IMG_WIDTH; j++) {
- frameBuffer[i][j] = bgColor;
- }
- }
-
- /* Foreground color. */
- for (i = 0; i < lowerRightY; i++){
- for (j = 0; j < lowerRightX; j++) {
- frameBuffer[i][j] = fgColor;
- }
- }
-
- if(fgColorIndex == ARRAY_SIZE(fgColorTable))
- fgColorIndex = 0;
- else
- fgColorIndex++;
-
- }
-
- void APP_LCDIF_IRQHandler(void)
- {
- uint32_t intStatus = 0;
-
- /*获取传输完成中断的状态,*/
- intStatus = ((LCDIF->CTRL1) & (1 <<9));
- /*清除 1 帧传输完成中断标志位*/
- LCDIF->CTRL1_CLR = (1 << 9);
-
- if (intStatus)
- {
- s_frameDone = true; //传输完成后,全局变量为true
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。