第一篇:修煉作文語言(教案)
修煉作文語言(教案)——中考作文語言專題 光明中學
歐陽松梅
教學目標:
1、明確作文語言表達技巧
2、提升學生的作文語言表達能力
教學重點:講解作文語言表達技巧 教學難點:提升語言表達能力 課時:2 教具:PPT課件
一、課堂導入
中考作文在語文試卷中幾乎占了“半壁江山”,其重要性顯而易見。要寫好一篇考場作文,涉及到考生多方面的能力。其中語言表達能力如何,對作文成績影響甚大。精美、豐富的語言成了中考作文的亮點。孔子曾有語云:“言之無文,行而不遠。”其中的“文”,即指“文采”,就是說寫文章要講究語言的藝術性。沒有文采的文章,就難以傳播,那么如何讓你的文章語言絢麗多姿、文采飛揚呢?今天,教大家三招,希望大家能在課堂上積極地現學現用。
二、招數展示
第一招:妙用詩詞佳句
俗話說“腹有詩書氣自華”,如能在文章中展現一些古詩詞,不但可以增強文化底蘊,還能增添文采,那是何樂而不為的一件事呢?如何妙用呢? 下策:直接引用
中策:故事(事例)+詩人+引用 上策:化用無痕
例段:我向往幽閑,我喜歡純凈,我心儀靜謐,我追求祥和……但這個紛擾中的世俗,在一片狼籍的溫床上,永遠沒有休止過可怕的夢想。潤色后:
我向往“采菊東籬下”的那種幽閑,我喜歡“清水出芙蓉”的那種純凈,我心儀“人跡板橋霜”的那種靜謐,我追求“低頭弄蓮子”的那種祥和??但這個紛擾中的世俗,在一片狼籍的溫床上,永遠沒有休止過“沉魚落雁”的可怕的夢想。例段:心靈的選擇在永存,在發展,在成熟,在升華。潤色后:
心靈的選擇在“天生我材必有用”中永存,在“舍生而取義”中發展,在“守拙歸園田”的閑適中成熟,在“人生自古誰無死,留取丹心照汗青”(文天祥《過零丁洋》)的千古絕唱中升華。訓練
1、(中策:例段展示)“思念使詩圣嘆故鄉月明,思念使女詞人瘦比黃花,思念使豪放派鼻祖幽夢還鄉,相顧無言淚千行,思念使婉約派泰斗酒醒曉風殘月楊柳岸,思念使摩詰先生每逢佳節走入‘遍插茱萸少一人’的心境,思念使邊塞詩人老態龍鐘遙望故園,思念使愛國詞人鐵馬冰河入夢來??”
——《思念是一道風景》
14歲的我愛讀書。不知何時,兒時的嬉笑已成了回憶。當有些人沉溺于網吧,有些人熱衷于神聊,日暮燈下,我卻端坐捧讀,為屈原壯志難酬扼腕不平,為朱自清的樸實自然感動不已,駐足雨果的巴黎圣母院,跟著三毛迷路在撒哈拉沙漠,聽張愛玲講那30年代的舊上海的往事,我總會有陶醉之情,久久不愿暫別,直至夜闌人靜。
總結:把融貫古今的史實和詩句鑲嵌在清麗脫俗的辭采和境界中,彰顯出豐厚的底蘊,浸透著濃郁的書卷氣息。這就是我們要追求的作文語言表達魅力,當然,這種語言技巧不能充斥全篇,否則,就成了掉書袋!
(下策:例段展示)例段:
荒城,狂風,斜陽似血,一位劍客臨風而立。他的劍很冷,他的眼神很冷,他的心更冷,……后來……
這個瓜娃子凍死了!
這個故事告訴我們:降溫了,扛是扛不住滴,必須加衣服!仿寫:
山野,寒流,殘月如鉤,一名刀客面水而坐。他的刀很酷,他的眼神很酷,他的心更酷!……后來……
這個瓜娃子凍死了!
這個故事告訴我們:降溫了,扮酷是沒有用的,必須加衣服!例段:
這是勇敢的海燕,在閃電之間,在怒吼的大海上高傲地飛翔。這是勝利的預言家在叫喊:——讓暴風雨來得更猛烈些吧!仿段:
這是勇敢的學生,在怒吼的老師前,在學生中間,無畏的站立,這是第一個吃螃蟹的人在叫喊:——讓作業布置得少些吧!
第二招:巧用修辭手法
1、妙用比喻
講究形神具備,注意選擇優美富有詩意的喻體
2、不可亂用夸張
夸張要合情合理,不可無趣、庸俗??
3、一定要會排比
第三招:善于描寫
(所謂描寫就是用色彩鮮明、立體感強、生動形象的文字語言把表述對象的狀態,生動、具體地描繪出來,給人以栩栩如生、身臨其境之感。是它是一種“形神兼備”的表述方法,是記人、敘事、寫景類文章的主要表述方法之一。
范例1:
不必說碧綠的菜畦,光滑的石井欄,高大的皂莢樹,紫紅的桑椹;也不必說鳴蟬在樹葉里長吟,肥胖的黃蜂伏在菜花上,輕捷的叫天子(云雀)忽然從草間直竄向云霄里去了。單是周圍的短短的泥墻根一帶,就有無限趣味。油蛉在這里低唱,蟋蟀們在這里彈琴。翻開斷磚來,有時會遇見蜈蚣;還有斑蝥,倘若用手指按住它的脊梁,便會拍的一聲,從后竅噴出一陣煙霧。何首烏藤和木蓮藤纏絡著,木蓮有蓮房一般的果實,何首烏有擁腫的根。有人說,何首烏根是有象人形的,吃了便可以成仙,我于是常常拔它起來,牽連不斷地拔起來,也曾因此弄壞了泥墻,卻從來沒有見過有一塊根象人樣。如果不怕刺,還可以摘到覆盆子,象小珊瑚珠攢成的小球,又酸又甜,色味都比桑椹要好得遠。
——魯迅《從百草園到三味書屋》
范例2 蜂兒如一朵小霧穩穩地停在半空;螞蟻搖頭晃腦捋著觸須,猛然間想透了什么,轉身疾行而去;瓢蟲爬得不耐煩了,累了祈禱一會兒便支開翅膀,忽悠一下升空了;樹干上留著一只蟬蛻,寂寞如一間空屋;露水在草葉上滾動,聚集,壓彎了草葉,轟然墜地,摔開萬道金光。
——史鐵生《我與地壇》
范例3 我想有一個家,家前有土,土上可種絲瓜,絲瓜沿竿而爬,迎光開出巨朵黃花,花謝結果,累累棚上。我就坐在那黃泥土地上,看絲瓜身上一粒粒突起的青色疙瘩,慢看……
——龍應臺《慢看》
練習一:
我微微向上望了望,看到了那把小而破的雨傘。突然,一個奇怪的念頭跑進了我的頭腦里,傘這么小,為何我的衣服是干的呢?帶著疑惑,我朝爸爸望去,我驚呆了,爸爸幾乎近半個身子在傘外,衣服都濕了,我的眼睛也濕了。
第二篇:新聞發言人的語言修煉
學習導航
通過學習本課程,你將能夠: ● 找到增強語言魅力的方法; ● 掌握回答敏感問題的三個技巧。
新聞發言人的語言修煉
一、增加語言魅力的方法
語言是一種力量。新聞發言人既然要發言,那么就要加強語言的修煉,同時還應采取倒金字塔型結構,即先將核心信息講出來,然后在有時間的前提下,再擺出立場和證據,也就是以立場和證據提供支撐,發布核心信息。
作為新聞發言人,在整個發言過程中,對語言的要求是“LESS IS MORE”:少即是多,也就是語言上要簡單、時間上要簡短、內容上要簡要。具體來講就是三個“一”:用初中一年級的語言(簡單),在一分鐘之內(簡短),傳遞一個核心信息(簡潔)。這是對新聞發言人語言運用能力最基本的要求,也是最高標準的要求。
新聞發言人必須提醒自己,不在于說什么,而在于怎么說,因此,一定要有好的表達方法。
關于新聞發言人的發言,主要存在以下誤解:
第一,發言人不能有框框,一定要自我發揮。事實上,任何一個發言人都擁有一定的框框,發言人是代表其后面的機構,在受權的范圍之內說話。
第二,發言人按既定方針政策執行,沒有什么活動余地。
第三,發言人必須字正腔圓,一字不差。實際情況是,新聞發言人不是播音員,也不是主持人,不必追求如同一個模子刻出來的聲音,不必都是俊男靚女,更不必一字不差地念稿子。第一個發言人都可以保留自己的特色,比如一點口音,習慣攥著拳頭、喜歡講笑話等。實際上,讓自己看上去更像個“常人”會讓發言人更有人緣。
具體來說,增強語言魅力的方法主要有六個:
1.舉例子
受眾在看電視、聽廣播以及讀報紙時,通常處于一種比較放松的狀態,政策條文和空泛的表述很難給其留下深刻的印象。相較而言,故事、個案、事實更能抓住眼球,有時還能調動受眾情緒,不論這種情緒是喜、怒、哀、樂,都將使信息獲得更有效的傳播。更重要的是,恰當的例子可能使發言人的話聽起來更有說服力和具有人情味。無論是官員、商人、記者,還是平民百姓,都是人,都喜歡關于人的故事,都會受到情感的驅動。所以,發言人應有意識地從小故事入手,而不是從大道理入手。
此外,發言人的作用不僅在于傳遞給記者和公眾理性的信息,還應該打動受眾的心,讓手中真正相信和記住自己的話。
舉例子的好處在于,通過鮮活的個案、運用講故事的手法重現給受眾一個場景、一幅圖畫、一段聲音,從而調動起受眾的情緒,最終達到使其記憶深刻的目的。
2.擺事實
在擺事實時,要注意的問題包括: 第一,證明自己的觀點是有據可依的; 第二,事實可以是一個或者多個; 第三,一般比較簡短;
第四,應緊緊圍繞且服務于自己的觀點;
第五,在語氣和語調上,允許采用一定程度的夸張和煽情的手法。發言時,新聞發言人在語氣和語調的處理上,可采用一定程度的夸張和煽情的手法,以引起記者、公眾的關注。
此外,在擺事實的過程中,發言人應保持語速平穩、表情莊重,這種理性的態度可以傳遞給記者“保持理性而非情緒化”的信息。
3.列數字
列數字時,要注意的問題包括:
數字不要太多
過多的數字會把記者搞糊涂,有的人則可能亂用,并做出所謂數字解讀,夸大數字的意義或揭露背后的問題。
挑選出最重要的數字
記者不是專家,挑選出最重要的數字是發言人的責任,這也會使發言人的核心信息看上去更加清晰和有說服力。
突出重點的數字
不要羅列數字,而要重點突出。在新聞發布稿中,一段話最好不要有三個以上的數字,有一個醒目的數字即可。比如挑選“最大”、“最小”、“首次”、“最后”、“第一”、“倒數第一”的數字。
數字要具體化
第四,數字要具體化。在列數字時,應采用“每位”、“人均”、“每天”、“每平米”等詞語,讓數字變得更加直觀,從而增添新聞價值。比如,相比較“緊急調撥的6800床棉被已送到災民手中”和“每一位災民都得到一套政府緊急調撥的棉衣”,很明顯后一種說法會更讓人感覺這次救災的成功。
找出數字的變化 在列數字時,新聞發言人不要停止于靜止的數字,要找出變化,否則很難留住讀者。比如,在“為了老百姓的菜籃子,國家調整350種農貿產品的最高零售價”和“為了老百姓的菜籃子,國家調整350種商品的最高零售價,其中最大降幅63%”兩種說法中,后一種顯然更能讓老百姓感覺得到實惠。
因此,發言人在準備信息時應勤動手勤思考,計算出對比數,如“同比增長XX”、“降低百分之XX”,“增加XX倍”等,甚至可以做出統計圖。
數字不要過于專業或容易引起歧義
不要使用過于專業或容易引起歧意的數字,要對專業性數字做出解讀。比如,“禽流感的病死率是1%”和“禽流感的病死率是1%。這個數字是美國流行病專家通過三十年的研究,觀察了30000名病人得出的結論”。后一種說法所起到的效果要更好。
不要過于精確的數字
不要過于精確的數字,要利于記憶。對于小數點后的數字可以采取湊整的方法,具體的數字可放到背景資料中。比如,“國家公務員考試報名結束最熱崗位4961人爭一職”和“國家公務員考試報名結束 最熱崗位近5000人爭一職”,后一種說法給人感覺更直觀,更利于記憶。
4.引用名言
引用名言、公理是增強語言力量的另一種方法,特別是對比較尖銳的問題,通過一句人們都容易理解的和接受的話,先把氣氛緩和下來,這是一種智慧的做法,可以綿里藏針地把意思傳遞出去。
【案例】
溫家寶總理關于因特網的回答
美國全國廣播公司曾對溫家寶總經理進行了如下采訪。
美國全國廣播公司記者:“您好,溫總理。剛才在您的答問當中,您也提到了因特網,現在我們知道胡主席也即將對美國進行訪問,在美國以及其他地方,大家對中國在因特網方面進行的內容審查都頗有微詞,我想了解一下您個人的看法,您是如何看待中國在因特網方面進行的審查的?您是否對現行的這一政策感到滿意?或者您對國際媒體對這一事件的報道是否滿意?”
溫家寶總理:“我想先引用兩句話,一句是簫伯納說的‘自由意味著責任’。一句是你們美國的老報人斯特朗斯基說的‘要講民主的話,不要只關在屋子里讀亞里士多德,要多坐地鐵和公共汽車’。……”
在上面的案例中,溫家寶總理沒有直接進行正面回答,而是通過引用外國名人的兩句名言很好的將自己的立場和意思委婉、明確地傳達出去。
5.打比方
善于打比方的發言人通常具有較強的形象思維能力和幽默感,這類發言人喜歡以輕松的方式發布信息。
打比方是把一個事物比作另外一個事物,后者應該是大多數人都熟知的、容易在各自經驗中尋找相對應的具體形象,能夠調動人們的視覺、聽覺、味覺和觸覺等感受,從而產生共鳴。
抓住問題核心的比方可以大大提高信息的新聞價值,成為媒體報道的焦點,甚至直接成為新聞的標題。因此,好的比方是需要精心設計的。
發言人在發布信息前應問問自己,是否打比方可以將信息講得更清楚和吸引人。例如,有人曾問一位經濟學家經濟發展速度是應該快還是慢時,經濟學家的回答是“經濟發展就像是騎自行車,太快了會摔跤,太慢了會倒。”這里用的自行車比喻就會讓人感覺一頭霧水。
6.作對比
不怕不識貨,就怕貨比貨。對比是增強信息發布效果的有效方法。在作對比時,需避免兩種錯誤: 第一,兩個事務之間不具備可比性。
第二,任何時候都不要貶低“兄弟單位”、“合作伙伴”,或指責某個人,就算是競爭對手也不要在新聞發布時公然以貶低別人達到抬高自己的目的。這不僅容易引起爭端,實際上更會有損自己的形象。
要點提示
增強語言魅力的方法: ① 舉例子; ② 擺事實; ③ 列數字; ④ 引用名言; ⑤ 打比方; ⑥ 作對比。
二、回答敏感問題的三個技巧
在日常工作當中,新聞發言人要做好三件事:日常的新聞發布、重大新聞的發布、突發事件的新聞發布,其中,突發事件最能體現新聞發言人的水平。
新聞發言人最希望看到的是,記者迫切想知道的事情正是自己打算告訴的。但是很多時候恰恰相反,特別是危機時期,媒體關注的正是發言人不能說或不好說的話。對于敏感問題,發言人總是擔心若回答得不妥當會引起媒體的猛烈炒作甚至社會的恐慌。因此,敏感問題是新聞發布之前發言人重點準備的對象。
為了避免這種不統一,新聞發言人需要掌握回答敏感問題的三個技巧:
1.搭橋法
概括來說,運用搭橋法處理問題一般適用于三種情況:
針對指責性問題
針對指責性問題,新聞發言人的回答方式有:
第一,“我們不贊成你的觀點??,但是我們需要指出的是??”;
第二,“我們首先觀點很明確,我不贊成你但是我要告訴你正確的東西是??”; 第三,“事故責任正在調查當中??,此外我們應該特別關注的是當前的??”; 第四,“那是過去的情形,我們現在已經處于領先地位”。
針對猜測性問題
針對猜測性問題,新聞發言人的回答方式有:
第一,“我不知道??,我所知道的是??”;
第二,“我不會對此妄加猜測,您應當關注的??”; 第三,“實際情況不是??,我目前掌握的情況是??”。
回答記者多次提問
回答記者多次追問,新聞發言人的回答方式有:
第一,“我們和你同樣關注這個問題,但我們目前正在努力做的是??”;
第二,“我首先肯定你,我們也在關注這個問題,但是我們現在目前更加關注??”; 第三,“剛才我已經回答了這個問題,我可以向各位透露的另外一點是??”; 第四,“這個不是我的專業領域,但是我能夠告訴你的是??”;
第五,“雖然它超過了我的專業領域,但是我可以在我的領域里面告訴你一些真相??”。
【案例】
2004年8月亞洲杯足球賽
在2004年8月亞洲杯足球賽中,中國球迷發生了一些針對日本球隊的激烈行為。由此,引起了輿論爭斗。針對這一事件,日本國內銷量最大且保守色彩也名列前茅的《讀賣新聞》發表文章指責說:“中方把球迷騷亂問題政治化的責任歸咎于日方,并強調反日的根源是歷史問題。如果中國固執己見,則根本無助于消除國內反日的火種。”該報堅持認為,目前中國國民的反日情緒根源在于“反日愛國教育”。
于是,有記者問中方新聞發言人孔泉:“據報道,在亞洲杯足球賽過程中,部分中國球迷對日本足球隊采取了一些不友好行為,請問中方對此有何評論?”
孔泉回答道:“‘2004年亞洲杯足球賽’是中方承辦的重要國際賽事。我們非常高興地看到,中國隊和日本隊以優異表現進入決賽,期待著中日球迷能在8月7日非常文明地欣賞到一場非常精彩的比賽。
據我們了解,在與日本隊有關的四場比賽中,場內外總體秩序良好,氣氛熱烈。眾所周知,在國際上的重大足球比賽中,少數球迷出現偏激行為時有發生,這不符合體育精神,我們并不贊成。
同時,需要指出的是,部分日本媒體將少數人的行為過分炒作和渲染,甚至與政治掛鉤,對此我們表示遺憾。
中日兩國有悠久的友好交流歷史。我們相信,加強體育在內的廣泛交流,有助于促進兩國人民之間的了解和友誼。”
在上面的案例中,孔泉的回答是四段論,邏輯很嚴密,首先總體表達對精彩比賽的期待,然后在氣氛好的同時對這一小事件有些遺憾,認為不應該將這些與政治掛鉤,最后期待加強友誼,整個回答中肯且全面。
2.旗幟法
旗幟法指用強調的方式給受眾造成最重要的印象,即優先表述最重要的內容,這是國務院新聞辦和外交部新聞司經常用的手法。
一般來講,發言人在發布會進場之后會說:“歡迎大家,下面我有兩件事情給大家說一說??”發言人一定是先把兩件事說完后才會回答提問。因為在提問的過程當中,可能會有爭論、有碰撞,也會讓人犯迷糊,所以一定要把最重要的觀點優先表達出來,在所有的新聞發布會里面也是如此。表達完最重要的觀點,發言人再去回答記者最想知道的事情。運用旗幟法回答問題的順序一定不能錯,一般是以“我想透露給大家的是??”、“我想強調的是??”、“最重要的問題是??”、“問題的關鍵是??”這些詞來開頭,而這些詞的意義就在于讓受眾注意后面所說的話。
3.重復法
新聞發言人要不斷地重復核心信息,以確保記者會采用。重復法的說法有以下三個: 第一,“我必須重申一遍??”; 第二,“我們始終不能忽視這一點??”; 第三,“我們是否可以換一個角度思考,??”。
一個成功的新聞發言人一定是善于重復講故事的人,而且是永遠有激情的。
總之,新聞發言人面對媒體時,要注意無可奉告的說法,學會采用空對空、開玩笑回避、踢皮球、轉移話題、進攻性回避、反問式回避的策略。
概括來說,面對媒體時,新聞發言人應注意以下要點:
第一,防守不能得分,要去搶球。每次在媒體面前露面都是展示自己、展示企業、展示機構的一次最寶貴的機會,所以新聞發言人不要想著去應對而是要積極地引導。
第二,弄明白記者意圖后再接受采訪。新聞發言人如果沒弄明白記者意圖后就貿然接受采訪,很可能就會說出不應該說的內容。
第三,應當巧妙地控制采訪,表達出自己最想說的話。旗幟法的意思是把最后發生的最重要的、最吸引人的放在最首要的位置說。
第四,必須有習慣把自己的故事重復地講給不同的記者,且每次都要有激情。新聞發言人要具備的素質可以概括為七個字:膽大、心細、臉皮厚。
第五,語氣和風度與說的內容一樣重要。第六,要不失時機地推銷自己的觀點。
第七,不要對記者有敵對情緒,也不要貶低記者。第八,不可強求記者改變寫作方式。第九,在任何時候都要保持鎮靜。第十,不要重復記者的負面指責。
第十一,準備一個有三層意義的答案。所謂三層意義的答案,即第一層是結論,第二層是事實,第三層是論證,實際上也就是論點、論據和論證。
第十二,遇到越難回答的問題,答案就要越簡短。第十三,無論什么時候,回答都要從公眾利益出發。
【案例】 嚴介和的苦惱
嚴介和是中國太平洋建設集團的董事長,其2004年的財富是15億,2005年飆升到125億,被稱為黑馬。但是當其排上2005年胡潤中國富豪榜榜單第二名之后,寒流隨即逼近,嚴介和很快陷入債務門。法院禁止其享用豪宅名車、限制出境,昔日風光無限的黑馬富豪陷入四面楚歌。
嚴介和向江蘇省有關領導和部門送交的報告《關于請求協調解決太平洋建設集團公司臨時困難的緊急報告》表示,媒體、銀行、法院互動“制裁”造成的壓力,已經給該公司生產經營和內部管理造成了巨大影響。
報告列舉了太平洋建設面臨的三個主要壓力:第一,媒體密集報道增加了銀行的恐慌;第二,銀行對該公司信任度下降;第三,法院懷疑其有錢不還,加大執行力度。這三種壓力形成了互動制裁的惡性循環,導致了一系列后果。
對于這樣一個局面,嚴介和的總結是:公司沒有文化部,導致與媒介溝通不暢通。
事實上,這種場災難并不是因為沒有文化部造成的,而是由于嚴介和一直口無遮攔給企業帶來了災難。如:
“我和很多領導私人感情很好。”
“太平洋經濟說白了是談判經濟,談判出的效益占75%,太平洋現在不參加投標了,現在我們更注重談判做短期BT項目。業主代表不合適,我們就向主要領導反映把他換掉,監理公司不滿意,我們就換監理。這樣下來賺的錢不得了,平均利潤高達35%。”
“之所以要收購國企,就是為了不讓別人譏諷我是包工頭、暴發戶,是‘散發著土地芬芳的人’,我的名聲都是國企給的,收購國企才讓自己成為各地政府的座上賓。”
“一切都在我的掌握之中。” “像我這樣的人最優秀的人也是悲哀的,因為智商、才華、能力、體魄都非常人能及,就能忍受常人不能忍受的委屈,承擔常人不能承擔的責任。商人就是,處處受人中傷的人。”
“一流的企業家是精明加厚道,二流的企業家是精明加精明,三流的企業家是厚道加厚道,適合政界。”
“民營企業是弱勢群體,如果他是富豪的話就更是了。” “員工中最差的是博士,其次是MBA。”
“圍繞鈔票運轉的人是老板,圍繞企業運轉的人是資本家,企業家是圍繞整個社會運轉的人,不要把自己想得那么高尚,你就是一個賺錢的資本家。”
“中國遍地是黃金,想怎么賺就怎么賺。”
“我的目標是打造無數個千萬富翁,結束“嚴介和時代”。“ “科學技術不是第一生產力,公共關系才是第一生產力。” …… ……
殊不知,正是這些沒有慎重推敲的話將嚴介和推向了風浪尖口,為之后接踵而至的災難埋下隱患。
從上面的案例可知,作為新聞發言人和公眾人物,掌握語言技巧,尤其是敏感問題的回答技巧非常重要。
第三篇:老板請修煉你的語言魅力
高超的語言技巧可以增強一個人的語言魅力。特別是對于企業家來說更是如此。某企業總經理何女士是一位非常有個性的女能人,她工作上熱情高,能力強,年輕漂亮,充滿一種健康向上的力量。在事業上也是一位非常成功的企業家。縱觀她的優點,最大的長處是她總是那么謙虛、關心人,待人體貼,對下屬更是如此,娓娓動聽的話語充滿了女性的溫柔。
有一位采訪過她的記者曾這樣生動地寫道:“不論你來自何方,只要有機會與她相處,她總是把你當作是她屋里唯一的重要客人。當你與她說話時,她的眼神、語言總會讓你忘了面對的是一名赫赫有名的總經理,而是與你親密相伴的朋友。她會認真地傾聽你的意見,讓你大膽地發表自己的意見和觀點。如果有別人在場,她并不會因為你僅是一名年輕的業務員或打字的秘書而怠慢你,仍然把你當作她的朋友一樣熱情對待。”
這種與人為善的優點、使得肅穆的總經理辦公室的氣氛一下子有如公園中湖岸柳陰下那般溫馨。事實上,不少成功的經理,待人接物總是那樣謙虛和隨和,只不過瑪麗總經理在這之上又加上了一份女人特有的溫馨。那么瑪麗總經理講話的魅力又表現在哪里呢?
(1)激勵了員工的士氣。對于自己的員工做出的成績予以充分肯定和恰如其分的表揚,這將對鼓舞士氣十分有效。特別是當眾表揚的話,效果就更好了。例如,在一次重大球賽結束后,獲得最佳得分手的球員在接受記者采訪時,當記者問到:“你今天為何表現得如此出色?”他則說:“這一切都是進攻隊員共同努力、出色配合的功勞。”可以肯定地說,這位球星深諳使用表揚這一秘密武器的作用。
(2)宣傳了自己的員工。公司的業務員在推銷或生意上的成功,雖然是在老板的率領下成功的,但在對外與宣傳這件事時,不同的說法得到的社會效果則不一樣。如,老板可以這樣說:“我談成了這筆業務。”如果用10分制來打分,這種說法可以打3分;但是如果他說:“是我們的努力,才促成了這筆生意。”則可得到6分,因為他把榮譽與部屬們分享。相反,他說:“是某人與他的部門談成了這筆生意。”那么則可以得滿分。因為他把功勞有意地都歸于其部門經理和下屬部門員工,從而贏得了下屬的尊敬和人心,這種感情投資長期下去,會使公司和總經理受益匪淺。長遠看,這種做法比一味地自我吹噓效果更好。
(3)光彩了自己的形象。把自己成功的碩果同別人共享,并不是一件勞而無獲的賠本買賣。這不是那種他得多了,你就得少了的加減法。在這種分享的過程中,也不會因為你付出得太多,你就所剩無幾。相反你會得到更多。因為對一位只會把功勞占為己有的經理來說,旁人或下屬對他的自我欣賞總會感到厭煩的,時間長了,老板就脫離員工成了孤家寡人。瑪麗總經理的語言的秘訣就是把老板講話中的“我”換成“我們”或“你的”那樣的字眼,她的談話就變得非常吸引人了。
第四篇:C語言嵌入式系統編程修煉之道
C語言嵌入式系統編程修煉之道收藏
C語言嵌入式系統編程修煉之道——背景篇...1 C語言嵌入式系統編程修煉之道——軟件架構篇...4 1.模塊劃分...4 2.多任務還是單任務...5 3.單任務程序典型架構...6 4.中斷服務程序...7 5.硬件驅動模塊...9 6.C的面向對象化...10 總結...10 C語言嵌入式系統編程修煉之道——內存操作篇...12 1.數據指針...12 2.函數指針...13 3.數組vs.動態申請...14 4.關鍵字const 15 5.關鍵字volatile.16 6.CPU字長與存儲器位寬不一致處理...17 總結...18 C語言嵌入式系統編程修煉之道——屏幕操作篇...19 1.漢字處理...19 2.系統時間顯示...20 3.動畫顯示...21 4.菜單操作...22 5.模擬MessageBox函數...24 總結...26 C語言嵌入式系統編程修煉之道——鍵盤操作篇...27 1.處理功能鍵...27 2.處理數字鍵...28 3.整理用戶輸入...29 總結...30 C語言嵌入式系統編程修煉之道——性能優化篇...31 1.使用宏定義...31 2.使用寄存器變量...31 3.內嵌匯編...32 4.利用硬件特性...32 5.活用位操作...33 總結
C語言嵌入式系統編程修煉之道——背景篇 不同于一般形式的軟件編程,嵌入式系統編程建立在特定的硬件平臺上,勢必要求其編程語言具備較強的硬件直接操作能力。無疑,匯編語言具備這樣的特質。但是,歸因于匯編語言開發過程的復雜性,它并不是嵌入式系統開發的一般選擇。而與之相比,C語言——一種“高級的低級”語言,則成為嵌入式系統開發的最佳選擇。筆者在嵌入式系統項目的開發過程中,一次又一次感受到C語言的精妙,沉醉于C語言給嵌入式開發帶來的便利。本文的目的在于進行“C語言嵌入式系統開發的內功心法”秀,一共包括25招。
圖1給出了本文的討論所基于的硬件平臺,實際上,這也是大多數嵌入式系統的硬件平臺。它包括兩部分:
(1)
以通用處理器為中心的協議處理模塊,用于網絡控制協議的處理;(2)
以數字信號處理器(DSP)為中心的信號處理模塊,用于調制、解調和數/模信號轉換。
本文的討論主要圍繞以通用處理器為中心的協議處理模塊進行,因為它更多地牽涉到具體的C語言編程技巧。而DSP編程則重點關注具體的數字信號處理算法,主要涉及通信領域的知識,不是本文的討論重點。
著眼于討論普遍的嵌入式系統C編程技巧,系統的協議處理模塊沒有選擇特別的CPU,而是選擇了眾所周知的CPU芯片——80186,每一位學習過《微機原理》的讀者都應該對此芯片有一個基本的認識,且對其指令集比較熟悉。80186的字長是16位,可以尋址到的內存空間為1MB,只有實地址模式。C語言編譯生成的指針為32位(雙字),高16位為段地址,低16位為段內編譯,一段最多64KB。
圖1 系統硬件架構
協議處理模塊中的FLASH和RAM幾乎是每個嵌入式系統的必備設備,前者用于存儲程序,后者則是程序運行時指令及數據的存放位置。系統所選擇的FLASH和RAM的位寬都為16位,與CPU一致。
實時鐘芯片可以為系統定時,給出當前的年、月、日及具體時間(小時、分、秒及毫秒),可以設定其經過一段時間即向CPU提出中斷或設定報警時間到來時向CPU提出中斷(類似鬧鐘功能)。
NVRAM(非易失去性RAM)具有掉電不丟失數據的特性,可以用于保存系統的設置信息,譬如網絡協議參數等。在系統掉電或重新啟動后,仍然可以讀取先前的設置信息。其位寬為8位,比CPU字長小。文章特意選擇一個與CPU字長不一致的存儲芯片,為后文中一節的討論創造條件。
UART則完成CPU并行數據傳輸與RS-232串行數據傳輸的轉換,它可以在接收到[1~MAX_BUFFER]字節后向CPU提出中斷,MAX_BUFFER為UART芯片存儲接收到字節的最大緩沖區。
鍵盤控制器和顯示控制器則完成系統人機界面的控制。以上提供的是一個較完備的嵌入式系統硬件架構,實際的系統可能包含更少的外設。之所以選擇一個完備的系統,是為了后文更全面的討論嵌入式系統C語言編程技巧的方方面面,所有設備都會成為后文的分析目標。
嵌入式系統需要良好的軟件開發環境的支持,由于嵌入式系統的目標機資源受限,不可能在其上建立龐大、復雜的開發環境,因而其開發環境和目標運行環境相互分離。因此,嵌入式應用軟件的開發方式一般是,在宿主機(Host)上建立開發環境,進行應用程序編碼和交叉編譯,然后宿主機同目標機(Target)建立連接,將應用程序下載到目標機上進行交叉調試,經過調試和優化,最后將應用程序固化到目標機中實際運行。
CAD-UL是適用于x86處理器的嵌入式應用軟件開發環境,它運行在Windows操作系統之上,可生成x86處理器的目標代碼并通過PC機的COM口(RS-232串口)或以太網口下載到目標機上運行,如圖2。其駐留于目標機FLASH存儲器中的monitor程序可以監控宿主機Windows調試平臺上的用戶調試指令,獲取CPU寄存器的值及目標機存儲空間、I/O空間的內容。圖2 交叉開發環境
后續章節將從軟件架構、內存操作、屏幕操作、鍵盤操作、性能優化等多方面闡述C語言嵌入式系統的編程技巧。軟件架構是一個宏觀概念,與具體硬件的聯系不大;內存操作主要涉及系統中的FLASH、RAM和NVRAM芯片;屏幕操作則涉及顯示控制器和實時鐘;鍵盤操作主要涉及鍵盤控制器;性能優化則給出一些具體的減小程序時間、空間消耗的技巧。
本文即將講述的25個主題可分為兩類,一類是編程技巧,有很強的適用性;一類則介紹嵌入式系統編程的一般常識,具有一定的理論意義。So, let’s go.C語言嵌入式系統編程修煉之道——軟件架構篇 1.模塊劃分
模塊劃分的“劃”是規劃的意思,意指怎樣合理的將一個很大的軟件劃分為一系列功能獨立的部分合作完成系統的需求。C語言作為一種結構化的程序設計語言,在模塊的劃分上主要依據功能(依功能進行劃分在面向對象設計中成為一個錯誤,牛頓定律遇到了相對論),C語言模塊化程序設計需理解如下概念:(1)
模塊即是一個.c文件和一個.h文件的結合,頭文件(.h)中是對于該模塊接口的聲明;
(2)
某模塊提供給其它模塊調用的外部函數及數據需在.h中文件中冠以extern關鍵字聲明;
(3)
模塊內的函數和全局變量需在.c文件開頭冠以static關鍵字聲明;(4)
永遠不要在.h文件中定義變量!定義變量和聲明變量的區別在于定義會產生內存分配的操作,是匯編階段的概念;而聲明則只是告訴包含該聲明的模塊在連接階段從其它模塊尋找外部函數和變量。如: /*module1.h*/ int a = 5;
/* 在模塊1的.h文件中定義int a */
/*module1.c*/ #include “module1.h”
/* 在模塊1中包含模塊1的.h文件 */ /*module2.c*/ #include “module1.h”
/* 在模塊2中包含模塊1的.h文件 */ /*module3.c*/ #include “module1.h”
/* 在模塊3中包含模塊1的.h文件 */ 以上程序的結果是在模塊1、2、3中都定義了整型變量a,a在不同的模塊中對應不同的地址單元,這個世界上從來不需要這樣的程序。正確的做法是: /*module1.h*/ extern int a;
/* 在模塊1的.h文件中聲明int a */ /*module1.c*/ #include “module1.h”
/* 在模塊1中包含模塊1的.h文件 */ int a = 5;
/* 在模塊1的.c文件中定義int a */ /*module2.c*/ #include “module1.h”
/* 在模塊2中包含模塊1的.h文件 */
/*module3.c*/ #include “module1.h”
/* 在模塊3中包含模塊1的.h文件 */ 這樣如果模塊1、2、3操作a的話,對應的是同一片內存單元。一個嵌入式系統通常包括兩類模塊:
(1)硬件驅動模塊,一種特定硬件對應一個模塊;
(2)軟件功能模塊,其模塊的劃分應滿足低偶合、高內聚的要求。2.多任務還是單任務
所謂“單任務系統”是指該系統不能支持多任務并發操作,宏觀串行地執行一個任務。而多任務系統則可以宏觀并行(微觀上可能串行)地“同時”執行多個任務。
多任務的并發執行通常依賴于一個多任務操作系統(OS),多任務OS的核心是系統調度器,它使用任務控制塊(TCB)來管理任務調度功能。TCB包括任務的當前狀態、優先級、要等待的事件或資源、任務程序碼的起始地址、初始堆棧指針等信息。調度器在任務被激活時,要用到這些信息。此外,TCB還被用來存放任務的“上下文”(context)。任務的上下文就是當一個執行中的任務被停止時,所要保存的所有信息。通常,上下文就是計算機當前的狀態,也即各個寄存器的內容。當發生任務切換時,當前運行的任務的上下文被存入TCB,并將要被執行的任務的上下文從它的TCB中取出,放入各個寄存器中。嵌入式多任務OS的典型例子有Vxworks、ucLinux等。嵌入式OS并非遙不可及的神壇之物,我們可以用不到1000行代碼實現一個針對80186處理器的功能最簡單的OS內核,作者正準備進行此項工作,希望能將心得貢獻給大家。
究竟選擇多任務還是單任務方式,依賴于軟件的體系是否龐大。例如,絕大多數手機程序都是多任務的,但也有一些小靈通的協議棧是單任務的,沒有操作系統,它們的主程序輪流調用各個軟件模塊的處理程序,模擬多任務環境。3.單任務程序典型架構
(1)從CPU復位時的指定地址開始執行;(2)跳轉至匯編代碼startup處執行;
(3)跳轉至用戶主程序main執行,在main中完成: a.初試化各硬件設備;
b.初始化各軟件模塊; c.進入死循環(無限循環),調用各模塊的處理函數
用戶主程序和各模塊的處理函數都以C語言完成。用戶主程序最后都進入了一個死循環,其首選方案是: while(1){ } 有的程序員這樣寫: for(;;){ } 這個語法沒有確切表達代碼的含義,我們從for(;;)看不出什么,只有弄明白for(;;)在C語言中意味著無條件循環才明白其意。下面是幾個“著名”的死循環:(1)操作系統是死循環;(2)WIN32程序是死循環;(3)嵌入式系統軟件是死循環;
(4)多線程程序的線程處理函數是死循環。你可能會辯駁,大聲說:“凡事都不是絕對的,2、3、4都可以不是死循環”。Yes,you are right,但是你得不到鮮花和掌聲。實際上,這是一個沒有太大意義的牛角尖,因為這個世界從來不需要一個處理完幾個消息就喊著要OS殺死它的WIN32程序,不需要一個剛開始RUN就自行了斷的嵌入式系統,不需要莫名其妙啟動一個做一點事就干掉自己的線程。有時候,過于嚴謹制造的不是便利而是麻煩。君不見,五層的TCP/IP協議棧超越嚴謹的ISO/OSI七層協議棧大行其道成為事實上的標準? 經常有網友討論:
printf(“%d,%d”,++i,i++);
/* 輸出是什么?*/ c = a+++b;
/* c=? */ 等類似問題。面對這些問題,我們只能發出由衷的感慨:世界上還有很多有意義的事情等著我們去消化攝入的食物。實際上,嵌入式系統要運行到世界末日。4.中斷服務程序
中斷是嵌入式系統中重要的組成部分,但是在標準C中不包含中斷。許多編譯開發商在標準C上增加了對中斷的支持,提供新的關鍵字用于標示中斷服務程序(ISR),類似于__interrupt、#program interrupt等。當一個函數被定義為ISR的時候,編譯器會自動為該函數增加中斷服務程序所需要的中斷現場入棧和出棧代碼。
中斷服務程序需要滿足如下要求:(1)不能返回值;
(2)不能向ISR傳遞參數;
(3)ISR應該盡可能的短小精悍;
(4)printf(char * lpFormatString,?)函數會帶來重入和性能問題,不能在ISR中采用。
在某項目的開發中,我們設計了一個隊列,在中斷服務程序中,只是將中斷類型添加入該隊列中,在主程序的死循環中不斷掃描中斷隊列是否有中斷,有則取出隊列中的第一個中斷類型,進行相應處理。/* 存放中斷的隊列 */ typedef struct tagIntQueue { int intType;
/* 中斷類型 */ struct tagIntQueue *next;}IntQueue;
IntQueue lpIntQueueHead;
__interrupt ISRexample(){
int intType;
intType = GetSystemType();QueueAddTail(lpIntQueueHead, intType);/* 在隊列尾加入新的中斷 */ } 在主程序循環中判斷是否有中斷: While(1){ If(!IsIntQueueEmpty())
{
intType = GetFirstInt();
switch(intType)
/* 是不是很象WIN32程序的消息解析函數? */
{
/* 對,我們的中斷類型解析很類似于消息驅動 */
case xxx:
/* 我們稱其為“中斷驅動”吧? */
…
break;
case xxx:
…
break;
…
} }
} 按上述方法設計的中斷服務程序很小,實際的工作都交由主程序執行了。5.硬件驅動模塊
一個硬件驅動模塊通常應包括如下函數:(1)中斷服務程序ISR(2)硬件初始化
a.修改寄存器,設置硬件參數(如UART應設置其波特率,AD/DA設備應設置其采樣速率等);
b.將中斷服務程序入口地址寫入中斷向量表: /* 設置中斷向量表 */
m_myPtr = make_far_pointer(0l);/* 返回void far型指針void far * */
m_myPtr += ITYPE_UART;/* ITYPE_UART: uart中斷服務程序 */ /* 相對于中斷向量表首地址的偏移 */
*m_myPtr = &UART _Isr;
/* UART _Isr:UART的中斷服務程序 */(3)設置CPU針對該硬件的控制線
a.如果控制線可作PIO(可編程I/O)和控制信號用,則設置CPU內部對應寄存器使其作為控制信號;
b.設置CPU內部的針對該設備的中斷屏蔽位,設置中斷方式(電平觸發還是邊緣觸發)。
(4)提供一系列針對該設備的操作接口函數。例如,對于LCD,其驅動模塊應提供繪制像素、畫線、繪制矩陣、顯示字符點陣等函數;而對于實時鐘,其驅動模塊則需提供獲取時間、設置時間等函數。6.C的面向對象化
在面向對象的語言里面,出現了類的概念。類是對特定數據的特定操作的集合體。類包含了兩個范疇:數據和操作。而C語言中的struct僅僅是數據的集合,我們可以利用函數指針將struct模擬為一個包含數據和操作的“類”。下面的C程序模擬了一個最簡單的“類”: #ifndef C_Class
#define C_Class struct #endif C_Class A {
C_Class A *A_this;
/* this指針 */
void(*Foo)(C_Class A *A_this);/* 行為:函數指針 */
int a;
/* 數據 */
int b;};我們可以利用C語言模擬出面向對象的三個特性:封裝、繼承和多態,但是更多的時候,我們只是需要將數據與行為封裝以解決軟件結構混亂的問題。C模擬面向對象思想的目的不在于模擬行為本身,而在于解決某些情況下使用C語言編程時程序整體框架結構分散、數據和函數脫節的問題。我們在后續章節會看到這樣的例子。總結
本篇介紹了嵌入式系統編程軟件架構方面的知識,主要包括模塊劃分、多任務還是單任務選取、單任務程序典型架構、中斷服務程序、硬件驅動模塊設計等,從宏觀上給出了一個嵌入式系統軟件所包含的主要元素。
請記住:軟件結構是軟件的靈魂!結構混亂的程序面目可憎,調試、測試、維護、升級都極度困難。
一個高尚的程序員應該是寫出如藝術作品般程序的程序員。
C語言嵌入式系統編程修煉之道——內存操作篇 1.數據指針
在嵌入式系統的編程中,常常要求在特定的內存單元讀寫內容,匯編有對應的MOV指令,而除C/C++以外的其它編程語言基本沒有直接訪問絕對地址的能力。在嵌入式系統的實際調試中,多借助C語言指針所具有的對絕對地址單元內容的讀寫能力。以指針直接操作內存多發生在如下幾種情況:
(1)
某I/O芯片被定位在CPU的存儲空間而非I/O空間,而且寄存器對應于某特定地址;
(2)
兩個CPU之間以雙端口RAM通信,CPU需要在雙端口RAM的特定單元(稱為mail box)書寫內容以在對方CPU產生中斷;
(3)
讀取在ROM或FLASH的特定單元所燒錄的漢字和英文字模。譬如:
unsigned char *p =(unsigned char *)0xF000FF00;*p=11;以上程序的意義為在絕對地址0xF0000+0xFF00(80186使用16位段地址和16位偏移地址)寫入11。在使用絕對地址指針時,要注意指針自增自減操作的結果取決于指針指向的數據類別。上例中p++后的結果是p= 0xF000FF01,若p指向int,即: int *p =(int *)0xF000FF00;p++(或++p)的結果等同于:p = p+sizeof(int),而p—(或—p)的結果是p = p-sizeof(int)。同理,若執行:
long int *p =(long int *)0xF000FF00;則p++(或++p)的結果等同于:p = p+sizeof(long int),而p—(或—p)的結果是p = p-sizeof(long int)。
記住:CPU以字節為單位編址,而C語言指針以指向的數據類型長度作自增和自減。理解這一點對于以指針直接操作內存是相當重要的。2.函數指針
首先要理解以下三個問題:
(1)C語言中函數名直接對應于函數生成的指令代碼在內存中的地址,因此函數名可以直接賦給指向函數的指針;
(2)調用函數實際上等同于“調轉指令+參數傳遞處理+回歸位置入棧”,本質上最核心的操作是將函數生成的目標代碼的首地址賦給CPU的PC寄存器;(3)因為函數調用的本質是跳轉到某一個地址單元的code去執行,所以可以“調用”一個根本就不存在的函數實體,暈?請往下看: 請拿出你可以獲得的任何一本大學《微型計算機原理》教材,書中講到,186 CPU啟動后跳轉至絕對地址0xFFFF0(對應C語言指針是0xF000FFF0,0xF000為段地址,0xFFF0為段內偏移)執行,請看下面的代碼:
typedef void(*lpFunction)();
/* 定義一個無參數、無返回類型的 */ /* 函數指針類型 */ lpFunction lpReset =(lpFunction)0xF000FFF0;
/* 定義一個函數指針,指向*/ /* CPU啟動后所執行第一條指令的位置 */ lpReset();
/* 調用函數 */ 在以上的程序中,我們根本沒有看到任何一個函數實體,但是我們卻執行了這樣的函數調用:lpReset(),它實際上起到了“軟重啟”的作用,跳轉到CPU啟動后第一條要執行的指令的位置。
記住:函數無它,唯指令集合耳;你可以調用一個沒有函數體的函數,本質上只是換一個地址開始執行指令!3.數組vs.動態申請
在嵌入式系統中動態內存申請存在比一般系統編程時更嚴格的要求,這是因為嵌入式系統的內存空間往往是十分有限的,不經意的內存泄露會很快導致系統的崩潰。
所以一定要保證你的malloc和free成對出現,如果你寫出這樣的一段程序: char * function(void){
char *p;
p =(char *)malloc(…);
if(p==NULL)?;
?
/* 一系列針對p的操作 */ return p;} 在某處調用function(),用完function中動態申請的內存后將其free,如下: char *q = function();? free(q);上述代碼明顯是不合理的,因為違反了malloc和free成對出現的原則,即“誰申請,就由誰釋放”原則。不滿足這個原則,會導致代碼的耦合度增大,因為用戶在調用function函數時需要知道其內部細節!
正確的做法是在調用處申請內存,并傳入function函數,如下: char *p=malloc(…);if(p==NULL)?;function(p);? free(p);p=NULL;而函數function則接收參數p,如下: void function(char *p){ ?
/* 一系列針對p的操作 */ } 基本上,動態申請內存方式可以用較大的數組替換。對于編程新手,筆者推薦你盡量采用數組!嵌入式系統可以以博大的胸襟接收瑕疵,而無法“海納”錯誤。畢竟,以最笨的方式苦練神功的郭靖勝過機智聰明卻范政治錯誤走反革命道路的楊康。
給出原則:
(1)盡可能的選用數組,數組不能越界訪問(真理越過一步就是謬誤,數組越過界限就光榮地成全了一個混亂的嵌入式系統);
(2)如果使用動態申請,則申請后一定要判斷是否申請成功了,并且malloc和free應成對出現!4.關鍵字const const意味著“只讀”。區別如下代碼的功能非常重要,也是老生長嘆,如果你還不知道它們的區別,而且已經在程序界摸爬滾打多年,那只能說這是一個悲哀: const int a;int const a;const int *a;int * const a;int const * a const;(1)關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息。例如,在函數的形參前添加const關鍵字意味著這個參數在函數體內不會被修改,屬于“輸入參數”。在有多個形參的時候,函數的調用者可以憑借參數前是否有const關鍵字,清晰的辨別哪些是輸入參數,哪些是可能的輸出參數。
(2)合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改,這樣可以減少bug的出現。const在C++語言中則包含了更豐富的含義,而在C語言中僅意味著:“只能讀的普通變量”,可以稱其為“不能改變的變量”(這個說法似乎很拗口,但卻最準確的表達了C語言中const的本質),在編譯階段需要的常數仍然只能以#define宏定義!故在C語言中如下程序是非法的: const int SIZE = 10;char a[SIZE];/* 非法:編譯階段不能用到變量 */ 5.關鍵字volatile C語言編譯器會對用戶書寫的代碼進行優化,譬如如下代碼: int a,b,c;a = inWord(0x100);/*讀取I/O空間0x100端口的內容存入a變量*/ b = a;a = inWord(0x100);/*再次讀取I/O空間0x100端口的內容存入a變量*/ c = a;很可能被編譯器優化為: int a,b,c;a = inWord(0x100);/*讀取I/O空間0x100端口的內容存入a變量*/ b = a;c = a;但是這樣的優化結果可能導致錯誤,如果I/O空間0x100端口的內容在執行第一次讀操作后被其它程序寫入新值,則其實第2次讀操作讀出的內容與第一次不同,b和c的值應該不同。在變量a的定義前加上volatile關鍵字可以防止編譯器的類似優化,正確的做法是: volatile int a;
volatile變量可能用于如下幾種情況:
(1)并行設備的硬件寄存器(如:狀態寄存器,例中的代碼屬于此類);(2)一個中斷服務子程序中會訪問到的非自動變量(也就是全局變量);(3)多線程應用中被幾個任務共享的變量。6.CPU字長與存儲器位寬不一致處理
在背景篇中提到,本文特意選擇了一個與CPU字長不一致的存儲芯片,就是為了進行本節的討論,解決CPU字長與存儲器位寬不一致的情況。80186的字長為16,而NVRAM的位寬為8,在這種情況下,我們需要為NVRAM提供讀寫字節、字的接口,如下: typedef unsigned char BYTE;typedef unsigned int WORD;
/* 函數功能:讀NVRAM中字節
* 參數:wOffset,讀取位置相對NVRAM基地址的偏移
* 返回:讀取到的字節值 */ extern BYTE ReadByteNVRAM(WORD wOffset){
LPBYTE lpAddr =(BYTE*)(NVRAM + wOffset * 2);/* 為什么偏移要×2? */
return *lpAddr;}
/* 函數功能:讀NVRAM中字
* 參數:wOffset,讀取位置相對NVRAM基地址的偏移
* 返回:讀取到的字 */ extern WORD ReadWordNVRAM(WORD wOffset){
WORD wTmp = 0;
LPBYTE lpAddr;
/* 讀取高位字節 */
lpAddr =(BYTE*)(NVRAM + wOffset * 2);
/* 為什么偏移要×2? */
wTmp +=(*lpAddr)*256;
/* 讀取低位字節 */
lpAddr =(BYTE*)(NVRAM +(wOffset +1)* 2);
/* 為什么偏移要×2? */
wTmp += *lpAddr;
return wTmp;}
/* 函數功能:向NVRAM中寫一個字節
*參數:wOffset,寫入位置相對NVRAM基地址的偏移 *
byData,欲寫入的字節 */ extern void WriteByteNVRAM(WORD wOffset, BYTE byData){
… }
/* 函數功能:向NVRAM中寫一個字 */ *參數:wOffset,寫入位置相對NVRAM基地址的偏移 *
wData,欲寫入的字 */ extern void WriteWordNVRAM(WORD wOffset, WORD wData){
… } 子貢問曰:Why偏移要乘以2? 子曰:請看圖1,16位80186與8位NVRAM之間互連只能以地址線A1對其A0,CPU本身的A0與NVRAM不連接。因此,NVRAM的地址只能是偶數地址,故每次以2為單位前進!
圖1 CPU與NVRAM地址線連接
子貢再問:So why 80186的地址線A0不與NVRAM的A0連接? 子曰:請看《IT論語》之《微機原理篇》,那里面講述了關于計算機組成的圣人之道。總結
本篇主要講述了嵌入式系統C編程中內存操作的相關技巧。掌握并深入理解關于數據指針、函數指針、動態申請內存、const及volatile關鍵字等的相關知識,是一個優秀的C語言程序設計師的基本要求。當我們已經牢固掌握了上述技巧后,我們就已經學會了C語言的99%,因為C語言最精華的內涵皆在內存操作中體現。
我們之所以在嵌入式系統中使用C語言進行程序設計,99%是因為其強大的內存操作能力!
如果你愛編程,請你愛C語言; 如果你愛C語言,請你愛指針; 如果你愛指針,請你愛指針的指針!
C語言嵌入式系統編程修煉之道——屏幕操作篇 1.漢字處理
現在要解決的問題是,嵌入式系統中經常要使用的并非是完整的漢字庫,往往只是需要提供數量有限的漢字供必要的顯示功能。例如,一個微波爐的LCD上沒有必要提供顯示“電子郵件”的功能;一個提供漢字顯示功能的空調的LCD上不需要顯示一條“短消息”,諸如此類。但是一部手機、小靈通則通常需要包括較完整的漢字庫。
如果包括的漢字庫較完整,那么,由內碼計算出漢字字模在庫中的偏移是十分簡單的:漢字庫是按照區位的順序排列的,前一個字節為該漢字的區號,后一個字節為該字的位號。每一個區記錄94個漢字,位號則為該字在該區中的位置。因此,漢字在漢字庫中的具體位置計算公式為:94*(區號-1)+位號-1。減1是因為數組是以0為開始而區號位號是以1為開始的。只需乘上一個漢字字模占用的字節數即可,即:(94*(區號-1)+位號-1)*一個漢字字模占用字節數,以16*16點陣字庫為例,計算公式則為:(94*(區號-1)+(位號-1))*32。漢字庫中從該位置起的32字節信息記錄了該字的字模信息。
對于包含較完整漢字庫的系統而言,我們可以以上述規則計算字模的位置。但是如果僅僅是提供少量漢字呢?譬如幾十至幾百個?最好的做法是: 定義宏:
# define EX_FONT_CHAR(value)
# define EX_FONT_UNICODE_VAL(value)(value), # define EX_FONT_ANSI_VAL(value)(value), 定義結構體:
typedef struct _wide_unicode_font16x16 { WORD value;
/* 內碼 */ BYTE data[32];/* 字模點陣 */ }Unicode;#define CHINESE_CHAR_NUM ?
/* 漢字數量 */ 字模的存儲用數組:
Unicode chinese[CHINESE_CHAR_NUM] = { {
EX_FONT_CHAR(“業”)
EX_FONT_UNICODE_VAL(0x4e1a)
{0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x04, 0x44, 0x44, 0x46, 0x24, 0x4c, 0x24, 0x48, 0x14, 0x50, 0x1c, 0x50,0x14, 0x60, 0x04, 0x40, 0x04, 0x40, 0x04, 0x44, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00}
},{
EX_FONT_CHAR(“中”)
EX_FONT_UNICODE_VAL(0x4e2d)
{0x01, 0x00, 0x01, 0x00, 0x21, 0x08, 0x3f, 0xfc, 0x21, 0x08, 0x21, 0x08, 0x21, 0x08, 0x21, 0x08, 0x21, 0x08,0x3f, 0xf8, 0x21, 0x08, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00}
},{
EX_FONT_CHAR(“云”)
EX_FONT_UNICODE_VAL(0x4e91)
{0x00, 0x00, 0x00, 0x30, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xfe, 0x03, 0x00, 0x07, 0x00,0x06, 0x40, 0x0c, 0x20, 0x18, 0x10, 0x31, 0xf8, 0x7f, 0x0c, 0x20, 0x08, 0x00, 0x00}
},{
EX_FONT_CHAR(“件”)
EX_FONT_UNICODE_VAL(0x4ef6)
{0x10, 0x40, 0x1a, 0x40, 0x13, 0x40, 0x32, 0x40, 0x23, 0xfc, 0x64, 0x40, 0xa4, 0x40, 0x28, 0x40, 0x2f, 0xfe,0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40}
} } 要顯示特定漢字的時候,只需要從數組中查找內碼與要求漢字內碼相同的即可獲得字模。如果前面的漢字在數組中以內碼大小順序排列,那么可以以二分查找法更高效的查找到漢字的字模。
這是一種很有效的組織小漢字庫的方法,它可以保證程序有很好的結構。2.系統時間顯示
從NVRAM中可以讀取系統的時間,系統一般借助NVRAM產生的秒中斷每秒讀取一次當前時間并在LCD上顯示。關于時間的顯示,有一個效率問題。因為時間有其特殊性,那就是60秒才有一次分鐘的變化,60分鐘才有一次小時變化,如果我們每次都將讀取的時間在屏幕上完全重新刷新一次,則浪費了大量的系統時間。
一個較好的辦法是我們在時間顯示函數中以靜態變量分別存儲小時、分鐘、秒,只有在其內容發生變化的時候才更新其顯示。extern void DisplayTime(…){
static BYTE byHour,byMinute,bySecond;
BYTE byNewHour, byNewMinute, byNewSecond;
byNewHour = GetSysHour();
byNewMinute = GetSysMinute();
byNewSecond = GetSysSecond();
if(byNewHour!= byHour)
{ ?
/* 顯示小時 */ byHour = byNewHour;}
if(byNewMinute!= byMinute)
{ ?
/* 顯示分鐘 */ byMinute = byNewMinute;}
if(byNewSecond!= bySecond)
{ ?
/* 顯示秒鐘 */ bySecond = byNewSecond;} } 這個例子也可以順便作為C語言中static關鍵字強大威力的證明。當然,在C++語言里,static具有了更加強大的威力,它使得某些數據和函數脫離“對象”而成為“類”的一部分,正是它的這一特點,成就了軟件的無數優秀設計。3.動畫顯示
動畫是無所謂有,無所謂無的,靜止的畫面走的路多了,也就成了動畫。隨著時間的變更,在屏幕上顯示不同的靜止畫面,即是動畫之本質。所以,在一個嵌入式系統的LCD上欲顯示動畫,必須借助定時器。沒有硬件或軟件定時器的世界是無法想像的:
(1)
沒有定時器,一個操作系統將無法進行時間片的輪轉,于是無法進行多任務的調度,于是便不再成其為一個多任務操作系統;
(2)
沒有定時器,一個多媒體播放軟件將無法運作,因為它不知道何時應該切換到下一幀畫面;
(3)
沒有定時器,一個網絡協議將無法運轉,因為其無法獲知何時包傳輸超時并重傳之,無法在特定的時間完成特定的任務。
因此,沒有定時器將意味著沒有操作系統、沒有網絡、沒有多媒體,這將是怎樣的黑暗?所以,合理并靈活地使用各種定時器,是對一個軟件人的最基本需求!在80186為主芯片的嵌入式系統中,我們需要借助硬件定時器的中斷來作為軟件定時器,在中斷發生后變更畫面的顯示內容。在時間顯示“xx:xx”中讓冒號交替有無,每次秒中斷發生后,需調用ShowDot: void ShowDot(){ static BOOL bShowDot = TRUE;
/* 再一次領略static關鍵字的威力 */ if(bShowDot)
{ showChar(‘:’,xPos,yPos);} else
{ showChar(‘ ’,xPos,yPos);
} bShowDot =!bShowDot;} 4.菜單操作
無數人為之絞盡腦汁的問題終于出現了,在這一節里,我們將看到,在C語言中哪怕用到一丁點的面向對象思想,軟件結構將會有何等的改觀!筆者曾經是個笨蛋,被菜單搞暈了,給出這樣的一個系統: 圖1 菜單范例
要求以鍵盤上的“←→”鍵切換菜單焦點,當用戶在焦點處于某菜單時,若敲擊鍵盤上的OK、CANCEL鍵則調用該焦點菜單對應之處理函數。我曾經傻傻地這樣做著:
/* 按下OK鍵 */ void onOkKey(){ /* 判斷在什么焦點菜單上按下Ok鍵,調用相應處理函數 */ Switch(currentFocus){ case MENU1:
menu1OnOk();
break;case MENU2:
menu2OnOk();
break;? } } /* 按下Cancel鍵 */ void onCancelKey(){ /* 判斷在什么焦點菜單上按下Cancel鍵,調用相應處理函數 */ Switch(currentFocus){ case MENU1:
menu1OnCancel();
break;case MENU2:
menu2OnCancel();
break;? } } 終于有一天,我這樣做了:
/* 將菜單的屬性和操作“封裝”在一起 */ typedef struct tagSysMenu
{
char *text;
/* 菜單的文本 */
BYTE xPos;/* 菜單在LCD上的x坐標 */
BYTE yPos;/* 菜單在LCD上的y坐標 */
void(*onOkFun)();
/* 在該菜單上按下ok鍵的處理函數指針 */
void(*onCancelFun)();/* 在該菜單上按下cancel鍵的處理函數指針 */ }SysMenu, *LPSysMenu;當我定義菜單時,只需要這樣: static SysMenu menu[MENU_NUM] = {
{
“menu1”, 0, 48, menu1OnOk, menu1OnCancel
} ,{
“ menu2”, 7, 48, menu2OnOk, menu2OnCancel
} ,{
“ menu3”, 7, 48, menu3OnOk, menu3OnCancel
} ,{
“ menu4”, 7, 48, menu4OnOk, menu4OnCancel
}
… };OK鍵和CANCEL鍵的處理變成: /* 按下OK鍵 */ void onOkKey(){
menu[currentFocusMenu].onOkFun();
} /* 按下Cancel鍵 */ void onCancelKey(){ menu[currentFocusMenu].onCancelFun();
} 程序被大大簡化了,也開始具有很好的可擴展性!我們僅僅利用了面向對象中的封裝思想,就讓程序結構清晰,其結果是幾乎可以在無需修改程序的情況下在系統中添加更多的菜單,而系統的按鍵處理函數保持不變。面向對象,真神了!5.模擬MessageBox函數
MessageBox函數,這個Windows編程中的超級猛料,不知道是多少入門者第一次用到的函數。還記得我們第一次在Windows中利用MessageBox輸出“Hello,World!”對話框時新奇的感覺嗎?無法統計,這個世界上究竟有多少程序員學習Windows編程是從MessageBox(“Hello,World!”,?)開始的。在我本科的學校,廣泛流傳著一個詞匯,叫做“‘Hello,World’級程序員”,意指入門級程序員,但似乎“‘Hello,World’級”這個說法更搞笑而形象。
圖2 經典的Hello,World!圖2給出了兩種永恒經典的Hello,World對話框,一種只具有“確定”,一種則包含“確定”、“取消”。是的,MessageBox的確有,而且也應該有兩類!這完全是由特定的應用需求決定的。
嵌入式系統中沒有給我們提供MessageBox,但是鑒于其功能強大,我們需要模擬之,一個模擬的MessageBox函數為:
/****************************************** /*
函數名稱:
MessageBox /*
功能說明:
彈出式對話框,顯示提醒用戶的信息 /*
參數說明:
lpStr---提醒用戶的字符串輸出信息
/*
TYPE---輸出格式(ID_OK = 0, ID_OKCANCEL = 1)/*
返回值:
返回對話框接收的鍵值,只有兩種 KEY_OK, KEY_CANCEL /****************************************** typedef enum TYPE
{ ID_OK,ID_OKCANCEL
}MSG_TYPE;extern
BYTE MessageBox(LPBYTE lpStr, BYTE TYPE){
BYTE keyValue =-1;
ClearScreen();
/* 清除屏幕 */
DisplayString(xPos,yPos,lpStr,TRUE);/* 顯示字符串 */
/* 根據對話框類型決定是否顯示確定、取消 */
switch(TYPE)
{
case
ID_OK:
DisplayString(13,yPos+High+1, “ 確定 ”, 0);
break;
case
ID_OKCANCEL:
DisplayString(8, yPos+High+1, “ 確定 ”, 0);
DisplayString(17,yPos+High+1, “ 取消 ”, 0);
break;
default:
break;
}
DrawRect(0, 0, 239, yPos+High+16+4);/* 繪制外框 */
/* MessageBox是模式對話框,阻塞運行,等待按鍵 */
while((keyValue!= KEY_OK)||(keyValue!= KEY_CANCEL))
{ keyValue = getSysKey();} /* 返回按鍵類型 */ if(keyValue== KEY_OK){ return ID_OK;} else { return ID_CANCEL;} } 上述函數與我們平素在VC++等中使用的MessageBox是何等的神似啊?實現這個函數,你會看到它在嵌入式系統中的妙用是無窮的。總結
本篇是本系列文章中技巧性最深的一篇,它提供了嵌入式系統屏幕顯示方面一些很巧妙的處理方法,靈活使用它們,我們將不再被LCD上凌亂不堪的顯示內容所困擾。
屏幕乃嵌入式系統生存之重要輔助,面目可憎之顯示將另用戶逃之夭夭。屏幕編程若處理不好,將是軟件中最不系統、最混亂的部分,筆者曾深受其害。
C語言嵌入式系統編程修煉之道——鍵盤操作篇 1.處理功能鍵
功能鍵的問題在于,用戶界面并非固定的,用戶功能鍵的選擇將使屏幕畫面處于不同的顯示狀態下。例如,主畫面如圖1: 圖1 主畫面
當用戶在設置XX上按下Enter鍵之后,畫面就切換到了設置XX的界面,如圖2:
圖2 切換到設置XX畫面
程序如何判斷用戶處于哪一畫面,并在該畫面的程序狀態下調用對應的功能鍵處理函數,而且保證良好的結構,是一個值得思考的問題。
讓我們來看看WIN32編程中用到的“窗口”概念,當消息(message)被發送給不同窗口的時候,該窗口的消息處理函數(是一個callback函數)最終被調用,而在該窗口的消息處理函數中,又根據消息的類型調用了該窗口中的對應處理函數。通過這種方式,WIN32有效的組織了不同的窗口,并處理不同窗口情況下的消息。
我們從中學習到的就是:
(1)將不同的畫面類比為WIN32中不同的窗口,將窗口中的各種元素(菜單、按鈕等)包含在窗口之中;
(2)給各個畫面提供一個功能鍵“消息”處理函數,該函數接收按鍵信息為參數;
(3)在各畫面的功能鍵“消息”處理函數中,判斷按鍵類型和當前焦點元素,并調用對應元素的按鍵處理函數。
/* 將窗口元素、消息處理函數封裝在窗口中 */ struct windows {
BYTE currentFocus;
ELEMENT element[ELEMENT_NUM];
void(*messageFun)(BYTE keyValue);
… };/* 消息處理函數 */ void messageFunction(BYTE keyValue){
BYTE i = 0;
/* 獲得焦點元素 */
while((element [i].ID!= currentFocus)&&(i < ELEMENT_NUM))
{
i++;
}
/* “消息映射” */
if(i < ELEMENT_NUM)
{
switch(keyValue)
{
case OK:
element[i].OnOk();
break;
…
}
} } 在窗口的消息處理函數中調用相應元素按鍵函數的過程類似于“消息映射”,這是我們從WIN32編程中學習到的。編程到了一個境界,很多東西都是相通的了。其它地方的思想可以拿過來為我所用,是為編程中的“拿來主義”。
在這個例子中,如果我們還想玩得更大一點,我們可以借鑒MFC中處理MESSAGE_MAP的方法,我們也可以學習MFC定義幾個精妙的宏來實現“消息映射”。2.處理數字鍵
用戶輸入數字時是一位一位輸入的,每一位的輸入都對應著屏幕上的一個顯示位置(x坐標,y坐標)。此外,程序還需要記錄該位置輸入的值,所以有效組織用戶數字輸入的最佳方式是定義一個結構體,將坐標和數值捆綁在一起: /* 用戶數字輸入結構體 */ typedef struct tagInputNum
{
BYTE byNum;/* 接收用戶輸入賦值 */
BYTE xPos;
/* 數字輸入在屏幕上的顯示位置x坐標 */
BYTE yPos;
/* 數字輸入在屏幕上的顯示位置y坐標 */
}InputNum, *LPInputNum;那么接收用戶輸入就可以定義一個結構體數組,用數組中的各位組成一個完整的數字:
InputNum inputElement[NUM_LENGTH];/* 接收用戶數字輸入的數組 */ /* 數字按鍵處理函數 */ extern void onNumKey(BYTE num){
if(num==0|| num==1)/* 只接收二進制輸入 */
{ /* 在屏幕上顯示用戶輸入 */ DrawText(inputElement[currentElementInputPlace].xPos, inputElement[currentElementInputPlace].yPos, “%1d”, num);
/* 將輸入賦值給數組元素 */
inputElement[currentElementInputPlace].byNum = num;
/* 焦點及光標右移 */
moveToRight();
} } 將數字每一位輸入的坐標和輸入值捆綁后,在數字鍵處理函數中就可以較有結構的組織程序,使程序顯得很緊湊。3.整理用戶輸入
繼續第2節的例子,在第2節的onNumKey函數中,只是獲取了數字的每一位,因而我們需要將其轉化為有效數據,譬如要轉化為有效的XXX數據,其方法是:
/* 從2進制數據位轉化為有效數據:XXX */ void convertToXXX(){
BYTE i;
XXX = 0;
for(i = 0;i < NUM_LENGTH;i++)
{
XXX += inputElement[i].byNum*power(2, NUM_LENGTH1);
}
} 反之,我們也可能需要在屏幕上顯示那些有效的數據位,因為我們也需要能夠反向轉化:
/* 從有效數據轉化為2進制數據位:XXX */ void convertFromXXX(){
BYTE i;
XXX = 0;
for(i = 0;i < NUM_LENGTH;i++)
{
inputElement[i].byNum = XXX / power(2, NUM_LENGTH1)% 2;
}
} 當然在上面的例子中,因為數據是2進制的,用power函數不是很好的選擇,直接用“<< >>”移位操作效率更高,我們僅是為了說明問題的方便。試想,如果用戶輸入是十進制的,power函數或許是唯一的選擇了。總結
本篇給出了鍵盤操作所涉及的各個方面:功能鍵處理、數字鍵處理及用戶輸入整理,基本上提供了一個全套的按鍵處理方案。對于功能鍵處理方法,將LCD屏幕與Windows窗口進行類比,提出了較新穎地解決屏幕、鍵盤繁雜交互問題的方案。
計算機學的許多知識都具有相通性,因而,不斷追趕時髦技術而忽略基本功的做法是徒勞無意的。我們最多需要“精通”三種語言(精通,一個在如今的求職簡歷里泛濫成災的詞語),最佳拍檔是匯編、C、C++(或JAVA),很顯然,如果你“精通”了這三種語言,其它語言你應該是可以很快“熟悉”的,否則你就沒有“精通”它們。
C語言嵌入式系統編程修煉之道——性能優化篇 1.使用宏定義
在C語言中,宏是產生內嵌代碼的唯一方法。對于嵌入式系統而言,為了能達到性能要求,宏是一種很好的代替函數的方法。
寫一個“標準”宏MIN,這個宏輸入兩個參數并返回較小的一個:
錯誤做法:
#define MIN(A,B)(A <= B ? A : B)正確做法:
#define MIN(A,B)((A)<=(B)?(A):(B))對于宏,我們需要知道三點:(1)宏定義“像”函數;
(2)宏定義不是函數,因而需要括上所有“參數”;(3)宏定義可能產生副作用。下面的代碼:
least = MIN(*p++, b);將被替換為:
((*p++)<=(b)?(*p++):(b))發生的事情無法預料。
因而不要給宏定義傳入有副作用的“參數”。2.使用寄存器變量
當對一個變量頻繁被讀寫時,需要反復訪問內存,從而花費大量的存取時間。為此,C語言提供了一種變量,即寄存器變量。這種變量存放在CPU的寄存器中,使用時,不需要訪問內存,而直接從寄存器中讀寫,從而提高效率。寄存器變量的說明符是register。對于循環次數較多的循環控制變量及循環體內反復使用的變量均可定義為寄存器變量,而循環計數是應用寄存器變量的最好候選者。(1)
只有局部自動變量和形參才可以定義為寄存器變量。因為寄存器變量屬于動態存儲方式,凡需要采用靜態存儲方式的量都不能定義為寄存器變量,包括:模塊間全局變量、模塊內全局變量、局部static變量;
(2)
register是一個“建議”型關鍵字,意指程序建議該變量放在寄存器中,但最終該變量可能因為條件不滿足并未成為寄存器變量,而是被放在了存儲器中,但編譯器中并不報錯(在C++語言中有另一個“建議”型關鍵字:inline)。
下面是一個采用寄存器變量的例子: /* 求1+2+3+?.+n的值 */ WORD Addition(BYTE n){ register i,s=0;for(i=1;i<=n;i++){ s=s+i;} return s;} 本程序循環n次,i和s都被頻繁使用,因此可定義為寄存器變量。3.內嵌匯編
程序中對時間要求苛刻的部分可以用內嵌匯編來重寫,以帶來速度上的顯著提高。但是,開發和測試匯編代碼是一件辛苦的工作,它將花費更長的時間,因而要慎重選擇要用匯編的部分。
在程序中,存在一個80-20原則,即20%的程序消耗了80%的運行時間,因而我們要改進效率,最主要是考慮改進那20%的代碼。
嵌入式C程序中主要使用在線匯編,即在C程序中直接插入_asm{ }內嵌匯編語句:
/* 把兩個輸入參數的值相加,結果存放到另外一個全局變量中 */ int result;
void Add(long a, long *b)
{
_asm
{
MOV
AX, a
MOV
BX, b
ADD
AX, [BX]
MOV
result, AX
}
}
4.利用硬件特性
首先要明白CPU對各種存儲器的訪問速度,基本上是:
CPU內部RAM > 外部同步RAM > 外部異步RAM > FLASH/ROM 對于程序代碼,已經被燒錄在FLASH或ROM中,我們可以讓CPU直接從其中讀取代碼執行,但通常這不是一個好辦法,我們最好在系統啟動后將FLASH或ROM中的目標代碼拷貝入RAM中后再執行以提高取指令速度; 對于UART等設備,其內部有一定容量的接收BUFFER,我們應盡量在BUFFER被占滿后再向CPU提出中斷。例如計算機終端在向目標機通過RS-232傳遞數據時,不宜設置UART只接收到一個BYTE就向CPU提中斷,從而無謂浪費中斷處理時間;
如果對某設備能采取DMA方式讀取,就采用DMA讀取,DMA讀取方式在讀取目標中包含的存儲信息較大時效率較高,其數據傳輸的基本單位是塊,而所傳輸的數據是從設備直接送入內存的(或者相反)。DMA方式較之中斷驅動方式,減少了CPU 對外設的干預,進一步提高了CPU與外設的并行操作程度。5.活用位操作
使用C語言的位操作可以減少除法和取模的運算。在計算機程序中數據的位是可以操作的最小數據單位,理論上可以用“位運算”來完成所有的運算和操作,因而,靈活的位操作可以有效地提高程序運行的效率。舉例如下: /* 方法1 */ int i,j;i = 879 / 16;j = 562 % 32;
/* 方法2 */ int i,j;i = 879 >> 4;j = 562-(562 >> 5 << 5);對于以2的指數次方為“*”、“/”或“%”因子的數學運算,轉化為移位運算“<< >>”通常可以提高算法效率。因為乘除運算指令周期通常比移位運算大。
C語言位運算除了可以提高運算效率外,在嵌入式系統的編程中,它的另一個最典型的應用,而且十分廣泛地正在被使用著的是位間的與(&)、或(|)、非(~)操作,這跟嵌入式系統的編程特點有很大關系。我們通常要對硬件寄存器進行位設置,譬如,我們通過將AM186ER型80186處理器的中斷屏蔽控制寄存器的第低6位設置為0(開中斷2),最通用的做法是: #define INT_I2_MASK
0x0040
wTemp = inword(INT_MASK);outword(INT_MASK, wTemp &~INT_I2_MASK);而將該位設置為1的做法是:
#define INT_I2_MASK
0x0040
wTemp = inword(INT_MASK);outword(INT_MASK, wTemp | INT_I2_MASK);判斷該位是否為1的做法是:
#define INT_I2_MASK
0x0040
wTemp = inword(INT_MASK);if(wTemp & INT_I2_MASK){
?
/* 該位為1 */ } 上述方法在嵌入式系統的編程中是非常常見的,我們需要牢固掌握。總結
在性能優化方面永遠注意80-20準備,不要優化程序中開銷不大的那80%,這是勞而無功的。
宏定義是C語言中實現類似函數功能而又不具函數調用和返回開銷的較好方法,但宏在本質上不是函數,因而要防止宏展開后出現不可預料的結果,對宏的定義和使用要慎而處之。很遺憾,標準C至今沒有包括C++中inline函數的功能,inline函數兼具無調用開銷和安全的優點。
使用寄存器變量、內嵌匯編和活用位操作也是提高程序效率的有效方法。除了編程上的技巧外,為提高系統的運行效率,我們通常也需要最大可能地利用各種硬件設備自身的特點來減小其運轉開銷,例如減小中斷次數、利用DMA傳輸方式等。
第五篇:修煉美術課堂語言的三種有效途徑
修煉美術課堂語言的三種有效途徑
【摘 要】語言是人類特有的用來交流、溝通的工具,更是課堂教學中少不了的法寶。作為藝術課任教的一位美術教師,更需要掌握精簡睿智的語言藝術。
【關鍵詞】美術課堂;修煉語言
教師是“吃開口飯的”,需要掌握好語言藝術。實踐中的落實往往比想象中的困難。在美術教學課堂中,一般教師的語言往往退居其次,“美”的引導和“術”的示范卻占了較大的比例。特別是小學階段年輕的美術老師,更多的時間是將花在規范課堂紀律上,對語言的修煉往往沒有下足功夫。
美術教師如何說話,如何提高語言修煉?是不是美術老師就不需要字字珠璣計較?語言上的修煉,在很多美術老師看來可能有點“不可思議”,但實際教學中卻是一件需要真正正視的事情。在聽課中往往發現,即使是同一課時相同的教學設計,不同風格的老師來上,教學效果也大相徑庭,期中就包括了語言效果帶來的差異。有的教師言簡意賅,風趣幽默,學生聽得興趣盎然,創作靈感源源不斷,作業豐富多彩。但有的老師語言沉悶壓抑,講解啰嗦,學生提不起精神,不得要領,學生聽得云里霧里。不同的課堂語言,使得教學效果截然兩樣,這已是無需爭辯的事實。推而廣之,在美術課上也存在著相同的問題,由此看來“一堂美術課的成功與否和教師的課堂語言表達有很大的關系。可以說,教師的教學水平、能力、魅力及教學修養、教學風格等都會從教師課堂語言的運用上具體體現出來。”善于駕馭語言的美術教師在課堂中能夠表現出來的高超的語言藝術,其結果總是使人贊嘆不已。甚至可以說,作為教師我們一輩子可能都需要研究在課堂中怎么說話。具備了注重語言修煉的美術教師可以直接提高課堂教學質量,提升孩子的們的審美情趣和能力,對孩子們的健康成長有很大幫助。結合自己的上課經驗,筆者想從三個方面來談談如何進行美術課堂中的語言修煉。
一、美術課堂盡量使用準確規范的術語,語言隨和但不隨意
語言的修煉離不開規范、專業的語言,它可以提高美術課堂的藝術氛圍。美術課一般出現泛泛而談,而事實上美術課的時間把控是十分重要的,一節40分鐘的課程,要求導入、示范、學生創作、講評、小結這幾個過程基本都要有,簡潔明了的提煉語在開始的導入環節就顯得尤其珍貴。太平白直敘未免顯得呆板,過分追求花式的開頭又未免冗長,如在上《群居建筑》時,有的老師提問:“各個地方的民居有什么特點?它們分別是由什么圖形組成的?”這樣問顯然是不妥的。作為立體的建筑,應該問:“它們分別是由哪些形體組成的?”如立方體、圓柱體、球體、錐體等等。另外在評價美術作品時也要注重用規范的美術語言,如:“這幅作品的色彩鮮艷、涂色均勻”“這幅畫的構圖比較合理。”“他的畫表現了近大遠小的透視現象,有空間感。”??小學美術課中所出現的專業術語并不多,所以教師在使用時就更應該用正確、規范的語言來進行表述。學生才能夠耳濡目染,并且把這種美的理解根植在內心。
語言的規范性還表現在準確性上。教學語言用詞用句要正確,語法要無誤,這是構成準確性的起點。
二、精心組織美術課堂各個教學環節,語言嚴謹而又自然
美術課與其他課程一樣需要師生的密切配合才能取得好效果,教師在課堂中藥精心組織各個教學環節,語言表達嚴謹、自然,讓學生輕松學習。(以筆者任教經驗為例)我曾經聽過一位富有經驗教師講過,美術教師講授時間不要超過該年齡段學生的年紀。這個說法與新課標規定不謀而合,新課標關于美術課老師講解時間明確規定,每節課教師授課時間是10~15分鐘,有的課堂可以變短。在短時間,教會學生相應的美術知識和技法要求教師語言精練,抓住重點,用較少的時間傳遞大量的信息。并精心錘煉描述性語言,把學生帶入美的意境。課堂中教師偶爾出現一句富有詩情畫意的語句,教學效果更是不同凡響。另外美術課堂的語言講究條理性,必須合乎語法、邏輯,同時尊重學生的身心特征規律。如在低年級教師的語言需兒童化,面對六年級學生語言過分兒化又會讓學生反感,與其年齡特點不符合。因此在課堂語言的組織上,單調冗長的說辭只能給學生帶來思維的混亂,導致學生心理煩躁、注意力分散。不少美術教師在課堂中語言單調、枯燥,既無啟發性,又無吸引力。枯燥的語言不僅影響學生的積極思維,而且會影響學生的語言表達。更嚴重的后果是喪失了美術課的魅力,使學生厭學美術。
三、靈活處理美術課堂中的各種情況,語言幽默不失冷靜
興趣是最好的老師,良好的開始是成功的一半,因此富有激情、創意的導入環節就顯得尤為重要,如在《我的動物朋友》早上一進課室,我就神秘的告訴對學生說,“老師今天早上收到一封神秘來信,想知道是誰寄來的?信里面寫的有哪些內容嗎?”說完我還特意揚了揚手中巨大的黃色信封,班上所有的學生都目不轉睛的看著我,齊刷刷舉起了小手躍躍欲試,學生的好奇心這一刻都被調的老高,氣氛也一下子進入了正題。
此外,幽默、風趣的語言也是課堂教學的潤滑劑。幽默的課堂語言能使學生在笑聲中受到啟迪和教育,在還知不覺中學習知識。如在《神奇的熱帶植物》一課中,有學生畫了一株巨大的仙人掌,簡單粗暴的構圖,為了引導他繼續完善背景,我試著問他,“你的仙人掌種在什么地方呢?有的種在沙漠,有的種在花盆,那你的呢?”“種在你頭上”他大聲的說,全班哄笑,那刻,我感到尷尬和難堪,似乎受到了不公正的待遇,想直接把學生的畫拿走,不再讓他的創作繼續,可是這樣的結果呢?我的生氣已經無法避免,更嚴重的是他可能不再喜歡畫畫,不再喜歡上美術課,他可能還會影響喜歡畫畫的同學,難道這是我希望看到的嗎?我想了下,笑著說“謝謝你把仙人掌種在老師頭上,我想那一定是道特別的風景,可是老師如果真頂著一個巨大的仙人掌給你們上課,你們覺得安全嗎?那老師不是變成了刺猬嗎?”全班又一次哄笑,他似乎也知道不妥,慢慢低下頭,默默的完成自己的作品。另外幽默的語言還可以提高批評的效果,讓課堂違紀的同學心悅誠服。教師在課堂上遇到某些特殊情況時,假如控制不住自己的情緒和理智,動不動就對學生發火訓斥,其弊端是眾人皆知的。如果用幽默的語言來處理,其作用和效果就大不一樣。記得上五年級的一節課時,還沒踏進教室,就聽到學生喧嘩吵鬧的聲音,而上課的預備鈴聲早已經打過了,我想了想,故意站在門口不進去,學生一個個驚訝的看著我,過了兩分鐘的樣子全班安靜下來,我試探性的問剛剛吵的最兇的一個學生,“老師是不是走錯教室了?這是菜市場吧?不?菜市場都不一定有這么熱鬧,那剛剛大家都是在買菜賣菜嗎?”全班捂嘴又不好意思的笑了,培養一定的幽默感,是調節師生情緒狀態所不可缺少的有效方法。
記得蘇霍姆林斯基曾說過:“教師的語言——是一種什么也代替不了的影響學生心靈的工具。”教師的語言藝術不僅僅只是單純的傳道授業,學生善良的靈魂和健康發展的身心,更是教師綜合素質的反映。我們要在實際教學中不斷探索,不斷改進,善于提煉精準的語言為美術課堂點睛,用藝術的語言為美術課堂添彩。