电子产业一站式赋能平台

PCB联盟网

搜索
查看: 67|回复: 0
收起左侧

嵌入式开发常见的3个C语言技巧

[复制链接]

864

主题

864

帖子

8156

积分

高级会员

Rank: 5Rank: 5

积分
8156
发表于 2023-12-27 08:31:00 | 显示全部楼层 |阅读模式
1.操作寄存器在嵌入式开发中,常常要操作寄存器,对寄存器进行写入,读出等等操作。每个寄存器都有自己固有的地址,通过C语言访问这些地址就变得尤为重要。5 l$ j! \" v9 u4 ]- U3 i! z/ J
#define GSTATUS1        (*(volatile unsigned int *)0x560000B0)6 m) O4 f- t8 ?) a0 g' q# r
在这里,我们举一个例子。这是一个状态寄存器的宏定义。首先,通过unsigned int我们能够知道,该寄存器是32位的。因为要避免程序执行过程中直接从cache中读取数据,所以用volatile进行修饰。每次都要重新读取该地址上的值。首先(volatile unsigned int * )是一个指针,我们就假设它为p吧。* B% M; b7 [2 H9 f, ~
它存储的地址就是后面的0x560000B0,然后取这个地址的值,也就是 * p,所以源代码变成了(* (volatile unsigned int * )0x560000B0),接下来我们就能直接赋值给GSTATUS1来改变地址0x560000B0上存储的值了。0 X; U8 J, A+ r" k( R) i
/* NAND FLASH (see S3C2410 manual chapter 6) */
: c  g' B) P; U6 H* P/ xtypedef struct {
7 P# I; c6 U0 v( u2 o7 n    S3C24X0_REG32   NFCONF;  T. e) o$ h5 @! z8 \, ]
    S3C24X0_REG32   NFCMD;& v/ C/ L8 I  m1 Q  z; @  |* y
    S3C24X0_REG32   NFADDR;. _* W9 A  K$ L4 k; s7 r! k2 Z
    S3C24X0_REG32   NFDATA;
' z. O8 O8 e' F4 L$ @  O0 U; j    S3C24X0_REG32   NFSTAT;
) k4 O7 ~7 S1 H. [    S3C24X0_REG32   NFECC;
+ ]' |# z/ Y5 L  t} S3C2410_NAND;
0 U8 B! q: S  o/ q) i3 y0 mstatic S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;. Y& D# Y1 k( M( z
volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFSTAT;2 @& ^% B( ?0 l7 S( i2 F1 {  y
有时候,你会看到这样一种情况的赋值。其实这和我们刚刚讲过的差不多。只不过这里是在定义了指针的同时对指针进行赋值。3 ]! @" a2 u0 ]$ |
这里首先定义了结构体S3C2410_NAND,里面全部是32位的变量。又定义了这种结构体类型的指针,且指向0x4e000000这个地址,也就是此刻s3c2410nand指向了一个实际存在的物理地址。6 z9 r* y  \1 Q
s3c2410nand指针访问了NFSTAT变量,但我们要的是它的地址,而不是它地址上的值。所以用&取NFSTAT地址,这样再强制转换为unsigned char型的指针,赋给p,就可以直接通过p来给NFSTAT赋值了。9 f0 J/ R4 _* {& t+ G% q
2.操作函数指针指针不光能指向变量、字符串、数组,还能够指向函数。在C语言中允许将函数的入口地址赋值给指针。这样就可以通过指针来访问函数。0 C( I9 P2 V) X
还可以把函数指针当成参数来传递。函数指针可以简化代码,减少修改代码时的工作量。通过接下来的讲解大家会体会到这一点的。
3 w3 S. \, m; v" C#include
& I1 [+ y1 E8 h0 c6 U4 Susing namespace std;
  E* l' r3 ~$ Z2 \/*比较函数声明*/  V4 H! Q% ]- i9 D2 n
int max(int,int);9 S  ]. i% X/ a" f# G1 Y) k6 W
int (*test)(int,int);
( O+ D) Z$ ?3 Tint main(int argc,char* argv[])1 A6 R# y! ?# [# U6 I
{  _4 {! Z; ~5 |- o
  int largernumber;
7 A4 O: `$ [7 {2 o3 B2 v6 k, d" d  d/*将max函数的入口地址赋值给
0 B# y5 L% l- y *函数指针test
, p  W* T! e2 t+ F$ u8 @( `  f */
8 _" Y2 H, t% T  W3 G4 k( l  test=max;
# j- }" ]. Z, V& d- p1 D/*通过指针test调用函数max实
7 o. K6 l+ ^0 i; S1 v$ J *现比较大小
! p7 o+ Z7 K0 r6 T2 q; l */
2 ]8 k- Y5 [0 h0 B* t  largernumber=(*test)(1,2);
7 o& H: r% n( U) a6 ]3 e; h% ]1 a$ y  coutendl;
) U' G1 w9 T% i  return 0;      3 T$ c+ ^9 x4 j( Q8 G2 {5 C8 w
}
* ^! W  a% {6 o0 L6 Gint max(int a,int b)
. `& h, m/ e& }# b* I{
" {8 p1 F- J7 d   return (a>b?a:b);  1 Z- T; [# L# F% \& X
}
8 u* b. W6 v) h% j. _/ p3 X7 ]通过注释大家应该很容易理解,函数指针其实和变量指针、字符串指针差不多的。如果大家理解了这个小程序,那么理解起下面这个有关Nand flash的源代码就好多了。7 S* u6 ~5 G8 `7 B
typedef struct {; ]% C- L0 r9 w$ M* Q5 O
    void (*nand_reset)(void);
2 y% H. K0 p6 Z6 ]5 ]0 o    void (*wait_idle)(void);
8 Y% J3 f4 e6 W    void (*nand_select_chip)(void);
8 O6 g# q1 E  D  N* {8 E+ ?5 D    void (*nand_deselect_chip)(void);
1 [7 k. ?3 @. E    void (*write_cmd)(int cmd);  {+ {9 D/ P- ^
    void (*write_addr)(unsigned int addr);
- V- {2 x$ x% W0 j+ y5 f    unsigned char (*read_data)(void);
) `* E$ Z% y  C. S}t_nand_chip;
/ p7 D) s' w0 Zstatic t_nand_chip nand_chip;
% A9 w4 E& @9 b7 o' ]/* NAND Flash操作的总入口, 它们将调用S3C2410或S3C2440的相应函数 */
& g4 U0 @4 `0 [6 ~: [0 I' [static void nand_reset(void);
- `" R! M+ N2 [. J! h  B' [static void wait_idle(void);
6 {/ G6 T' T' E- @/ d1 m- Pstatic void nand_select_chip(void);
+ m! N$ V1 P  c* qstatic void nand_deselect_chip(void);
" e% G. y: P4 Sstatic void write_cmd(int cmd);5 d8 V# C+ [# B" r3 G" `
static void write_addr(unsigned int addr);7 v3 V. v* q. E0 Z0 E
static unsigned char read_data(void);0 [; ]7 ]6 M, t7 I% \9 Z- @
/* S3C2410的NAND Flash处理函数 */$ G4 y4 v3 h  O: w2 |
static void s3c2410_nand_reset(void);
& [! ^/ @6 R- |) O3 v9 Estatic void s3c2410_wait_idle(void);
- E+ A2 ~& e$ \3 estatic void s3c2410_nand_select_chip(void);+ z/ ~4 R& h# [( g& G
static void s3c2410_nand_deselect_chip(void);
  i  U  D: V( T8 }- a. F. [static void s3c2410_write_cmd(int cmd);& f, t4 L" \4 O2 r# L* p9 s
static void s3c2410_write_addr(unsigned int addr);
4 j' b" ^4 r" b, f# Nstatic unsigned char s3c2410_read_data();- V8 e, O" c) P# X( L) l
/* S3C2440的NAND Flash处理函数 */, F1 |3 c; K# o. a, v( C' V
static void s3c2440_nand_reset(void);$ @( x& g) N6 O+ E/ ]- [. Z
static void s3c2440_wait_idle(void);% [, R1 P8 h7 K
static void s3c2440_nand_select_chip(void);8 X; f9 M1 i4 {' e  G7 ?
static void s3c2440_nand_deselect_chip(void);" J/ E  ]  C" L0 T/ U! I% ~
static void s3c2440_write_cmd(int cmd);
1 V1 N9 t- j6 ^/ c4 J4 Y5 Astatic void s3c2440_write_addr(unsigned int addr);
& E* I( l$ N$ O$ F0 }static unsigned char s3c2440_read_data(void);& u7 y% R0 h6 v4 B
! {1 r$ ~) z# ?# s
/* 初始化NAND Flash *// C/ L7 G" ~7 ]' k& S9 M
void nand_init(void): N7 A' g; U6 B1 K. d( f$ C$ [
{
, l2 J* c0 D  `& R* H#define TACLS   0. T) S2 C5 {( v5 r. F
#define TWRPH0  3
  T7 J8 C1 D, l  H! }#define TWRPH1  0
+ }  W% Z, A6 T    /* 判断是S3C2410还是S3C2440 */: V9 o4 [. H/ `8 n6 M& i, q
    if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))- ^/ {- ^/ X: ~& S2 [+ U* C
    {5 q: ?1 A9 W9 S
        nand_chip.nand_reset         = s3c2410_nand_reset;
( t. c; p4 u  ?        nand_chip.wait_idle          = s3c2410_wait_idle;, h' q) w; [0 M1 @7 d$ v/ H
        nand_chip.nand_select_chip   = s3c2410_nand_select_chip;
' j  }, T) Q; q7 i, J' W        nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip;% d. h3 }; y4 n! ]: T! f6 L  n
        nand_chip.write_cmd          = s3c2410_write_cmd;
2 J; @, E. P6 I3 Z. A% E        nand_chip.write_addr         = s3c2410_write_addr;4 h( g  N4 c0 x; h' Y2 ^% ~0 q* U
        nand_chip.read_data          = s3c2410_read_data;1 b* x/ S) T' w" I
        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */
) o9 B) k; J: f+ \1 H5 _        s3c2410nand->NFCONF = (115)|(112)|(111)|(TACLS8)|(TWRPH04)|(TWRPH10);
7 E' z1 o4 U2 I8 {% ^    }
) @: Q* F5 A$ c" R7 P    else
( U) v0 L' V* t( j    {: X$ e+ w4 O0 P
        nand_chip.nand_reset         = s3c2440_nand_reset;: e( X& ~6 T+ O7 H; \2 X, S( j
        nand_chip.wait_idle          = s3c2440_wait_idle;+ K1 z+ s3 E+ C1 L6 m, `
        nand_chip.nand_select_chip   = s3c2440_nand_select_chip;7 R/ N+ R- v& {8 W& g4 B0 e
        nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
0 h6 E; V- w* s        nand_chip.write_cmd          = s3c2440_write_cmd;
% f: c/ k- a/ [' [#ifdef LARGER_NAND_PAGE
2 f! P- l+ }; H) K" s  a        nand_chip.write_addr         = s3c2440_write_addr_lp;, A! M* G: ~' i1 h1 l7 D; A
#else
3 q2 d. E8 c0 S' r7 ]  ]! a3 ]1 u' L        nand_chip.write_addr         = s3c2440_write_addr;
8 p9 O+ Q2 B# f5 l#endif
1 V- X/ E: L& y& d3 z% {; U) Y        nand_chip.read_data          = s3c2440_read_data;* z, v1 f/ H& g+ q2 ?
        /* 设置时序 */% z* C8 N$ t* k: c9 g9 C. }& y, l
        s3c2440nand->NFCONF = (TACLS12)|(TWRPH08)|(TWRPH14);. a, R6 _% B, j* e% C
        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
1 w; d4 i, s& E% N' H        s3c2440nand->NFCONT = (14)|(11)|(10);
! r5 S2 R& q7 W$ o    }: A9 \2 A- n# J6 b7 ~
    3 F  j" j8 v' c" K# c7 \, X
    /* 复位NAND Flash */
1 h. t( K9 C6 F0 C% g, Q    nand_reset();; G: d; l8 Z+ ]; ?1 n* y- p+ K1 V
}
% @8 Q) V2 T* S: d6 n这段代码是用于操作Nand Flash的一段源代码。首先我们看到开始定义了一个结构体,里面放置的全是函数指针。他们等待被赋值。然后是定义了一个这种结构体的变量nand_chip。然后是即将操作的函数声明。! r6 G6 T4 i5 l8 ]5 T" J
这些函数将会被其他文件的函数调用。因为在这些函数里一般都只有一条语句,就是调用结构体的函数指针。) }0 P3 E8 F* w% M
接着往下看,是针对两种架构的函数声明。然后在nand_init函数中对nand_chip进行赋值,这也就是我们刚刚讲过的,将函数的入口地址赋值给指针。现在nand_chip已经被赋值了。如果我们要对Nand进行读写操作,我们只需调用nand_chip.read_data()或者nand_chip.write_cmd()等等函数。2 w( `4 u3 i9 f1 D. ~5 A4 B3 \! _- R, m$ i
这是比较方便的一点,另一点,此代码具有很强的移植性,如果我们又用到了一种芯片,我们就不需要改变整篇代码,只需在nand_init函数中增加对新的芯片的判断,然后给nand_chip赋值即可。所以我说函数指针会使代码具有可移植性,易修改性。/ Q5 q: N/ a( s7 ?; J" \& N

6 S, q9 A, @; u5 r4 ?  ^3.操作寄存器的位#define GPFCON      (*(volatile unsigned long *)0x56000050)# a8 i$ Q* }/ _) b% b, i- I
GPFCON &=~ (0x13);6 z) |( t  ^/ X
GPFCON |= (0x13);
" w  V9 S3 n' g0 s  A3 s结合我们刚刚所讲的,首先宏定义寄存器,这样我们能够直接给它赋值。位操作中,我们要学会程序第2行中的,给目标位清0,这里是给bit3清0。第3行则是给bit3置1。8 t8 d0 A" c' {1 s
文章来源于网络,版权归原作者所有,如有侵权,请联系删除。——EOF——一个我十分佩服的朋友阿秀开发了一个互联网大厂面试真题记录网站,支持按照行业公司岗位科目考察时间等查看面试真题,有意者欢迎扫码下方二维码适用~5 S2 V# i. ~$ T+ B7 y0 e. y3 R1 t/ K

rwbhfawhc5v64067882609.png

rwbhfawhc5v64067882609.png

% W! D8 Y, Q: }
# O5 ]& n6 z$ f& r你好,我是飞宇,本硕均于某中流985 CS就读,先后于百度搜索以及字节跳动电商等部门担任Linux C/C++后端研发工程师。9 Q4 f3 Z. ]1 q8 s0 K1 ]5 p# }
同时,我也是知乎博主@韩飞宇,日常分享C/C++、计算机学习经验、工作体会,欢迎点击此处查看我以前的学习笔记&经验&分享的资源。1 J1 G/ P1 @# I
我组建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起进群交流。( ~+ c0 d$ c' x7 N0 ^) v* ^" z5 Y  c

soafdaz30vy64067882710.png

soafdaz30vy64067882710.png
; R9 M6 P+ E2 [/ }1 L+ p. d& N3 {1 [
欢迎你添加我的微信,我拉你进技术交流群。此外,我也会经常在微信上分享一些计算机学习经验以及工作体验,还有一些内推机会。
8 y8 U- j- v; t
% `8 S# V. U/ {0 z9 C% x

2tfow4owdry64067882810.png

2tfow4owdry64067882810.png
0 h' Z; H% ~; O
加个微信,打开另一扇窗
9 c; n! M8 h) D6 w

fizsx52xyh364067882910.gif

fizsx52xyh364067882910.gif
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


联系客服 关注微信 下载APP 返回顶部 返回列表