1. static:这个可以将变量申明为静态的,限制变量/函数的使用范围(只限于本文件中);延长变量的生命周期使变量和main函数的生命周期一致。
变量申明——延长变量生命周期:
void Test(void)
{
static uint8_t i = 0;
i++;
}
函数申明——只能在本文件中使用:
static void Test(void)
{
uint8_t i = 0;
i++;
}
2. extern:这个关键字用于申明变量,扩大变量的使用范围(扩大到其他文件)
变量申明:
extern uint8_t code Crc8_Table[];
函数申明:
extern uint8_t Generate_CRC_8bit(const uint8_t *pstr,uint8_t len,const uint8_t *crc_table);
3. const:表示此变量为不可修改
//变量不可以被修改
const uint8_t code_arr[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//此指针变量不能被修改,但是指针指向的内容可以修改。这个和数组名称一样
char * const p = 0x00001010;
*p = 1;
4. typedef:数据类型定义符号,用于定义新的数据类型
typedef struct leds_struct{
uint16_t pin;
uint32_t port;
}LED_STRUCT;//定义LED_STRUCT新类型
typedef unsiged char uint8_t; //定义uin8_t类型
5. volatile:可变的,用于将变量申明为实时可变。这样编译器不会将此关键字申明的变量优化,CPU每次都会从内存中读取变量数据而不是从CPU寄存器中来获取(速度比内存快)。常用于申明中断变量与外设寄存器变量
volatile unsigned char flag = 0; //定义一个实时变量
//中断服务函数
void TIM1_IRQHandler(void)
{
flag = 1;
}
宏定义形式:
//使用宏定义,使用volatile申明寄存器地址为实时可变。因为这个是一个寄存器里面的
//内容会被外设随时修改。
#define GPL0CON (*(volatile unsigned int *)0x000D0100)
结构体形式:
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;
1. 指针作为函数形参:可以修改原始变量的值
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->ODR ^= GPIO_Pin;
}
void MyLed_Ctrl(void)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);//修改GPIOA对应的寄存器值
}
2. 指针变量:操作内存地址
//拷贝一定长度的内存
void My_memcyp(unsigned char *dec,unsigned char *src,unsigned int ssize)
{
while(ssize--)
{
*dec++ = *src++;
}
}
ldr:用于将内存中的数据加载到寄存器中(内存→寄存器)
ldr r0,#1 ;将1读取到寄存器r0中
小技巧:使用"ldr"伪指令,可以让编译器帮我们自动将数值转换成合法立即数
ldr r0, =0x10010000 ;将地址加载到r0寄存器中
str:用于将寄存器中的数据保存到内存中(寄存器→内存)
str r1, [r0] ;将r1寄存器中的数值写入到r0寄存器保存的数值对应的内存地址中去
mov:把立即数存入到寄存器中
mov r1, #1 ;向r1寄存器中写入立即数1
orr:orr{条件}{S} 目的寄存器,操作数1,操作数2
orr r0,r0,#0xD3
;0xD3=1101 0011
;将r0与0xd3作算数或运算,然后将结果返还给r0,即把r0的bit[7:6]和bit[4]和bit[1:0]置为1
bic: bic{条件}{S} 目的寄存器,操作数1,操作数2
bic r0,r0,#0x1F ; 0x1F=0001 1111 含义:清除R0的bit[4:0]位
b:跳转,不返回
b . ;跳转到当前地址位置:相当于while(1)死循环
bl:跳转前把返回地址放入lr中,以便返回,以便用于函数调用
bl _delay ;跳转到_delay函数中
完整的代码:
;说明:LED闪烁汇编裸机程序
;作者:Jack.Tang
;时间:2020年5月22日21:15:13
PRESERVE8 ;8字节对齐
AREA LedBlink_4412, CODE, READONLY ;声明以下内容为代码
;定义寄存器地址,这个和C语言中的#defing宏定义是一样的功能
GPL2CON EQU 0x11000100
GPK1CON EQU 0x11000060
GPL2DAT EQU 0x11000104
GPK1DAT EQU 0x11000064
ENTRY ;以下为汇编程序入口
EXPORT _start ;注意:汇编文件中的符号(symbol)表示的是地址。这个类似于C语言中的函数名称
;设置GPL2_0为输出模式--GPL2CON寄存器--LED2
_start LDR r0, =GPL2CON
LDR r1, =0x00000001
STR r1, [r0]
;设置GPK1_1为输出模式--GPK1CON寄存器--LED3
LDR r0, =GPK1CON
LDR r1, =0x00000010
STR r1, [r0] ;将寄存器r1中的值写回到r0寄存器保存的值对应的地址中。这个就和C语言中的指针*p一样的效果
;设置GPL2DAT寄存器为1,点亮LED2
LDR r0, =GPL2DAT
MOV r1, #1
STR r1, [r0]
;设置GPK1DAT寄存器为1,点亮LED3
LDR r0, =GPK1DAT
MOV r1, #2
STR r1, [r0]
BL . ;跳转到当前地址。功能:死循环防止程序跑飞
END ;程序结束标志
关键字:结构体指针、结构体
typedef struct list_node{
struct list_node *next;
uint8_t buf_data;
}LIST_NODE;
注意:next指针指向的是下一个节点的地址。意思就是如果知道当前节点地址p,那么下一个节点的地址就是p->next。记住这一点后面链表的插入时需要使用到。
补充说明:
假设已知头节点(第一个节点)的地址为p,那么第二个节点的地址就为p->next,第三个节点的地址为p->next->next。以此类推,剩余其他节点的地址也可以推导出来。记住这一点,这个在后面的链表增加/删除会用到。
注意:单向链表为“单向”的,即只能由前面节点的地址来获取后面节点的地址,不能反过来!
节点添加:
1. 通过判断头节点的next指针是否为空来确认是否空链表。空链表直接将head和tail指针指向此节点,并设置head和tail节点的next指针指向NULL
2. 非空链表,新节点添加到尾部。节点“连接”操作是通过节点中的next指针完成的
删除尾节点:
“断链”:即将当前节点的next指针指向NULL(空地址)处。这样此节点就与后面的节点没有连接了
操作:先将倒数第二个节点与尾部节点“断链”,移动尾部指针tail指向倒数第二个节点使它成为新的尾部节点。最后使用free函数将原先尾部节点所占用的内存释放。
插入节点:
操作:先“断链”,再将新的节点连接起来
说明:此代码中的head和tail只是节点指针,它们不是节点。因为没有分配内存给它们!
国内gitee:https://gitee.com/jack_tang_1994/data_struct.git
百度云:
链接:https://pan.baidu.com/s/1BQ5VCJN88FbCG-YVrHjKLw
提取码:cceu