目录
2.编写示例程序:datax、datay、dataz三个数中,找出最大值并显示。
1.学习CH04示例程序,包括gpio.c和4个工程中的main.c
2.给出gpio_set(LIGHT_RED,LIGHT_OFF);语句中LIGHT_RED和LIGHT_OFF的值是多少?贴出每一步的查找截图
1.编写UART_2串口发送程序时,初始化需要设置哪些参数?
2.假设速度为115200,系统时钟为72MHz,波特率寄存器BRR中的值应该是多少?
4.以下是中断源使能函数,假设中断源为TIM6,将函数实例化(写出各项具体数值)。
5.假设将UART_2和TIM6交换其在中断向量表中的位置和IRQ号, UART_2可以正常中断吗?
1.运行并理解..\04\-Software\CH06文件夹中的几个程序。
2.实现UART_2串口的接收程序,当收到字符时:①在电脑的输出窗口显示下一个字符,如收到A显示B;②亮灯:收到字符G亮绿灯;收到R亮红灯;收到B亮蓝灯;收到其他字符不亮灯。
2.2 UART部分用直接地址方式实现(即不调用uart.c中的函数,其他部分如GPIO、中断设置可调用函数)。
第一章
1.列表罗列嵌入式系统常用术语
中文名 | 英文缩写 | 英文全称 |
---|---|---|
封装 | PKG | Package |
印制电路板 | PCB | Printed Circuit Board |
动态可读写随机存储器 | DRAM | Dynamic Random Access Memory |
静态可读写随机存储器 | SRAM | Static Random Access Memory |
只读存储器 | ROM | Read Only Memory |
闪速存储器 | FM | Flash Memory |
模拟量与开关量 | AS, DS | Analog Signal, Digital Signal |
常见封装形式可分为两大类:
通孔封装:单列直插(SIP)、双列直插(DIP)、Z字形直插式封装(ZIP)......
贴片封装:小外形封装(SOP)、紧缩小外形封装(SSOP)、四方扁平封装(QFP)、塑料薄方封装(LQFP)、塑料扁平组件式封装(PFP)、插针网格阵列封装(PGA)、球栅阵列封装(BGA)......
中文名 | 英文缩写 | 英文全称 |
---|---|---|
并行通信 | PC | Parallel Communication |
串行通信 | SC | Serial Communication |
串行外设接口 | SPI | Serial Peripheral Interface |
集成电路互联总线 | I2C | Inter-Integrated Circuit |
通用串行总线 | USB | Universal Serial Bus |
控制器局域网 | CAN | Controller Area Network |
边界扫描测试协议 | JTAG | Joint Test Action Group |
串行线调试技术 | SWD | Serial Wire Debug |
中文名 | 英文缩写 | 英文全称 |
---|---|---|
通用输入输出 | GPIO | General Purpose I/O |
模数转换 | ADC | Analog to Digital Convert |
数模转换 | DAC | Digital to Analog Convert |
脉冲宽度调制器 | PWM | Pulse Width Modulator |
看门狗 | WDT | Watch Dog |
液晶显示 | LCD | Liquid Crystal Dispaly |
发光二极管 | LED | Light Emitting Diode |
键盘 | KBD | Keyboard |
2.运行示例程序
步骤1:硬件接线
步骤2:打开环境,导入工程
步骤3:编译工程
步骤4:连接GEC。这一步刚开始检测到GEC在COM串口但是握手失败,发现是安装了老版本的环境但是下的工程文件是新版的,最后卸载了旧版环境安装了对应最新版的就成功连接上了,浪费我半天,最烦的是晚上某大学的金葫芦社区打不开,那手册又是全英文的几百页看不懂,气煞我也
步骤5:下载机器码
步骤6:观察运行结果
步骤7:通过串口观察运行情况
①把波特率设为115200并打开对应串口
②验证串口收发。
打开另一个串口,默认波特率,发送数据,启动!
(发的太快有时会显示乱码,原因是小日子排放的核污水污染了数据bushi)
第二章(1)
1.理解并学习main.s汇编源文件
写出94~101行语句的c语言描述
源代码(94-101):
//(2)======主循环部分(开头)=====================================
main_loop: //主循环标签(开头)
//(2.1)主循环次数变量mMainLoopCount+1
ldr r2,=mMainLoopCount //r2←mMainLoopCount的地址
ldr r1, [r2]
add r1,#1
str r1,[r2]
//(2.2)未达到主循环次数设定值,继续循环
ldr r2,=MainLoopNUM
cmp r1,r2
blO main_loop //未达到,继续循环
c语言描述:
void main_loop() {
// (2)======主循环部分(开头)=====================================
// 主循环开始
while(1) {
// (2.1)主循环次数变量mMainLoopCount+1
mMainLoopCount += 1;
// (2.2)未达到主循环次数设定值,继续循环
if (mMainLoopCount >= MainLoopNUM)
{
break; // 如果mMainLoopCount大于或等于MainLoopNUM,则退出循环
}
2.修改main.s源文件
修改main.s源文件,增加以下内容:
1、在第一行显示“广州大学”字样·
hello_information: //字符串标号
.ascii "-------------------------------------------------------\n"
.ascii "广州大学,庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆\n"
.ascii "------------------------------------------------------\n\0"
2、编写一个1+2+..+10的程序,将求和结果存入名为“sumresult”的内存单元中,并将求和结果用printf显示出来。(注:建议在源程序中“main loop:”语句之前的地方增加结束时用“bl.“停住。printf的格式详见05 UserBoard/printf.h文件)
//修改处
sumresult:
.word 0
mov r2, #0 // 将寄存器r2初始化为0,用来累计求和
mov r0, #10 // 将寄存器r0初始化为10,用于循环计数
p1:
add r2, r2, r0 // 累加r0到r2,即累计求和
sub r0, r0, #1 // 将r0减1
cmp r0, #0 // 比较r0与0
bne p1 // 如果不等于0,继续循环
ldr r1, =sumresult // 将sumresult的地址加载到r1
str r2, [r1] // 将计算结果存储到sumresult指向的内存空间
ldr r0, =data_format // 加载十进制格式字符串的地址到r0
ldr r1, =sumresult // 加载sumresult的地址到r1
ldr r1, [r1] // 从内存地址加载实际的计算结果到r1
bl printf // 调用printf函数,r0是格式字符串的地址,r1是值
bl . //在此打桩(.表示当前地址),理解发光二极管为何亮起来了?
//(1)======启动部分(结尾)=======================================
//(2)======主循环部分(开头)=====================================
结果
第二章(2)
1.学习main.s源程序
打开04-Software/ch02/CH02-1-20220118工程目录07 AppPrg/main.s源程序。
源程序(作业2):
//=====================================================================
//文件名称:main.s
//功能概要:汇编编程调用GPIO构件控制小灯闪烁(利用printf输出提示信息)
//版权所有:SD-ARM(sumcu.suda.edu.cn)
//版本更新:20180810-20191018
//=====================================================================
.include "include.inc" //头文件中主要定义了程序中需要使用到的一些常量
//(0)数据段与代码段的定义
//(0.1)定义数据存储data段开始,实际数据存储在RAM中
.section .data
//(0.1.1)定义需要输出的字符串,标号即为字符串首地址,\0为字符串结束标志
hello_information: //字符串标号
.ascii "-------------------------------------------------------\n"
.ascii "广州大学,庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆庆\n"
.ascii "------------------------------------------------------\n\0"
data_format:
.ascii "%d\n\0" //printf使用的数据格式控制符
data_format1:
.ascii "%08x:%02x\n\0" //printf使用的数据格式控制符,其中8表示输出位数,
//0表示将输出的前面补上0,直到占满指定列宽为止
light_show1:
.ascii "LIGHT_BLUE:ON--\n\0" //灯亮状态提示
light_show2:
.ascii "LIGHT_BLUE:OFF--\n\0" //灯暗状态提示
light_show3:
.ascii "闪烁次数mLightCount=\0" //闪烁次数提示
//(0.1.2)定义变量
.align 4 //.word格式四字节对齐
mMainLoopCount: //定义主循环次数变量
.word 0
mFlag: //定义灯的状态标志,1为亮,0为暗
.byte 'A'
.align 4
mLightCount:
.word 0
//修改处(作业2)
sumresult:
.word 0
//(0.2)定义代码存储text段开始,实际代码存储在Flash中
.section .text
.syntax unified //指示下方指令为ARM和thumb通用格式
.thumb //Thumb指令集
.type main function //声明main为函数类型
.global main //将main定义成全局函数,便于芯片初始化之后调用
.align 2 //指令和数据采用2字节对齐,兼容Thumb指令集
//--------------------------------------------------------------------
//main.c使用的内部函数声明处
//--------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程,参见书稿)
main:
//(1)======启动部分(开头)主循环前的初始化工作======================
//(1.1)声明main函数使用的局部变量
//(1.2)【不变】关总中断
cpsid i
//(1.3)给主函数使用的局部变量赋初值
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
// 初始化蓝灯, r0、r1、r2是gpio_init的入口参数
ldr r0,=LIGHT_BLUE //r0指明端口和引脚(用=,因常量>=256,需用ldr)
mov r1,#GPIO_OUTPUT //r1指明引脚方向为输出
mov r2,#LIGHT_OFF //r2指明引脚的初始状态为亮
bl gpio_init //调用gpio初始化函数
// 初始化串口UART_User1
mov r0,#UART_User //串口号
ldr r1,=UART_BAUD //波特率
bl uart_init //调用uart初始化函数
//(1.6)使能模块中断
mov r0,#UART_User //串口号
bl uart_enable_re_int //调用uart中断使能函数
//(1.7)【不变】开总中断
cpsie i
//显示hello_information定义的字符串
ldr r0,=hello_information //待显示字符串首地址
bl printf //调用printf显示字符串
//修改处(作业2)
mov r2, #0 // 将寄存器r2初始化为0,用来累计求和
mov r0, #10 // 将寄存器r0初始化为10,用于循环计数
p1:
add r2, r2, r0 // 累加r0到r2,即累计求和
sub r0, r0, #1 // 将r0减1
cmp r0, #0 // 比较r0与0
bne p1 // 如果不等于0,继续循环
ldr r1, =sumresult // 将sumresult的地址加载到r1
str r2, [r1] // 将计算结果存储到sumresult指向的内存空间
ldr r0, =data_format // 加载十进制格式字符串的地址到r0
ldr r1, =sumresult // 加载sumresult的地址到r1
ldr r1, [r1] // 从内存地址加载实际的计算结果到r1
bl printf // 调用printf函数,r0是格式字符串的地址,r1是值
bl . //在此打桩(.表示当前地址),理解发光二极管为何亮起来了?
//(1)======启动部分(结尾)=======================================
//(2)======主循环部分(开头)=====================================
main_loop: //主循环标签(开头)
//(2.1)主循环次数变量mMainLoopCount+1
ldr r2,=mMainLoopCount //r2←mMainLoopCount的地址
ldr r1, [r2]
add r1,#1
str r1,[r2]
//(2.2)未达到主循环次数设定值,继续循环
ldr r2,=MainLoopNUM
cmp r1,r2
blO main_loop //未达到,继续循环
//(2.3)达到主循环次数设定值,执行下列语句,进行灯的亮暗处理
//测试代码部分[理解机器码存储]
Label:
MOV R0,#0xDE //立即数范围为0x00~0xFF
ldr r0,=data_format1 //输出格式送r0
ldr r1,=Label //r1中是Label地址
ldrb r2,[r1] //r2中是Label地址中的数据
bl printf
ldr r0,=data_format1 //输出格式送r0
ldr r1,=Label+1 //r1中是Label+1地址
ldrb r2,[r1] //r2中是Label+1地址中的数据
bl printf
ldr r0,=data_format1 //输出格式送r0
ldr r1,=Label+2 //r1中是Label+2地址
ldrb r2,[r1] //r2中是Label+2地址中的数据
bl printf
ldr r0,=data_format1 //输出格式送r0
ldr r1,=Label+3 //r1中是Label+3地址
ldrb r2,[r1] //r2中是Label+3地址中的数据
bl printf
//(2.3.1)清除循环次数变量
ldr r2,=mMainLoopCount //r2←mMainLoopCount的地址
mov r1,#0
str r1,[r2]
//(2.3.2)如灯状态标志mFlag为'L',灯的闪烁次数+1并显示,改变灯状态及标志
//判断灯的状态标志
ldr r2,=mFlag
ldr r6,[r2]
cmp r6,#'L'
bne main_light_off //mFlag不等于'L'转
//mFlag等于'L'情况
ldr r3,=mLightCount //灯的闪烁次数mLightCount+1
ldr r1,[r3]
add r1,#1
str r1,[r3]
ldr r0,=light_show3 //显示“灯的闪烁次数mLightCount=”
bl printf
ldr r0,=data_format //显示灯的闪烁次数值
ldr r2,=mLightCount
ldr r1,[r2]
bl printf
ldr r2,=mFlag //灯的状态标志改为'A'
mov r7,#'A'
str r7,[r2]
ldr r0,=LIGHT_BLUE //亮灯
ldr r1,=LIGHT_ON
bl gpio_set
ldr r0, =light_show1 //显示灯亮提示
bl printf
//mFlag等于'L'情况处理完毕,转
b main_exit
//(2.3.3)如灯状态标志mFlag为'A',改变灯状态及标志
main_light_off:
ldr r2,=mFlag //灯的状态标志改为'L'
mov r7,#'L'
str r7,[r2]
ldr r0,=LIGHT_BLUE //暗灯
ldr r1,=LIGHT_OFF
bl gpio_set
ldr r0, =light_show2 //显示灯暗提示
bl printf
main_exit:
b main_loop //继续循环
//(2)======主循环部分(结尾)=====================================
.end //整个程序结束标志(结尾)
2.编写示例程序:datax、datay、dataz三个数中,找出最大值并显示。
(将该语句段放在main.s中“bl .”前位置。)
定义变量:
//(0.1.2)定义变量
.align 4 //.word格式四字节对齐
mMainLoopCount: //定义主循环次数变量
.word 0
mFlag: //定义灯的状态标志,1为亮,0为暗
.byte 'A'
.align 4
mLightCount:
.word 0
//修改处(作业2)
sumresult:
.word 0
//修改处(作业3)
datax:
.word 114514 // 定义变量datax并初始化为homo
datay:
.word 0x8765432d1 // 定义变量datay并初始化为16进制数8765432d1
dataz:
.word 7777777 // 定义变量dataz并初始化为7777777
datamax:
.space 4 // 为变量datamax分配4字节的空间,用于存放三个数中的最大值
data_format5:
.ascii "最大数字为:%d\n\0" // 定义字符串,用于printf格式化输出最大值
比大小:
//修改处(作业3)
ldr r1,=datax // 将x的地址加载到r1寄存器
ldr r2,[r1] // 将x的值加载到r2寄存器
ldr r1,=datay // 将y的地址加载到r1寄存器
ldr r3,[r1] // 将y的值加载到r3寄存器
//r2==x,r3==y
cmp r2,r3 // 比较r2和r3的值
bge tag1 // 如果r2>=r3,则跳转到tag1
mov r2,r3 // 如果r2<r3,将r3的值移动到r2,即r2现在是r2和r3中的较大者
//让较大值始终在r2
tag1:
ldr r1,=dataz // 将z的地址加载到r1寄存器
ldr r3,[r1] // 将z的值加载到r3寄存器
//r3==z把较小的值覆盖
cmp r2,r3 // 再次比较,这次是r2(当前最大值)与z的值
bge tag2 // 如果r2>=r3,跳转到tag2
mov r2,r3 // 如果r2<r3,更新r2为r3的值,此时r2为三个数中的最大值
//让较大值始终在r2
tag2:
ldr r1,=datamax // 将max的地址加载到r1寄存器
str r2,[r1] // 将最大值r2存储在max指定的地址
//得到三者最大值在datamax中
ldr r0,=data_format5 // 加载data_format5的地址到r0寄存器,r0用作printf的格式字符串
ldr r2,=datamax // 加载max的地址到r2寄存器
ldr r1,[r2] // 加载max的值到r1寄存器,r1用作printf的输出值
bl printf // 调用printf函数打印最大值
b . // 无限循环到当前地址,常用于嵌入式系统调试,防止程序继续执行未定义行为
bl . //在此打桩(.表示当前地址),理解发光二极管为何亮起来了?
连接板子,编译并更新串口后的结果:
(十进制结果为1985229521,跟datay:0x8765432d1不相等,啊?)
在32位系统中,一个整数(通常是int或unsigned int类型)可以表示的最大无符号值是0xFFFFFFFF,等于十进制的4294967295。而datay值实际上是一个40位的数,超过了32位可以表示的范围。当这个值被存储到一个32位的变量中时,它会被截断,只保留最低的32位。0x8765432d1的二进制表示为1000 0111 0110 0101 0100 0011 0010 1101 0001。当它被截断为32位时,最高的8位(1000)会被丢弃,这恰好是十六进制的0x765432d1,对应的十进制值为1985229521。
第三章
1.对照命名格式,给出所用MCU芯片型号标识所获得的信息
认识一个MCU,从了解型号含义开始,一般来说,主要包括芯片家族、产品类型、具体特性、引脚数目、Flash大小、封装类型及温度范围等。
STM32L系列芯片的命名格式为 STM32 X AAA Y B T C
字段 | 说明 | 取值 |
STM32 | 芯片家族 | 表示32位MCU |
X | 产品类型 | F:基础型 |
L:超低功耗型 | ||
W:无线系统芯片 | ||
AAA | 具体特性 (取决于产品系列) | 0xx:入门级MCU |
1xx:主流MCU | ||
2xx:高性能MCU | ||
4xx:高性能微控制器,具有DSP和FPU指令 | ||
7xx:配备ARM-Cortex-M7内核的超高性能MCU | ||
Y | 引脚数目 | T表示36;C表示48;R表示64;V表示100;Z表示144;B表示208;N表示216 |
B | Flash大小 | 8:64KB |
C:256KB | ||
E:512KB | ||
I:2048KB | ||
T | 封装类型 | T:LQFP封装 |
H:BGA封装 | ||
I:UFBGA封装 | ||
C | 温度范围 | 6/A: -40℃ ~ +85℃ |
7/B:-40℃ ~ +105℃ | ||
3/C:-40℃ ~ +125℃ | ||
D: -40℃ ~ +150℃ |
本书所使用的MCU型号为STM32L431RCT6。对照命名格式,可以从型号获得以下信息:属于32位的MCU,超低功耗型,高性能微控制器,引脚数为64,Flash大小为256KB,封装形式为64引脚LQFP封装,工作温度范围为-40℃ ~ +85℃
2.给出所用MCU芯片的RAM及Flash大小、地址范围
在苏州大学嵌入式社区下载的文件中找到数据手册
RAM信息:64kb的嵌入式静态RAM
Flash信息:256KB
地址范围:代码区的地址范围是0.5G,Flash的地址范围是0x0800 0000到0x0804 0000
第四章
1.学习CH04示例程序,包括gpio.c和4个工程中的main.c
(1)GPIO-ASM-STM32L431-20231129的main.s
功能:使用汇编调用GPIO实现蓝色小灯的闪烁。
(2)GPIO_BLUELIGHT-20230328的main.c
功能:c语言实现红灯常亮。
(3)GPIO-Output-Component_STM32L431_20200928的main.c
功能:利用api实现蓝灯的循环亮灭
(4)GPIO-Output-DirectAddress_STM32L431_20200928的main.c
功能:用直接操作寄存器地址的方式来控制蓝色小灯的亮灭
2.给出gpio_set(LIGHT_RED,LIGHT_OFF);语句中LIGHT_RED和LIGHT_OFF的值是多少?贴出每一步的查找截图
找到user.h文件
看到LIGHT_RED由PTB_NUM|7定义
LIGHT_OFF的值为 1
在gpio.h文件中找到了PTB_NUM定义为1<<8,综上可知LIGHT_RED的值是263
3.用直接地址编程方式,实现红绿蓝三灯轮流闪烁
文件(4)/07_AppPrg/main.c
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
//----------------------------------------------------------------------
//声明使用到的内部函数
//main.c使用的内部函数声明处
//----------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程见书稿)
int main(void)
{
//(1)======启动部分(开头)==========================================
//(1.1)声明main函数使用的局部变量
uint32_t mMainLoopCount; //主循环使用的记录主循环次数变量
//uint8_t mFlag; //主循环使用的临时变量
//(1.2)【不变】关总中断
DISABLE_INTERRUPTS;
//(1.3)给主函数使用的局部变量赋初值
mMainLoopCount = 0; //主循环使用的记录主循环次数变量
//mFlag='A'; //主循环使用的临时变量:蓝灯状态标志
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
// B口9脚(蓝灯,低电平点亮)
//(1.5.1)声明变量
volatile uint32_t* RCC_AHB2; //GPIO的B口时钟使能寄存器地址
volatile uint32_t* gpio_ptr; //GPIO的B口基地址
volatile uint32_t* gpio_mode; //引脚模式寄存器地址=口基地址
volatile uint32_t* gpio_bsrr; //置位/复位寄存器地址
volatile uint32_t* gpio_brr; //GPIO位复位寄存器
//(1.5.2)变量赋值
RCC_AHB2=(uint32_t*)0x4002104C; //GPIO的B口时钟使能寄存器地址
gpio_ptr=(uint32_t*)0x48000400; //GPIO的B口基地址
gpio_mode=gpio_ptr; //引脚模式寄存器地址=口基地址
gpio_bsrr=gpio_ptr+6; //置位/复位寄存器地址
gpio_brr=gpio_ptr+10; //GPIO位复位寄存器
//(1.5.3)GPIO初始化
//(1.5.3.1)使能相应GPIOB的时钟
*RCC_AHB2|=(1<<1); //GPIOB的B口时钟使能
//(1.5.3.1)定义B口9脚为输出引脚(令D19、D18=01)方法如下:
//这里使得B9变为了01,输出模式,亮蓝灯
*gpio_mode &= ~(3<<18); //0b11111111111100111111111111111111;
*gpio_mode |=(1<<18); //0b00000000000001000000000000000000;
//这里使得B8变为了01输出模式,绿灯亮
*gpio_mode &= ~(3<<16); //0b11111111111111001111111111111111;
*gpio_mode |=(1<<16); //0b00000000000000010000000000000000;
//这里使得B7变为了01输出模式,红灯亮
*gpio_mode &= ~(3<<14); //0b11111111111111110011111111111111;
*gpio_mode |=(1<<14); //0b00000000000000000100000000000000;
//(思考:为什么这样赋值?答案见本文件末尾注①)
//(1.6)使能模块中断
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
printf("庆大学 XJD777\r\n");
//for(;;) { } //在此打桩,理解蓝色发光二极管为何亮起来了?
//(1)======启动部分(结尾)==========================================
//(2)======主循环部分(开头)=========================================
for(;;)
{
//(2.1)主循环次数+1,并判断是否小于特定常数
mMainLoopCount++; //+1
if (mMainLoopCount<=6556677) continue; //如果小于特定常数,继续循环
//(2.2)主循环次数超过特定常数,灯状态进行切换(这样灯会闪烁)
mMainLoopCount=0; //清主循环次数
//红灯
//切换灯状态
*gpio_brr|=(1<<7); //设置灯“亮”
printf("红灯亮\r\n"); //通过调试串口输出灯的状态
for(int i=0;i<10000000;i++){}
*gpio_bsrr|=(1<<7); //设置灯“暗”
printf("红灯灭\r\n"); //通过调试串口输出灯的状态
for(int i=0;i<10000000;i++){}
//绿灯
*gpio_brr|=(1<<8); //设置灯“亮”
printf("绿灯亮\r\n"); //通过调试串口输出灯的状态
for(int i=0;i<10000000;i++){}
*gpio_bsrr|=(1<<8); //设置灯“暗”
printf("绿灯灭\r\n"); //通过调试串口输出灯的状态
for(int i=0;i<10000000;i++){}
//蓝灯
*gpio_brr|=(1<<9); //设置灯“亮”
printf("蓝灯亮\r\n"); //通过调试串口输出灯的状态
for(int i=0;i<10000000;i++){}
*gpio_bsrr|=(1<<9); //设置灯“暗”
printf("蓝灯灭\r\n"); //通过调试串口输出灯的状态
for(int i=0;i<10000000;i++){}
}
//(2)======主循环部分(结尾)========================================
}
结果
三灯闪烁
4.用调用构件方式,实现红绿蓝的八种组合轮流闪烁
文件(3)/07_AppPrg/main.c
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
//----------------------------------------------------------------------
//声明使用到的内部函数
//main.c使用的内部函数声明处
//----------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程,参见书稿)
int main(void)
{
//(1)======启动部分(开头)==========================================
//(1.1)声明main函数使用的局部变量
uint32_t mMainLoopCount; //主循环次数变量
//uint8_t mFlag; //灯的状态标志
uint32_t mLightCount; //灯的状态切换次数
uint8_t groupFlag;
//(1.2)【不变】关总中断
DISABLE_INTERRUPTS;
//(1.3)给主函数使用的局部变量赋初值
mMainLoopCount=1; //主循环次数变量
//mFlag='A'; //灯的状态标志
mLightCount=0; //闪烁循环次数
groupFlag = 0; //8种组合闪烁的标志,0~7分别对应代表暗,红,绿,黄,蓝,紫,青,白
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
//gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON); //初始化蓝灯
//初始化三盏灯,但初始状态为黑
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF); //红灯
gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF); //绿灯
gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF); //蓝灯
//(1.6)使能模块中断
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
printf("-------------------------------\n");
printf("庆XJD777 调用构件八灯闪烁\n");
printf("-------------------------------\n");
//(1)======启动部分(结尾)==========================================
//(2)======主循环部分(开头)========================================
for(;;)
{
//(2.1)主循环次数变量+1
mMainLoopCount++;
//(2.2)未达到主循环次数设定值,继续循环
if (mMainLoopCount<=12888999) continue;
//(2.3)达到主循环次数设定值,执行下列语句,进行灯的亮暗处理
//(2.3.1)清除循环次数变量
mMainLoopCount=0;
if(groupFlag == 0) //暗
{
mLightCount++;
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
printf("当前循环次数:%d\n",mLightCount);
printf("暗灯\n");
groupFlag = 1;
continue;
}
if(groupFlag == 1) //红
{
gpio_set(LIGHT_RED,LIGHT_ON);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
printf("红灯\n");
groupFlag = 2;
continue;
}
if(groupFlag == 2) //绿
{
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_GREEN,LIGHT_ON);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
printf("绿灯\n");
groupFlag = 3;
continue;
}
if(groupFlag == 3) //黄=红+绿
{
gpio_set(LIGHT_RED,LIGHT_ON);
gpio_set(LIGHT_GREEN,LIGHT_ON);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
printf("黄灯\n");
groupFlag = 4;
continue;
}
if(groupFlag == 4) //蓝
{
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_ON);
printf("蓝灯\n");
groupFlag = 5;
continue;
}
if(groupFlag == 5) //紫=红+蓝
{
gpio_set(LIGHT_RED,LIGHT_ON);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_ON);
printf("紫灯\n");
groupFlag = 6;
continue;
}
if(groupFlag == 6) //青=蓝+绿
{
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_GREEN,LIGHT_ON);
gpio_set(LIGHT_BLUE,LIGHT_ON);
printf("青灯\n");
groupFlag = 7;
continue;
}
if(groupFlag == 7) //白=红+蓝+绿
{
gpio_set(LIGHT_RED,LIGHT_ON);
gpio_set(LIGHT_GREEN,LIGHT_ON);
gpio_set(LIGHT_BLUE,LIGHT_ON);
printf("白灯\n");
groupFlag = 0;
continue;
}
}
结果
八灯闪烁
第六章(1)
1.编写UART_2串口发送程序时,初始化需要设置哪些参数?
时钟使能寄存器地址:
RCC_APB1: UART的时钟使能寄存器地址,用于使能UART时钟。
RCC_AHB2: GPIO的A口时钟使能寄存器地址,用于使能GPIOA的时钟。
端口基地址:
gpio_ptr: GPIOA端口的基地址,用于配置GPIO端口的相关设置。
uart_ptr: UART2端口的基地址,用于配置UART相关寄存器。
引脚模式寄存器地址和复用功能寄存器地址:
gpio_mode: GPIO引脚模式寄存器地址,用于配置引脚模式。
gpio_afrl: GPIO复用功能低位寄存器,用于配置引脚的复用功能。
UART相关寄存器地址:
uart_cr1: UART控制寄存器1基地址,用于配置UART的工作模式和使能相关功能。
uart_brr: UART波特率寄存器地址,用于配置波特率。
uart_isr: UART中断和状态寄存器基地址,用于配置中断相关设置。
uart_cr2: UART控制寄存器2基地址,用于配置UART的工作模式。
uart_cr3: UART控制寄存器3基地址,用于配置UART的工作模式。
波特率设置参数:
根据波特率计算得到的USARTDIV值,写入到UART波特率寄存器(uart_brr)中。
其他设置:
清除相应的标志位,如中断状态寄存器中的标志位。
禁用相应的功能,如关闭UART功能、关闭发送和接收功能。
使能UART功能:
启用UART发送和接收功能。
最后开启UART功能使能位,使UART开始正常工作。
2.假设速度为115200,系统时钟为72MHz,波特率寄存器BRR中的值应该是多少?
USART BRR寄存器(中的USARTDIV值用来计算串口的通信速率。假设USART CR1中第15位“过采样”模式为:
过采样因子为8:USARTDIV = 2*72MHz / 115200 = 1250
过采样因子为16:USARTDIV = 72MHz / 115200 = 625
3.中断向量表在哪个文件中?表中有多少项?给出部分截图。
中断向量表在\03_MCU\startup\startup_stm32|431rctx.s中。表中有98项。前16项为内核中断,后面的为非内核中断
4.以下是中断源使能函数,假设中断源为TIM6,将函数实例化(写出各项具体数值)。
__STATIC INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32 t)(IRQn) >= 0)
{
NVIC -> ISER [(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32 t)IRQn) & 0x1FUL));
}
}
实例化:
找到TIM6的中断号为54
54/32=1 54%32=22, 将ISER[1]的第22位设置为1
//函数内部实现将IRQ号值右移5位,54>>5=1,索引值为1
ISER[(((uint32_t)IRQn) >> 5UL)]
//54 & 0x1F=22,第22位
(uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL))
5.假设将UART_2和TIM6交换其在中断向量表中的位置和IRQ号, UART_2可以正常中断吗?
可以正常中断,因为中断的处理逻辑还依赖于硬件中断控制器(NVIC)的配置以及中断服务例程(ISR)的正确实现。
第六章(2/实验二)
1.运行并理解..\04\-Software\CH06文件夹中的几个程序。
UART-STM32L431-ADDR-20210103:
通过直接操作寄存器实现GPIO和UART的初始化和配置。通过UART发送数据,并在循环中不断计数和打印发送次数。
UART-STM32L431-ISR-20210109:使用函数封装方式初始化硬件,并通过计数器控制LED灯的状态。
开发板蓝灯闪烁。
UART-STM32L431-Sent-20210103:结合了LED状态控制和UART通信,通过计数器和状态反转实现LED灯的控制,并增加了串口数据发送和长浮点数输出。
每次蓝灯亮和暗都会发送一次数据。
2.实现UART_2串口的接收程序,当收到字符时:
①在电脑的输出窗口显示下一个字符,如收到A显示B;
②亮灯:收到字符G亮绿灯;收到R亮红灯;收到B亮蓝灯;收到其他字符不亮灯。
2.1 用构件调用方式实现。
isr.c部分代码
void USART2_IRQHandler(void)
{
uint8_t ch;
uint8_t flag;
DISABLE_INTERRUPTS; //关总中断
//接收一个字节的数据
ch = uart_re1(UART_User,&flag); //调用接收一个字节的函数,清接收中断位
if(flag) //有数据
{
if((ch == 'R')||(ch == 'r')) //打开红灯(开灯函数为金葫芦编写)
{
gpio_set(LIGHT_GREEN,LIGHT_OFF); //关闭绿灯
gpio_set(LIGHT_BLUE,LIGHT_OFF); //关闭蓝灯
gpio_set(LIGHT_RED,LIGHT_ON); //打开红灯
}
else if((ch == 'G')||(ch == 'g')) //打开绿灯
{
gpio_set(LIGHT_BLUE,LIGHT_OFF); //关闭蓝灯
gpio_set(LIGHT_RED,LIGHT_OFF); //关闭红灯
gpio_set(LIGHT_GREEN,LIGHT_ON); //打开绿灯
}
else if((ch == 'B')||(ch == 'b')) //打开蓝灯
{
gpio_set(LIGHT_RED,LIGHT_OFF); //关闭红灯
gpio_set(LIGHT_GREEN,LIGHT_OFF); //关闭绿灯
gpio_set(LIGHT_BLUE,LIGHT_ON); //打开蓝灯
}
uart_send1(UART_User,ch + 1); //回发接收到的字节
}
ENABLE_INTERRUPTS; //开总中断
}
显示下一个字符:
输入RGB亮灯:
2.2 UART部分用直接地址方式实现(即不调用uart.c中的函数,其他部分如GPIO、中断设置可调用函数)。
isr.c部分代码
//USART2中断服务函数
void USART2_IRQHandler(void)
{
volatile uint32_t* usart2 = (volatile uint32_t*)0x40004400UL; //usart2寄存器基地址
volatile uint32_t* usart2_cr1 = usart2; //usart2控制寄存器1基地址
volatile uint32_t* usart2_isr = usart2 + 7; //usart2状态寄存器基地址
volatile uint32_t* usart2_rdr = usart2 + 9; //usart2接收数据寄存器基地址
volatile uint32_t* usart2_tdr = usart2 + 10; //usart2发送数据寄存器基地址
volatile uint32_t* nvic_icer = (volatile uint32_t*)0xE000E180UL; //nvic中断清除使能寄存器基地址
volatile uint32_t* nvic_icpr = (volatile uint32_t*)0xE000E280UL; //nvic中断清除挂起寄存器
uint8_t data;
DISABLE_INTERRUPTS; //关总中断
if((*usart2_cr1)&(0x1UL<<5U)) //判断是否使能接收缓冲区非空中断
{
for (uint32_t i = 0; i < 0xFFFF; ++i)//查询指定次数
{
if((*usart2_isr)&(0x1UL<<5U)) //判断读取数据寄存器是否非空
{
data = *usart2_rdr; //读取接收寄存器
if((data == 'R')||(data == 'r')) //打开红灯(开灯函数为金葫芦编写)
{
gpio_set(LIGHT_GREEN,LIGHT_OFF); //关闭绿灯
gpio_set(LIGHT_BLUE,LIGHT_OFF); //关闭蓝灯
gpio_set(LIGHT_RED,LIGHT_ON); //打开红灯
}
else if((data == 'G')||(data == 'g')) //打开绿灯
{
gpio_set(LIGHT_BLUE,LIGHT_OFF); //关闭蓝灯
gpio_set(LIGHT_RED,LIGHT_OFF); //关闭红灯
gpio_set(LIGHT_GREEN,LIGHT_ON); //打开绿灯
}
else if((data == 'B')||(data == 'b')) //打开蓝灯
{
gpio_set(LIGHT_RED,LIGHT_OFF); //关闭红灯
gpio_set(LIGHT_GREEN,LIGHT_OFF); //关闭绿灯
gpio_set(LIGHT_BLUE,LIGHT_ON); //打开蓝灯
}
for (uint32_t j = 0; j < 0xFFFF; ++j)//查询指定次数
{
if((*usart2_isr)&(0x1UL<<7U)) //判断发送数据寄存器是否为空
{
*usart2_tdr = data + 1; //回发接收到的内容(内容加一)
break;
}
}//end for
break;
}
}//end for
}
ENABLE_INTERRUPTS; //开总中断
}
结果同上。
第七章
8.1
8.2
第十章(实验四)
10.1对于can的驱动函数文件加注释。在can(加注释).c中标了“//2024.6”的语句加以理解并写出注释。
//======================================================================
//文件名称:can.c
//功能概要:uart底层驱动构件源文件
//版权所有:苏州大学嵌入式系统与物联网研究所(sumcu.suda.edu.cn)
//更新记录:2021-02-03 V1.0 JJL
//======================================================================
#include "can.h"
CAN_TypeDef *CAN_ARR[] = {(CAN_TypeDef*)CAN1_BASE};
IRQn_Type table_irq_can[2] = {CAN1_RX0_IRQn, CAN1_RX1_IRQn};
uint8_t can_send_once(uint8_t canNo, uint32_t DestID, uint16_t len ,uint8_t* buff);
uint8_t CAN_HWInit(uint8_t CANChannel);
uint8_t CAN_SWInit_Entry(uint8_t canNo);
void CAN_SWInit_CTLMode(uint8_t canNo);
void CAN_SWInit_BT(uint8_t canNo, uint32_t CANMode, uint32_t Prescaler);
uint8_t CAN_SWInit_Quit(uint8_t canNo);
uint8_t CANFilterConfig(uint8_t canNo, uint32_t canID, uint32_t FilterBank, uint32_t Can_Rx_FifoNo, uint8_t IsActivate, uint32_t FilterMode, uint32_t FilterScale);
//=====================================================================
//函数名称:can_init
//函数返回:无
//参数说明:canNo:模块号,本芯片只有CAN_1
// canID:自身CAN节点的唯一标识,例如按照CANopen协议给出
// BitRate:位速率
//功能概要:初始化CAN模块
//=====================================================================
void can_init(uint8_t canNo, uint32_t canID, uint32_t BitRate)
{
//声明Init函数使用的局部变量
uint32_t CANMode;
uint32_t CANFilterBank;
uint32_t CANFiltermode;
uint32_t CAN_Filterscale;
//给Init函数使用的局部变量赋初值
CANMode = CAN_MODE_NORMAL; //2024.6
CANFilterBank = CANFilterBank0;
CANFiltermode = CAN_FILTERMODE_IDMASK;
CAN_Filterscale = CAN_FILTERSCALE_32BIT;
//(1)CAN总线硬件初始化
CAN_HWInit(CAN_CHANNEL);
//(2)CAN总线进入软件初始化模式
CAN_SWInit_Entry(canNo);
//(3)CAN总线模式设置
CAN_SWInit_CTLMode(canNo);
//(4)CAN总线位时序配置
CAN_SWInit_BT(canNo,CANMode,BitRate);
//(5)CAN总线过滤器初始化
CANFilterConfig(canNo, canID, CANFilterBank, CAN_RX_FIFO0, 1, CANFiltermode, CAN_Filterscale);
//(6)CAN总线退出软件初始化模式,进入正常模式
CAN_SWInit_Quit(canNo);
}
//=====================================================================
//函数名称:can_send
//函数返回:0=正常,1=错误
//参数说明:canNo:模块号,本芯片只有CAN_1
// DestID:目标CAN节点的唯一标识,例如按照CANopen协议给出
// len:待发送数据的字节数
// buff:待发送数据发送缓冲区首地址
//功能概要:CAN模块发送数据
//=====================================================================
uint8_t can_send(uint8_t canNo, uint32_t DestID, uint16_t len ,uint8_t* buff)
{
if(DestID > 0x1FFFFFFFU) return 1;
uint8_t send_length;
for(int i = len; i > 0; i = i-8)
{
send_length = (i>8)?8:i;
if(can_send_once(canNo,DestID,send_length,buff+len-i) == 1) //2024.6
{
return 1;
}
}
return 0;
}
//=====================================================================
//函数名称:can_recv
//函数返回:接收到的字节数
//参数说明:canNo:模块号,本芯片只有CAN_1
// buff:接收到的数据存放的内存区首地址
//功能概要:在CAN模块接收中断中调用本函数接收已经到达的数据
//=====================================================================
uint8_t can_recv(uint8_t canNo, uint8_t *buff)
{
uint8_t len;
uint32_t RxFifo = CAN_RX_FIFO0;
//(1)判断哪个邮箱收到了报文信息
if(RxFifo == CAN_RX_FIFO0)
{
if ((CAN_ARR[canNo-1]->RF0R & CAN_RF0R_FMP0) == 0U) //2024.6
{
return 1;
}
}
else
{
if ((CAN_ARR[canNo-1]->RF1R & CAN_RF1R_FMP1) == 0U)
{
return 1;
}
}
//(2)获取数据长度
len = (CAN_RDT0R_DLC & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDTR) >> CAN_RDT0R_DLC_Pos; //2024.6
//(3)获取数据帧中的数据
buff[0] = (uint8_t)((CAN_RDL0R_DATA0 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDLR) >> CAN_RDL0R_DATA0_Pos);
buff[1] = (uint8_t)((CAN_RDL0R_DATA1 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDLR) >> CAN_RDL0R_DATA1_Pos);
buff[2] = (uint8_t)((CAN_RDL0R_DATA2 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDLR) >> CAN_RDL0R_DATA2_Pos);
buff[3] = (uint8_t)((CAN_RDL0R_DATA3 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDLR) >> CAN_RDL0R_DATA3_Pos);
buff[4] = (uint8_t)((CAN_RDH0R_DATA4 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDHR) >> CAN_RDH0R_DATA4_Pos);
buff[5] = (uint8_t)((CAN_RDH0R_DATA5 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDHR) >> CAN_RDH0R_DATA5_Pos);
buff[6] = (uint8_t)((CAN_RDH0R_DATA6 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDHR) >> CAN_RDH0R_DATA6_Pos);
buff[7] = (uint8_t)((CAN_RDH0R_DATA7 & CAN_ARR[canNo-1]->sFIFOMailBox[RxFifo].RDHR) >> CAN_RDH0R_DATA7_Pos);
//(4)清除标志位,等待接收下一帧数据
if (RxFifo == CAN_RX_FIFO0)
{
SET_BIT(CAN_ARR[canNo-1]->RF0R, CAN_RF0R_RFOM0); //2024.6
}
else
{
SET_BIT(CAN_ARR[canNo-1]->RF1R, CAN_RF1R_RFOM1);
}
return len;
}
//=====================================================================
//函数名称:CAN_enable_re_int
//函数返回:无
//参数说明:canNo:模块基地址号,Can_Rx_FifoNo:中断使用的邮箱号
//功能概要:CAN接收中断开启
//=====================================================================
void can_enable_recv_int(uint8_t canNo)
{
uint8_t Can_Rx_FifoNo;
Can_Rx_FifoNo = CAN_RX_FIFO0;
if(Can_Rx_FifoNo == CAN_RX_FIFO0)
SET_BIT(CAN_ARR[canNo-1]->IER, CAN_IER_FMPIE0); //2024.6
else
SET_BIT(CAN_ARR[canNo-1]->IER,CAN_IER_FMPIE1);
NVIC_EnableIRQ(table_irq_can[Can_Rx_FifoNo]); //2024.6
}
//=====================================================================
//函数名称:can_disable_recv_int
//函数返回:无
//参数说明:canNo:模块号,本芯片只有CAN_1
//功能概要:关闭CAN接收中断
//=====================================================================
void can_disable_recv_int (uint8_t canNo)
{
uint8_t Can_Rx_FifoNo;
Can_Rx_FifoNo = CAN_RX_FIFO0;
if(Can_Rx_FifoNo == CAN_RX_FIFO0)
CLEAR_BIT(CAN_ARR[canNo-1]->IER, CAN_IER_FMPIE0);
else
CLEAR_BIT(CAN_ARR[canNo-1]->IER,CAN_IER_FMPIE1);
NVIC_DisableIRQ(table_irq_can[Can_Rx_FifoNo]);
}
//=====================================================================
//函数名称:can_send_once
//函数返回:0=正常,1=错误
//参数说明:canNo:模块号,本芯片只有CAN_1
// DestID:目标CAN节点的唯一标识,例如按照CANopen协议给出
// len:待发送数据的字节数
// buff:待发送数据发送缓冲区首地址
//功能概要:CAN模块发送一次数据
//=====================================================================
uint8_t can_send_once(uint8_t canNo, uint32_t DestID, uint16_t len ,uint8_t* buff)
{
//(1)定义Can发送函数所需要用到的变量
uint32_t transmit_mailbox;
uint32_t register_tsr;
uint32_t rtr;
rtr = CAN_RTR_DATA;
register_tsr = READ_REG(CAN_ARR[canNo-1]->TSR);
//(2)判断3个邮箱中是否有空闲邮箱,若有,选取其中一个进行发送,选取顺序为1,2,3
if (((register_tsr & CAN_TSR_TME0) != 0U) ||
((register_tsr & CAN_TSR_TME1) != 0U) ||
((register_tsr & CAN_TSR_TME2) != 0U))
{
transmit_mailbox = (register_tsr & CAN_TSR_CODE) >> CAN_TSR_CODE_Pos; //2024.6
if(transmit_mailbox > 2U)
{
return 1;
}
//(2.1)判断并设置发送帧为标准帧还是扩展帧
if(DestID <= 0x7FFU) //2024.6
{
CAN_ARR[canNo-1]->sTxMailBox[transmit_mailbox].TIR = ((DestID << CAN_TI0R_STID_Pos)|CAN_ID_STD|rtr); //2024.6
}
else
{
CAN_ARR[canNo-1]->sTxMailBox[transmit_mailbox].TIR = ((DestID << CAN_TI0R_EXID_Pos)|CAN_ID_EXT|rtr); //2024.6
}
//(2.2)设置发送帧的数据长度
CAN_ARR[canNo-1]->sTxMailBox[transmit_mailbox].TDTR = len;
//SET_BIT(CAN_ARR[canNo-1]->sTxMailBox[transmit_mailbox].TDTR, CAN_TDT0R_TGT);
//(2.3)设置发送帧的数据
WRITE_REG(CAN_ARR[canNo-1]->sTxMailBox[transmit_mailbox].TDHR, //2024.6
((uint32_t)buff[7] << CAN_TDH0R_DATA7_Pos) |
((uint32_t)buff[6] << CAN_TDH0R_DATA6_Pos) |
((uint32_t)buff[5] << CAN_TDH0R_DATA5_Pos) |
((uint32_t)buff[4] << CAN_TDH0R_DATA4_Pos));
WRITE_REG(CAN_ARR[canNo-1]->sTxMailBox[transmit_mailbox].TDLR,
((uint32_t)buff[3] << CAN_TDL0R_DATA3_Pos) |
((uint32_t)buff[2] << CAN_TDL0R_DATA2_Pos) |
((uint32_t)buff[1] << CAN_TDL0R_DATA1_Pos) |
((uint32_t)buff[0] << CAN_TDL0R_DATA0_Pos));
//(2.4)发送Can数据报
SET_BIT(CAN_ARR[canNo-1]->sTxMailBox[transmit_mailbox].TIR, CAN_TI0R_TXRQ); //2024.6
return 0;
}
else
{
return 1;
}
}
//=====================================================================
//函数名称:CAN_HWInit
//函数返回:0=正常,1=错误
//参数说明:CANChannel:硬件引脚组号,共有3组,分别为PTA11&PTA12(CAN_CHANNEL0),PTB8&PTB9(CAN_CHANNEL1),PTD0&PTD1(2)
//功能概要:CAN模块引脚初始化
//=====================================================================
uint8_t CAN_HWInit(uint8_t CANChannel)
{
if(CANChannel < 0 || CANChannel > 2)
{
return 1;
}
if(CANChannel == 0)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_CAN1EN; //2024.6
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
GPIOA->MODER &= ~(GPIO_MODER_MODE11|GPIO_MODER_MODE12);
GPIOA->MODER |= (GPIO_MODER_MODE11_1|GPIO_MODER_MODE12_1); //2024.6
GPIOA->AFR[1] &= ~(GPIO_AFRH_AFSEL11|GPIO_AFRH_AFSEL12);
GPIOA->AFR[1] |= (GPIO_AFRH_AFSEL11_0|GPIO_AFRH_AFSEL11_3)|(GPIO_AFRH_AFSEL12_0|GPIO_AFRH_AFSEL12_3); //2024.6
}
else if(CANChannel == 1)
{
RCC->APB1ENR1 |= RCC_APB1ENR1_CAN1EN;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN;
GPIOB->MODER &= ~(GPIO_MODER_MODE8|GPIO_MODER_MODE9);
GPIOB->MODER |= (GPIO_MODER_MODE8_1|GPIO_MODER_MODE9_1);
GPIOB->AFR[1] &= ~(GPIO_AFRH_AFSEL8|GPIO_AFRH_AFSEL9);
GPIOB->AFR[1] |= ((GPIO_AFRH_AFSEL8_0|GPIO_AFRH_AFSEL8_3)|
(GPIO_AFRH_AFSEL9_0|GPIO_AFRH_AFSEL9_3));
}
else
{
RCC->APB1ENR1 |= RCC_APB1ENR1_CAN1EN;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIODEN;
GPIOD->MODER &= ~(GPIO_MODER_MODE0|GPIO_MODER_MODE1);
GPIOD->MODER |= (GPIO_MODER_MODE0_1|GPIO_MODER_MODE1_1);
GPIOD->AFR[0] &= ~(GPIO_AFRL_AFSEL0|GPIO_AFRL_AFSEL1);
GPIOD->AFR[0] |= ((GPIO_AFRL_AFSEL0_0 | GPIO_AFRL_AFSEL0_3)|
(GPIO_AFRL_AFSEL1_0 | GPIO_AFRL_AFSEL1_3));
}
return 0;
}
//=====================================================================
//函数名称:CAN_SWInit_Entry
//函数返回:0=正常,1=错误
//参数说明:canNo:模块基地址号,本芯片只有CAN_1,
//功能概要:进入初始化模式
//=====================================================================
uint8_t CAN_SWInit_Entry(uint8_t canNo)
{
int i;
CLEAR_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_SLEEP); //2024.6
i = 0;
while ((CAN_ARR[canNo-1]->MSR & CAN_MSR_SLAK) != 0U) //2024.6
{
if(i++ > 0x30000)
{
return 1;
}
}
SET_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_INRQ); //2024.6
i = 0;
while ((CAN_ARR[canNo-1]->MSR & CAN_MSR_INAK) == 0U)
{
if(i++ > 0x30000)
{
return 1;
}
}
return 0;
}
//=====================================================================
//函数名称:CAN_SWInit_CTLMode
//函数返回:无
//参数说明:canNo:模块基地址号,本芯片只有CAN_1,
//功能概要:CAN总线模式设置
//=====================================================================
void CAN_SWInit_CTLMode(uint8_t canNo)
{
CLEAR_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_TTCM); //2024.6
CLEAR_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_ABOM); //2024.6
CLEAR_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_AWUM); //2024.6
SET_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_NART); //2024.6
CLEAR_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_RFLM); //2024.6
CLEAR_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_TXFP); //2024.6
}
//函数名称:CAN_SWInit_CTLMode
//函数返回:无
//参数说明:canNo:模块基地址号,本芯片只有CAN_1,
// CANMode:CAN总线工作模式,分别为正常模式(CAN_MODE_NORMAL)、回环模式(CAN_MODE_LOOPBACK)、
// 静默模式(CAN_MODE_SILENT)以及回环与静默组合模式(CAN_MODE_SILENT_LOOPBACK)
//功能概要:CAN总线位时序配置
void CAN_SWInit_BT(uint8_t canNo, uint32_t CANMode, uint32_t Prescaler)
{
CAN_ARR[canNo-1]->BTR |= ((uint32_t)(Prescaler-1)|CAN_SJW_1TQ|CAN_BTR_TS1_1|CAN_BTR_TS1_0|CAN_BTR_TS2_2|CANMode); //2024.6
}
//=====================================================================
//函数名称:CAN_SWInit_Quit
//函数返回:0=正常,1=错误
//参数说明:canNo:模块基地址号
//功能概要:退出初始化模式,进入正常模式
//=====================================================================
uint8_t CAN_SWInit_Quit(uint8_t canNo)
{
int i;
CLEAR_BIT(CAN_ARR[canNo-1]->MCR, CAN_MCR_INRQ); //2024.6
i = 0;
while ((CAN_ARR[canNo-1]->MSR & CAN_MSR_INAK) != 0U) //2024.6
{
if (i++ > 0x30000)
{
return 1;
}
}
return 0;
}
//=====================================================================
//函数名称: CANFilterConfig
//函数返回:0=正常,1=错误
//参数说明: canNo:模块基地址号,
// canID:自身CAN节点的唯一标识,例如按照CANopen协议给出
// Can_Rx_FifoNo:中断使用的邮箱号,
// IsActivate:是否激活过滤器
// CANFilterBank:CAN总线过滤器组选择,共有28个,(CANFilterBank0~CANFilterBank27)
// CANFiltermode:CAN总线过滤器模式,分别为掩码模式(CAN_FILTERMODE_IDMASK)和列表模式(CAN_FILTERMODE_IDLIST)
// CAN_Filterscale:CAN总线过滤器位数,分别为32位(CAN_FILTERSCALE_32BIT)和16位(CAN_FILTERSCALE_16BIT)
//功能概要:CAN接收中断开启
//=====================================================================
uint8_t CANFilterConfig(uint8_t canNo, uint32_t CanID, uint32_t FilterBank, uint32_t Can_Rx_FifoNo, uint8_t IsActivate, uint32_t FilterMode, uint32_t FilterScale)
{
uint32_t FilterIdHigh, FilterIdLow, FilterMaskIdHigh, FilterMaskIdLow, filternbrbitpos;
if(CanID <= 0x7FFU) CanID = CanID << CAN_TI0R_STID_Pos; //2024.6
FilterIdHigh = (CanID >> 16) & 0xFFFF; //2024.6
FilterIdLow = (CanID & 0xFFFF); //2024.6
FilterMaskIdHigh = 0xFFE0; //2024.6
FilterMaskIdLow = 0x0000; //2024.6
filternbrbitpos = (uint32_t)1 << (FilterBank & 0x1FU); //2024.6
//设置过滤器初始化模式 (FINIT=1),在此模式下可以进行过滤器初始化
SET_BIT(CAN_ARR[canNo-1]->FMR, CAN_FMR_FINIT); //2024.6
CLEAR_BIT(CAN_ARR[canNo-1]->FA1R, filternbrbitpos); //2024.6
if (FilterScale == CAN_FILTERSCALE_16BIT)
{
CLEAR_BIT(CAN_ARR[canNo-1]->FS1R, filternbrbitpos);
CAN_ARR[canNo-1]->sFilterRegister[FilterBank].FR1 =
((0x0000FFFFU & (uint32_t)FilterMaskIdLow) << 16U) |
(0x0000FFFFU & (uint32_t)FilterIdLow);
CAN_ARR[canNo-1]->sFilterRegister[FilterBank].FR2 =
((0x0000FFFFU & (uint32_t)FilterMaskIdHigh) << 16U) |
(0x0000FFFFU & (uint32_t)FilterIdHigh);
}
if (FilterScale == CAN_FILTERSCALE_32BIT) //2024.6
{
SET_BIT(CAN_ARR[canNo-1]->FS1R, filternbrbitpos); //2024.6
CAN_ARR[canNo-1]->sFilterRegister[FilterBank].FR1 = //2024.6
((0x0000FFFFU & (uint32_t)FilterIdHigh) << 16U) |
(0x0000FFFFU & (uint32_t)FilterIdLow);
CAN_ARR[canNo-1]->sFilterRegister[FilterBank].FR2 =
((0x0000FFFFU & (uint32_t)FilterMaskIdHigh) << 16U) |
(0x0000FFFFU & (uint32_t)FilterMaskIdLow);
}
if (FilterMode == CAN_FILTERMODE_IDMASK) //2024.6
{
CLEAR_BIT(CAN_ARR[canNo-1]->FM1R, filternbrbitpos); //2024.6
}
else
{
SET_BIT(CAN_ARR[canNo-1]->FM1R, filternbrbitpos);
}
if (Can_Rx_FifoNo == CAN_FILTER_FIFO0) //2024.6
{
CLEAR_BIT(CAN_ARR[canNo-1]->FFA1R, filternbrbitpos); //2024.6
}
else
{
SET_BIT(CAN_ARR[canNo-1]->FFA1R, filternbrbitpos);
}
if (IsActivate == 1) //2024.6
{
SET_BIT(CAN_ARR[canNo-1]->FA1R, filternbrbitpos); //2024.6
}
//退出过滤器初始化模式 (FINIT=0)
CLEAR_BIT(CAN_ARR[canNo-1]->FMR, CAN_FMR_FINIT); //2024.6
return 0;
}