CS+コードジェネレータソースコードにおける変数の宣言の仕方について

プログラム初心者です。
CS+のコードジェネレータを使ったファイルにおいて変数の宣言の仕方(セオリー?一般論?)についてご教授ください

RX231スターターキットのチュートリアルプログラムを変更しています。
グローバル変数を宣言し
r_cg_main.c、r_cg_cmt_user.c
にてその変数を使用したいです。

具体的には、r_cg_cmt_user.cにおいて、
cmt0のインタラプト(1msecでコンペアマッチする設定としている)で、g_ms_count、g_sec_countという変数で ミリ秒、秒 をカウントし
r_cg_main.cのメインループにて、g_sec_count変数のとある条件で各種作業を行い、その変数が10秒になったらg_ms_count、g_sec_countをクリアし
再び10秒カウントに入り上記動作を繰り返したいと思っています。

見よう見まねで色々試行錯誤はしてみたのですが、以下どう考えればよいのかお教えいただければと思います。

質問
r_cg_userdefine.h 内でグローバル変数を宣言するのがセオリーですか?左記ヘッダはr_cg_main.c、r_cg_cmt_user.cでインクルードしています。
ただ、これを行うと色々エラーが出て、現在行き着いた(エラーが出なくなった)のは、
r_cg_userdefine.h にて、extern volatile uint16_t g_sec_count; と宣言
r_cg_main.c にて、特に宣言なし
r_cg_cmt_user.c にて、volatile uint16_t g_ms_counter; と宣言
としています。
externは他で定義されているグローバル変数を参照するための識別子となると、r_cg_userdefine.hでグローバル変数の実体は定義されていないように思えます。
上記の場合実体の定義はr_cg_cmt_user.cでされているのでしょうか?その場合、r_cg_main.cはなぜ宣言なしでその変数を使えているのでしょうか?

取り留めのない質問となりますが、グローバル変数やローカル変数はどこに配置し、どのようにexternなどで参照すべきがよいかご教授いただければ幸いです。

  • ミリ秒単位で更新されるタイマーで最大10秒まで数えたいのであればミリ秒と秒で変数を分ける意味はなく、参照する側がアトミック性を考慮する必要が生じて損なので変数は一つで良いでしょう。

    r_cg_cmt_user.c で
    volatile uint16_t g_ms_counter;
    を定義し、
    r_cg_cmt_user.h で
    extern volatile uint16_t g_ms_counter;
    を宣言すれば良いと思います。

    r_cg_cmt_user.h は r_cg_cmt_user.c の他、g_ms_counter を操作/参照したい *.c のファイル全てでインクルードする必要があります。
  • msatoさん、こんにちは。NoMaYです。

    可能であれば、以下の2つについて、プロジェクトのファイル一式をzipファイルに固めたものを添付して頂けるとうれしいです。それを実際に見て、状況の確認がしたいです。

    (1) 以下のエラーが発生していた以前のプロジェクトのファイル一式

    > r_cg_userdefine.h 内でグローバル変数を宣言するのがセオリーですか?左記ヘッダはr_cg_main.c、r_cg_cmt_user.cでインクルードしています。
    > ただ、これを行うと色々エラーが出て、

    (2) 以下の疑問点が生じている現在のプロジェクトのファイル一式

    > 現在行き着いた(エラーが出なくなった)のは、
    。。。
    > externは他で定義されているグローバル変数を参照するための識別子となると、r_cg_userdefine.hでグローバル変数の実体は定義されていないように思えます。
    > 上記の場合実体の定義はr_cg_cmt_user.cでされているのでしょうか?その場合、r_cg_main.cはなぜ宣言なしでその変数を使えているのでしょうか?

  • fujita nozomu 様
    ご教授ありがとうございます。
    ご指摘の通りやったら、問題なくコンパイルでき所望の動きをしました。

    初歩的な質問で恐縮ですが、
    ソースコードファイル1(含むmain関数)
    ソースコードファイル2(含むサブルーチン関数)
    ソースコードファイル3(含むサブルーチン関数)
    があった場合、グローバル変数はソースコードファイル1のmain関数前で宣言し
    ソースコードファイル2、3ではexternで参照するというのが一般的ではないのでしょうか?
    (手持ちの参考書がそんな感じで書いてあったもので)

    自分の認識だと、
    ソースコードファイル1(含むmain関数)=r_cg_main.c
    ソースコードファイル2(含むサブルーチン関数)=r_cg_cmt_user.c
    となり、r_cg_cmt_user.cで『volatile uint16_t g_ms_counter;』と宣言するのは、
    サブルーチン関数ファイルで宣言してメイン関数ファイルが参照していることになる?という感じがしてしまいます。
    宣言にあまりセオリー?一般論?みたいなものはないのでしょうか?
    もしくはコードジェネレータファイルはfujita nozomu 様のご教授にあるような宣言をしなければいけないという特殊性があったりするのでしょうか?

    また上記のような自分の思想から、r_cg_main.cにグローバル変数をまとめようかと思ったのですが、r_cg_userdefine.hというものが用意されていることから、
    ユーザーが定義するもの(#define、またグローバル変数を記載する)はここにまとめるのがよいのかと思いました。
    そしてそれ以外のソースコードファイルで、externで参照すれば良いのかと思ったのですが(実際、上記のような構成ではコンパイルエラーになりましたが・・・。)
    この考え方はセオリーから外れているのでしょうか?セオリーから外れているとすると、r_cg_userdefine.hには、どういう変数?を記載するものなのでしょうか?

    NoMaY様
    ご回答ありがとうございます。
    ソースコードはお見せしたほうが回答もしていただきやすいこと承知しておりますが、
    まずは文書にてご教授いただければと思ってる次第です。
    (折角ご厚意で見ていただけるという提案の中すみません)
  • 昔からのやり方ですが参考になれば。

    r_cg_userdefine.hに以下を記述します。
    GLOBAL int var1;

    main.cには以下を記述します。
    #include "r_cg_userdefine.h"
    #define GLOBAL // "GLOBAL"が消えるのでmain.cでvar1の実体を宣言することになります

    その他のCソースファイルには以下を記述します。
    #include "r_cg_userdefine.h"
    #define GLOBAL extern // 参照になります
  • > ソースコードファイル1(含むmain関数)
    > ソースコードファイル2(含むサブルーチン関数)
    > ソースコードファイル3(含むサブルーチン関数)
    > があった場合、グローバル変数はソースコードファイル1のmain関数前で宣言し

    ソースコードファイル 1~3 は機能で分けられていると思いますが、グローバル変数もいずれかの機能で操作されるものであればそのソースコードファイルに記述するのが自然でしょう。main() と同居させる意味はありません。

    > ソースコードファイル2、3ではexternで参照するというのが一般的ではないのでしょうか?

    個々のソースコードファイルに extern を書くと記述内容で矛盾が生じる危険性や修正が煩雑になる可能性があるので好ましい書き方ではありません。変数の外部宣言や関数プロトタイプはヘッダファイルに書き、それを参照しているソースコードファイルと定義しているソースコードファイルの両方でインクルードし、定義と宣言に矛盾がないことを防ぐのはC言語でのプログラミング上セオリーとして存在します。

    > (手持ちの参考書がそんな感じで書いてあったもので)

    おかしな書き方を勧めている本であれば捨てたほうが良いでしょう。

    > また上記のような自分の思想から、r_cg_main.cにグローバル変数をまとめようかと思ったのですが、r_cg_userdefine.hというものが用意されていることから、
    > ユーザーが定義するもの(#define、またグローバル変数を記載する)はここにまとめるのがよいのかと思いました。

    コードジェネレータが生成したソースコードファイル全てに共通して参照させたい情報のみ r_cg_userdefine.h に書くべきですが、通常はそのような情報はないと思うので、r_cg_userdefine.h を改変する機会はそうはないと思います。
  • Moo様

    ご教授ありがとうございます。
    やってみたのですが、私のやり方がマズイせいか上手くいきませんでした。
    もう少々格闘してみます。
  • fujita nozomu 様
    ご教授、ご解説ありがとうございます。

    fujita nozomu 様の解説を元に再度オリジナルのチュートリアルプログラムを見直してみたところ、定義の思想が理解できてきました。

    ご教授いただいたセオリーに則り、チュートリアルプログラムの宣言を参照にしつつ、今後のプログラミング進めていきたいと思います。

    Moo様
    お教えいただいた
    >#define GLOBAL // "GLOBAL"が消えるのでmain.cでvar1の実体を宣言することになります
    等の情報を元にネットなどで調べ、新たな知見を得ることができました。為になる情報ありがとうございました。

    ご回答いただいた皆様に感謝し、本件クローズとさせていただきます。

  • fujita nozomu 様

    クローズと言ったのにすみません。再度質問させてください。

    >コードジェネレータが生成したソースコードファイル全てに共通して参照させたい情報のみ r_cg_userdefine.h に書くべきですが、通常はそのような情報はないと思うので、r_cg_userdefine.h を改変する機会はそうはないと思います。

    と、ご教授いただきましたが、

    下記のような、定数を定義したいです。
    #define ADC_MES_NUMBER 512 //ADCの計測ポイント数

    使用先、目的は、
    ・r_cg_s12ad_user.c:この関数内の割込みでADC値を取得(とある配列に取得:g_adc_mes[512])し上記ADC_MES_NUMBERに達したらADCモジュールをストップする。(CMTにて秒カウンタ作り10秒周期でADをあるポイント数取得する感じです。例えば5秒からADをスタート、512ポイント取得したらADをストップ(このfsにはTPUのコンペアマッチを利用)。これを10秒周期で繰返す)

    ・r_cg_main.c:上記r_cg_s12ad_user.cでADC_MES_NUMBER分のデータが貯まったらその平均(g_adc_mes[512]のデータの総和/ADC_MES_NUMBER)を算出する。

    このような場合、『#define ADC_MES_NUMBER 512 //ADCの計測ポイント数』は、r_cg_userdefine.hに記入しても良いのでしょうか?(現在は記入して動作させています)それともr_cg_userdefine.hは極力編集せず、r_cg_main.cやr_cg_s12ad_user.cにて記載したほうが良いのでしょうか?

    ちなみに参照元としているチュートリアルプログラムのオリジナルファイルでは、r_cg_main.c内に #define xxx を用いた定義がないので、r_cg_userdefine.hに記入するのがよいかと思った次第です。

  • > 下記のような、定数を定義したいです。
    > #define ADC_MES_NUMBER 512 //ADCの計測ポイント数
    (略)
    > このような場合、『#define ADC_MES_NUMBER 512 //ADCの計測ポイント数』は、r_cg_userdefine.hに記入しても良いのでしょうか?

    r_cg_s12ad_user.c と r_cg_main.c でのみ共有する g_adc_mes[] の要素数を userdefine.h で定義するのはお勧めしません。

    > それともr_cg_userdefine.hは極力編集せず、r_cg_main.cやr_cg_s12ad_user.cにて記載したほうが良いのでしょうか?

    g_adc_mes[] を r_cg_main.c で定義するのであれば r_cg_main.h、r_cg_s12ad_user.c で定義するのであれば r_cg_s12ad_user.h で g_adc_mes[] の外部宣言と ADC_MES_NUMBER の定義を行うのが良いでしょう。

    > ちなみに参照元としているチュートリアルプログラムのオリジナルファイルでは、r_cg_main.c内に #define xxx を用いた定義がないので、r_cg_userdefine.hに記入するのがよいかと思った次第です。

    RX231スタータキットサンプルコードの Workspace/Tutorial/cg_src/ 以下は *.c の中で extern ~ してたりと大分褒められない書き方をしており参考にされないことをお勧めします。
    同プロジェクトの Workspace/Tutorial/ 直下の

    ascii.c
    ascii.h
    r_okaya_lcd.c
    r_okaya_lcd.h
    r_rsk_debug.c
    r_rsk_debug.h
    r_rsk_switch.c
    r_rsk_switch.h

    辺りは良いと思います。
  • fujita nozomu 様

    ご回答ありがとうございます。
    大変参考になりました。
    周りにプログラムの事を聞ける人もおらず、チュートリアルプログラムがお手本でした。

    ascii.cなど、参考になるプログラムの情報もありがとうございます。
    お教えいただいた内容参考にしながらプログラムの中身を見て勉強していきます。

    ありがとうございました。